From b98b8d5e4dee3f50eae00a3681d3bad054bc752a Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Mon, 2 Sep 2024 12:49:18 +0530 Subject: [PATCH 001/101] Add swipe icon --- frontend/src/components/svgIcons/index.js | 1 + frontend/src/components/svgIcons/swipe.js | 31 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 frontend/src/components/svgIcons/swipe.js diff --git a/frontend/src/components/svgIcons/index.js b/frontend/src/components/svgIcons/index.js index 08f80f5233..9809e2ad2f 100644 --- a/frontend/src/components/svgIcons/index.js +++ b/frontend/src/components/svgIcons/index.js @@ -85,3 +85,4 @@ export { DownloadIcon } from './download'; export { CircleMinusIcon } from './circleMinus'; export { CircleExclamationIcon } from './circleExclamation'; export { TableListIcon } from './tableList'; +export { SwipeIcon } from './swipe'; diff --git a/frontend/src/components/svgIcons/swipe.js b/frontend/src/components/svgIcons/swipe.js new file mode 100644 index 0000000000..147e76f53c --- /dev/null +++ b/frontend/src/components/svgIcons/swipe.js @@ -0,0 +1,31 @@ +import { PureComponent } from 'react'; + +export class SwipeIcon extends PureComponent { + render() { + return ( + + + + + + + + + ); + } +} From 0edfd8849246d7a002782a28d37094f773e3152c Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Mon, 2 Sep 2024 12:51:20 +0530 Subject: [PATCH 002/101] Add StatsCardWithDelta component --- frontend/src/components/statsCard.js | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/frontend/src/components/statsCard.js b/frontend/src/components/statsCard.js index a0777314e6..649052e455 100644 --- a/frontend/src/components/statsCard.js +++ b/frontend/src/components/statsCard.js @@ -19,6 +19,41 @@ export const StatsCard = ({ icon, description, value, className, invertColors = ); }; +export const StatsCardWithDelta = ({ + icon, + description, + value, + className, + delta, + invertColors = false, +}) => { + return ( +
+
+
{icon}
+ : value + } + label={description} + className="w-70 pt3-m mb1 fl" + invertColors={invertColors} + /> +
+
+ {delta} +
+
+ ); +}; + export const StatsCardContent = ({ value, label, className, invertColors = false }: Object) => (

{value}

@@ -26,6 +61,18 @@ export const StatsCardContent = ({ value, label, className, invertColors = false
); +export const StatsCardWithDeltaContent = ({ + value, + label, + className, + invertColors = false, +}: Object) => ( +
+

{value}

+ {label} +
+); + function getFormattedNumber(num) { if (typeof num !== 'number') return '-'; const value = shortNumber(num); From 0364a0e4955d21b364b3301d44d849d7fc86a52e Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Tue, 3 Sep 2024 12:52:58 +0530 Subject: [PATCH 003/101] feat: create view for partners mapswipe stats and show info banner --- frontend/src/views/messages.js | 4 ++++ frontend/src/views/partnersMapswipeStats.css | 17 ++++++++++++++ frontend/src/views/partnersMapswipeStats.js | 24 ++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 frontend/src/views/partnersMapswipeStats.css create mode 100644 frontend/src/views/partnersMapswipeStats.js diff --git a/frontend/src/views/messages.js b/frontend/src/views/messages.js index 782dc5ab87..0bcfbf3a40 100644 --- a/frontend/src/views/messages.js +++ b/frontend/src/views/messages.js @@ -814,4 +814,8 @@ export default defineMessages({ id: 'pages.create_campaign.error', defaultMessage: 'There was an error saving this campaign.', }, + mapswipeInfo: { + id: 'management.partner.stats.mapswipe.info', + defaultMessage: 'It may take up to 48 hours for updates', + }, }); diff --git a/frontend/src/views/partnersMapswipeStats.css b/frontend/src/views/partnersMapswipeStats.css new file mode 100644 index 0000000000..3bf7e57d23 --- /dev/null +++ b/frontend/src/views/partnersMapswipeStats.css @@ -0,0 +1,17 @@ +.mapswipe-stats-info-banner { + background-color: #d9d7d7; + width: fit-content; + margin-left: 20px; + border-radius: 3px; +} + +.mapswipe-stats-info-banner::before { + content: ''; + position: absolute; + left: -20px; + top: 0; + bottom: 0; + width: 20px; + background-color: #d9d7d7; + clip-path: polygon(100% 0, 0 50%, 100% 100%); +} diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js new file mode 100644 index 0000000000..feab20022f --- /dev/null +++ b/frontend/src/views/partnersMapswipeStats.js @@ -0,0 +1,24 @@ +import { FormattedMessage } from 'react-intl'; + +import { InfoIcon } from '../components/svgIcons'; +import messages from './messages'; +import './partnersMapswipeStats.css'; + +const InfoBanner = () => { + return ( +
+ + + + +
+ ); +}; + +export const PartnersMapswipeStats = () => { + return ( +
+ +
+ ); +}; From cdbb0468da3a53b6d6ba4b2e01c877ab24faf484 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Tue, 3 Sep 2024 12:57:55 +0530 Subject: [PATCH 004/101] Create a dir for partner mapswipe stats view, add Overview component with mock data --- .../partnerMapswipeStats/messages.js | 19 ++++++++++ .../partnerMapswipeStats/overview.js | 36 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 frontend/src/components/partnerMapswipeStats/messages.js create mode 100644 frontend/src/components/partnerMapswipeStats/overview.js diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js new file mode 100644 index 0000000000..18f3801979 --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -0,0 +1,19 @@ +import { defineMessages } from 'react-intl'; + +/** + * Internationalized messages for use on teams and orgs. + */ +export default defineMessages({ + totalSwipes: { + id: 'management.partners.stats.mapswipe.totalSwipes', + defaultMessage: 'Total Swipes', + }, + totalTimeSpent: { + id: 'management.partners.stats.mapswipe.totalTimeSpent', + defaultMessage: 'Total Time Spent', + }, + totalContributors: { + id: 'management.partners.stats.mapswipe.totalContributors', + defaultMessage: 'Total Contributors', + }, +}); diff --git a/frontend/src/components/partnerMapswipeStats/overview.js b/frontend/src/components/partnerMapswipeStats/overview.js new file mode 100644 index 0000000000..851244778d --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/overview.js @@ -0,0 +1,36 @@ +import { FormattedMessage } from 'react-intl'; + +import messages from './messages'; +import { StatsCardWithDelta } from '../statsCard'; +import { MappingIcon, SwipeIcon, ClockIcon } from '../svgIcons'; + +const iconClass = 'w-100'; +const iconStyle = { height: '55px' }; + +export const Overview = () => { + return ( +
+ } + description={} + value={'1 M'} + delta={66k swipes in the last 30 days} + className="w-100" + /> + } + description={} + value={'1 Mo 2 Days'} + delta={2 days 15 hours in the last 30 days} + className="w-100" + /> + } + description={} + value={'407'} + delta={33 active contributors in the last 30 days} + className="w-100" + /> +
+ ); +}; From dd13fa322efb3664cf17fc62f2ebeaf06d547c21 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Tue, 3 Sep 2024 13:01:43 +0530 Subject: [PATCH 005/101] feat: show overview stats with mock data --- frontend/src/views/partnersMapswipeStats.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index feab20022f..35c164af63 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -1,6 +1,7 @@ import { FormattedMessage } from 'react-intl'; import { InfoIcon } from '../components/svgIcons'; +import { Overview } from '../components/partnerMapswipeStats/overview'; import messages from './messages'; import './partnersMapswipeStats.css'; @@ -19,6 +20,7 @@ export const PartnersMapswipeStats = () => { return (
+
); }; From cff67453717ef5c2e910164cc08143061297db5f Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 4 Sep 2024 14:29:07 +0530 Subject: [PATCH 006/101] feat: create group members table with mock data --- .../partnerMapswipeStats/groupMembers.css | 14 ++ .../partnerMapswipeStats/groupMembers.js | 192 ++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 frontend/src/components/partnerMapswipeStats/groupMembers.css create mode 100644 frontend/src/components/partnerMapswipeStats/groupMembers.js diff --git a/frontend/src/components/partnerMapswipeStats/groupMembers.css b/frontend/src/components/partnerMapswipeStats/groupMembers.css new file mode 100644 index 0000000000..b47096b6b8 --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/groupMembers.css @@ -0,0 +1,14 @@ +.partner-mapswipe-stats-column-resizer { + position: absolute; + top: 27%; + height: 40%; + width: 2px; + background: rgba(177, 177, 177, 0.5); + cursor: col-resize; + user-select: none; + touch-action: none; +} + +.partner-mapswipe-stats-column-resizer.isResizing { + background: rgb(131, 131, 131); +} diff --git a/frontend/src/components/partnerMapswipeStats/groupMembers.js b/frontend/src/components/partnerMapswipeStats/groupMembers.js new file mode 100644 index 0000000000..c86c310e74 --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/groupMembers.js @@ -0,0 +1,192 @@ +import { FormattedMessage } from 'react-intl'; +import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table'; + +import { BanIcon, CircleExclamationIcon } from '../svgIcons'; +import { PaginatorLine } from '../paginator'; +import messages from './messages'; +import './groupMembers.css'; + +const MOCK_DATA = [ + { + user: 'map4life', + totalSwipes: 12023, + projectContributed: 'American Red Cross', + timeSpent: '2 Months', + }, + { + user: 'PinkSky234', + totalSwipes: 11984, + projectContributed: 'American Red Cross', + timeSpent: '1 Month', + }, + { + user: 'swiper55', + totalSwipes: 3540, + projectContributed: 'HOT', + timeSpent: '3 Weeks', + }, + { + user: 'edelweiss93', + totalSwipes: 1234, + projectContributed: 'American Red Cross', + timeSpent: '1 Week', + }, + { + user: 'mappy45', + totalSwipes: 842, + projectContributed: 'HOT', + timeSpent: '2 Weeks', + }, + { + user: 'sup3rMap', + totalSwipes: 325, + projectContributed: 'American Red Cross', + timeSpent: '5 Days', + }, + { + user: '4weeksmapping', + totalSwipes: 74, + projectContributed: 'HOT', + timeSpent: '1 Day', + }, +]; + +const COLUMNS = [ + { + accessorKey: 'user', + header: () => ( + + + + ), + }, + { + accessorKey: 'totalSwipes', + header: () => ( + + + + ), + }, + { + accessorKey: 'projectContributed', + header: () => ( + + + + ), + }, + { + accessorKey: 'timeSpent', + header: () => ( + + + + ), + }, +]; + +export const GroupMembers = () => { + const table = useReactTable({ + columns: COLUMNS, + data: MOCK_DATA, + getCoreRowModel: getCoreRowModel(), + columnResizeMode: 'onChange', + columnResizeDirection: 'ltr', + }); + + const isEmpty = false; + + return ( +
+

+ +

+
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + +
+ {header.isPlaceholder + ? null + : flexRender(header.column.columnDef.header, header.getContext())} + + {header.id !== 'timeSpent' && ( +
header.column.resetSize(), + onMouseDown: header.getResizeHandler(), + onTouchStart: header.getResizeHandler(), + className: `partner-mapswipe-stats-column-resizer ${ + table.options.columnResizeDirection === 'ltr' ? 'right-0' : '' + } ${header.column.getIsResizing() ? 'isResizing' : ''}`, + }} + /> + )} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+ + {isError ? ( +
+ +

+ +

+
+ ) : null} + + {isEmpty && ( +
+ +

+ +

+
+ )} +
+ +
+ {}} + lastPage={1} + className="flex items-center justify-center pa4" + /> +
+
+
+ ); +}; From fb9ab7b478784e8cf08503642274083324c25842 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 4 Sep 2024 14:30:21 +0530 Subject: [PATCH 007/101] Update messages with group members columns, empty and error state --- .../partnerMapswipeStats/messages.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index 18f3801979..54792d42a1 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -16,4 +16,32 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.totalContributors', defaultMessage: 'Total Contributors', }, + groupMembers: { + id: 'management.partners.stats.mapswipe.groupMembers', + defaultMessage: 'Group Members', + }, + userColumn: { + id: 'management.partners.stats.mapswipe.groupMembers.user', + defaultMessage: 'User', + }, + totalSwipesColumn: { + id: 'management.partners.stats.mapswipe.groupMembers.totalSwipes', + defaultMessage: 'Total Swipes', + }, + projectContributedColumn: { + id: 'management.partners.stats.mapswipe.groupMembers.projectContributed', + defaultMessage: 'Project Contributed', + }, + timeSpentColumn: { + id: 'management.partners.stats.mapswipe.groupMembers.timeSpent', + defaultMessage: 'Time Spent', + }, + groupMembersTableEmpty: { + id: 'management.partners.stats.mapswipe.groupMembers.empty', + defaultMessage: 'No group members were found.', + }, + groupMembersTableError: { + id: 'management.partners.stats.mapswipe.groupMembers.error', + defaultMessage: 'Something went wrong!', + }, }); From bfecd8fcc2b0c27821655b248070ddbd6338dd26 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 4 Sep 2024 14:31:38 +0530 Subject: [PATCH 008/101] feat: show group members table section with mock data --- frontend/src/views/partnersMapswipeStats.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 35c164af63..32ba9a2e96 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -2,6 +2,7 @@ import { FormattedMessage } from 'react-intl'; import { InfoIcon } from '../components/svgIcons'; import { Overview } from '../components/partnerMapswipeStats/overview'; +import { GroupMembers } from '../components/partnerMapswipeStats/groupMembers'; import messages from './messages'; import './partnersMapswipeStats.css'; @@ -21,6 +22,9 @@ export const PartnersMapswipeStats = () => {
+
+ +
); }; From 3159e1fbbbf943749c99214a0544c5b8aae0db3b Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 5 Sep 2024 16:48:18 +0530 Subject: [PATCH 009/101] feat: create contributions grid component --- .../partnerMapswipeStats/contributionsGrid.js | 96 +++++++++++++++++++ .../partnerMapswipeStats/groupMembers.js | 3 +- .../partnerMapswipeStats/messages.js | 20 ++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/partnerMapswipeStats/contributionsGrid.js diff --git a/frontend/src/components/partnerMapswipeStats/contributionsGrid.js b/frontend/src/components/partnerMapswipeStats/contributionsGrid.js new file mode 100644 index 0000000000..9b7f220bba --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/contributionsGrid.js @@ -0,0 +1,96 @@ +import CalendarHeatmap from 'react-calendar-heatmap'; +import { Tooltip } from 'react-tooltip'; +import { FormattedMessage, useIntl } from 'react-intl'; + +import messages from './messages'; + +const Legend = () => { + const indexes = [30, 50, 70, 100]; + const legendFontStyle = 'ph2 f7 blue-grey ttc'; + + return ( +
+ + + +
+ {indexes.map((i) => ( +
+ ))} + + + +
+ ); +}; + +export const ContributionsGrid = () => { + const intl = useIntl(); + const today = new Date(); + + const shiftDate = (date, numDays) => { + const newDate = new Date(date); + newDate.setDate(newDate.getDate() + numDays); + return newDate; + }; + + const countValues = [].map((r) => r.count); + const maxValue = Math.max(...countValues); + + const getHeatmapClass = (value) => { + const rate = value.count / maxValue; + + if (0.0 <= rate && rate < 0.25) { + return 'fill-red o-30'; + } + + if (0.25 <= rate && rate < 0.5) { + return 'fill-red o-50'; + } + + if (0.5 <= rate && rate < 0.75) { + return 'fill-red o-70'; + } + + if (0.75 <= rate && rate <= 1) { + return 'fill-red o-100'; + } + }; + + return ( +
+

+ +

+ +
+ { + if (!value) return 'fill-tan'; + return getHeatmapClass(value); + }} + showWeekdayLabels={true} + tooltipDataAttrs={(value) => { + let tooltipContent = intl.formatMessage(messages.contributionsGridEmpty); + if (value.count !== null) { + tooltipContent = `${value.count} ${intl.formatMessage( + messages.contributionsGridTooltip, + )}`; + } + + return { + 'data-tooltip-float': true, + 'data-tooltip-content': tooltipContent, + 'data-tooltip-id': 'partnerMapswipeContributionsGridTooltip', + }; + }} + /> + + +
+
+ ); +}; diff --git a/frontend/src/components/partnerMapswipeStats/groupMembers.js b/frontend/src/components/partnerMapswipeStats/groupMembers.js index c86c310e74..f17d017579 100644 --- a/frontend/src/components/partnerMapswipeStats/groupMembers.js +++ b/frontend/src/components/partnerMapswipeStats/groupMembers.js @@ -96,6 +96,7 @@ export const GroupMembers = () => { }); const isEmpty = false; + const isError = false; return (
@@ -177,7 +178,7 @@ export const GroupMembers = () => {
)} - +
Date: Thu, 5 Sep 2024 16:49:20 +0530 Subject: [PATCH 010/101] feat: show contributions grid section --- frontend/src/views/partnersMapswipeStats.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 32ba9a2e96..257ccd7c68 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -3,6 +3,7 @@ import { FormattedMessage } from 'react-intl'; import { InfoIcon } from '../components/svgIcons'; import { Overview } from '../components/partnerMapswipeStats/overview'; import { GroupMembers } from '../components/partnerMapswipeStats/groupMembers'; +import { ContributionsGrid } from '../components/partnerMapswipeStats/contributionsGrid'; import messages from './messages'; import './partnersMapswipeStats.css'; @@ -22,9 +23,14 @@ export const PartnersMapswipeStats = () => {
+
+ +
+ +
); }; From b587494b8290bdeaf399a8f5b3fb82cbf96ed38c Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Sat, 7 Sep 2024 13:26:13 +0530 Subject: [PATCH 011/101] feat: create timeSpentContributingByDay bar chart component --- .../partnerMapswipeStats/messages.js | 4 + .../timeSpentContributingByDay.js | 108 ++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index dbd0f09f80..3496a0d09c 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -64,4 +64,8 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.contributions.tooltip', defaultMessage: 'contribution(s)', }, + timeSpentContributingByDay: { + id: 'management.partners.stats.mapswipe.timeSpentContributingByDay', + defaultMessage: 'Time Spent Contributing by Day of Week', + }, }); diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js new file mode 100644 index 0000000000..f544b497fa --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js @@ -0,0 +1,108 @@ +import { useEffect, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { Chart } from 'chart.js/auto'; + +import messages from './messages'; + +const MOCK_DATA = [ + { x: 'Sun', y: 15 * 60 + 16 }, // 15 hours 16 minutes + { x: 'Mon', y: 30 * 60 }, // 1 day 6 hours + { x: 'Tue', y: 45 * 60 }, // 1 day 21 hours + { x: 'Wed', y: 61 * 60 }, // 2 days 13 hours + { x: 'Thu', y: 61 * 60 }, // 2 days 13 hours + { x: 'Fri', y: 45 * 60 }, // 1 day 21 hours + { x: 'Sat', y: 15 * 60 + 16 }, // 15 hours 16 minutes +]; + +export const TimeSpentContributingByDay = () => { + const chartRef = useRef(null); + const chartInstance = useRef(null); + + useEffect(() => { + if (chartInstance.current) { + chartInstance.current.destroy(); + } + + const timeSpentData = []; + for (const data of MOCK_DATA) { + timeSpentData.push(data.y); + } + + chartInstance.current = new Chart(chartRef.current.getContext('2d'), { + type: 'bar', + data: { + labels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + datasets: [ + { + label: 'Time Spent', + backgroundColor: '#d73f3f', + data: timeSpentData, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + legend: { display: false }, + scales: { + y: { + beginAtZero: true, + ticks: { + callback: function (value) { + const days = Math.floor(value / (24 * 60)); + const hours = Math.floor((value % (24 * 60)) / 60); + const minutes = value % 60; + + let label = ''; + if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; + if (hours > 0 || days > 0) label += `${hours} hr${hours !== 1 ? 's' : ''}`; + if (minutes > 0 && days === 0) + label += ` ${minutes} min${minutes !== 1 ? 's' : ''}`; + + return label.trim(); + }, + stepSize: 24 * 60, // One day + }, + }, + }, + plugins: { + tooltip: { + callbacks: { + label: function (context) { + const value = context.parsed.y; + const days = Math.floor(value / (24 * 60)); + const hours = Math.floor((value % (24 * 60)) / 60); + const minutes = value % 60; + + let label = 'Time Spent: '; + if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; + if (hours > 0 || days > 0) label += `${hours} hour${hours !== 1 ? 's' : ''} `; + if (minutes > 0) label += `${minutes} minute${minutes !== 1 ? 's' : ''}`; + + return label.trim(); + }, + }, + }, + }, + }, + }); + + return () => { + if (chartInstance.current) { + chartInstance.current.destroy(); + } + }; + }, []); + + return ( +
+

+ +

+ +
+ +
+
+ ); +}; From a6d0b860c46e51bc95355f9eba512c7c80235c7c Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Sat, 7 Sep 2024 13:27:35 +0530 Subject: [PATCH 012/101] feat: show time spent contributing by day section --- frontend/src/views/partnersMapswipeStats.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 257ccd7c68..5c1a2766e6 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -4,6 +4,7 @@ import { InfoIcon } from '../components/svgIcons'; import { Overview } from '../components/partnerMapswipeStats/overview'; import { GroupMembers } from '../components/partnerMapswipeStats/groupMembers'; import { ContributionsGrid } from '../components/partnerMapswipeStats/contributionsGrid'; +import { TimeSpentContributingByDay } from '../components/partnerMapswipeStats/timeSpentContributingByDay'; import messages from './messages'; import './partnersMapswipeStats.css'; @@ -31,6 +32,10 @@ export const PartnersMapswipeStats = () => {
+ +
+ +
); }; From 2a7b2682e8591b70d21e028ecf8b4bb340e09a95 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Tue, 10 Sep 2024 18:13:06 +0530 Subject: [PATCH 013/101] feat: create timeSpentContributing area chart component --- .../partnerMapswipeStats/messages.js | 4 + .../timeSpentContributing.js | 122 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 frontend/src/components/partnerMapswipeStats/timeSpentContributing.js diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index 3496a0d09c..5afd65a07c 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -64,6 +64,10 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.contributions.tooltip', defaultMessage: 'contribution(s)', }, + timeSpentContributing: { + id: 'management.partners.stats.mapswipe.timeSpentContributing', + defaultMessage: 'Time Spent Contributing', + }, timeSpentContributingByDay: { id: 'management.partners.stats.mapswipe.timeSpentContributingByDay', defaultMessage: 'Time Spent Contributing by Day of Week', diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js new file mode 100644 index 0000000000..0939cea451 --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js @@ -0,0 +1,122 @@ +import { useEffect, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { Chart } from 'chart.js/auto'; +import 'chartjs-adapter-date-fns'; + +import messages from './messages'; + +const MOCK_DATA = [ + { date: '2024-01-02', minutesSpent: 10 }, + { date: '2024-01-29', minutesSpent: 40 }, + { date: '2024-02-26', minutesSpent: 120 }, + { date: '2024-03-26', minutesSpent: 180 }, + { date: '2024-04-24', minutesSpent: 230 }, + { date: '2024-05-23', minutesSpent: 350 }, + { date: '2024-06-21', minutesSpent: 90 }, + { date: '2024-07-18', minutesSpent: 60 }, + { date: '2024-09-06', minutesSpent: 45 }, +]; + +export const TimeSpentContributing = () => { + const chartRef = useRef(null); + const chartInstance = useRef(null); + + useEffect(() => { + if (chartInstance.current) { + chartInstance.current.destroy(); + } + + const context = chartRef.current.getContext('2d'); + // Create gradient for the area + const gradient = context.createLinearGradient(0, 0, 0, 450); + gradient.addColorStop(0, 'rgba(215, 63, 63, 1)'); + gradient.addColorStop(0.4, 'rgba(215, 63, 63, 1)'); + gradient.addColorStop(1, 'rgba(215, 63, 63, 0)'); + + chartInstance.current = new Chart(context, { + type: 'line', + data: { + labels: MOCK_DATA.map((entry) => entry.date), + datasets: [ + { + label: 'Time Spent', + backgroundColor: gradient, + data: MOCK_DATA.map((entry) => entry.minutesSpent), + fill: true, + tension: 0.4, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + legend: { display: false }, + scales: { + x: { + type: 'time', + time: { + unit: 'day', + tooltipFormat: 'MMM d, yyyy', + }, + }, + y: { + beginAtZero: true, + ticks: { + callback: function (value) { + const days = Math.floor(value / (24 * 60)); + const hours = Math.floor((value % (24 * 60)) / 60); + const minutes = value % 60; + + let label = ''; + if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; + if (hours > 0 || days > 0) label += `${hours} hr${hours !== 1 ? 's' : ''}`; + if (minutes > 0 && days === 0) + label += ` ${minutes} min${minutes !== 1 ? 's' : ''}`; + + return label.trim(); + }, + stepSize: 60, + }, + }, + }, + plugins: { + tooltip: { + callbacks: { + label: function (context) { + const value = context.parsed.y; + const days = Math.floor(value / (24 * 60)); + const hours = Math.floor((value % (24 * 60)) / 60); + const minutes = value % 60; + + let label = 'Time Spent: '; + if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; + if (hours > 0 || days > 0) label += `${hours} hour${hours !== 1 ? 's' : ''} `; + if (minutes > 0) label += `${minutes} minute${minutes !== 1 ? 's' : ''}`; + + return label.trim(); + }, + }, + }, + }, + }, + }); + + return () => { + if (chartInstance.current) { + chartInstance.current.destroy(); + } + }; + }, []); + + return ( +
+

+ +

+ +
+ +
+
+ ); +}; From 77827474eb32e5ffc47830ba7250ad7a1a1b44f5 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Tue, 10 Sep 2024 18:14:09 +0530 Subject: [PATCH 014/101] feat: show time spent contributing section with mock data --- frontend/src/views/partnersMapswipeStats.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 5c1a2766e6..e40cd988a9 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -4,6 +4,7 @@ import { InfoIcon } from '../components/svgIcons'; import { Overview } from '../components/partnerMapswipeStats/overview'; import { GroupMembers } from '../components/partnerMapswipeStats/groupMembers'; import { ContributionsGrid } from '../components/partnerMapswipeStats/contributionsGrid'; +import { TimeSpentContributing } from '../components/partnerMapswipeStats/timeSpentContributing'; import { TimeSpentContributingByDay } from '../components/partnerMapswipeStats/timeSpentContributingByDay'; import messages from './messages'; import './partnersMapswipeStats.css'; @@ -33,6 +34,10 @@ export const PartnersMapswipeStats = () => { +
+ +
+
From 84311c5d5e27b613462aba772a69c8b8ad61ef7a Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Tue, 10 Sep 2024 18:32:46 +0530 Subject: [PATCH 015/101] feat: create swipes by project type doughnut chart component with mock data --- .../partnerMapswipeStats/messages.js | 4 ++ .../swipesByProjectType.js | 66 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 frontend/src/components/partnerMapswipeStats/swipesByProjectType.js diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index 5afd65a07c..b4a1f0c5aa 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -72,4 +72,8 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.timeSpentContributingByDay', defaultMessage: 'Time Spent Contributing by Day of Week', }, + swipesByProjectType: { + id: 'management.partners.stats.mapswipe.swipesByProjectType', + defaultMessage: 'Swipes by Project Type', + } }); diff --git a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js new file mode 100644 index 0000000000..9fd7450bc6 --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js @@ -0,0 +1,66 @@ +import { useEffect, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; +import Chart from 'chart.js/auto'; + +import messages from './messages'; + +export const SwipesByProjectType = () => { + const chartRef = useRef(null); + const chartInstance = useRef(null); + + useEffect(() => { + if (chartInstance.current) { + chartInstance.current.destroy(); + } + + if (!chartRef.current) return; + + const context = chartRef.current.getContext('2d'); + + chartInstance.current = new Chart(context, { + type: 'doughnut', + data: { + labels: ['Find', 'Validate'], + datasets: [ + { + data: [75, 25], + backgroundColor: [ + 'rgba(255, 159, 64, 0.8)', // Orange for Find + 'rgba(152, 251, 152, 0.8)', // Light green for Validate + 'rgba(54, 162, 235, 0.8)', // Bue for Compare + ], + borderColor: '#fff', + borderWidth: 2, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'right', + }, + }, + }, + }); + + return () => { + if (chartInstance.current) { + chartInstance.current.destroy(); + } + }; + }, []); + + return ( +
+

+ +

+ +
+ +
+
+ ); +}; From 469e3a2c1fd2e2580e262985ab48a85aa89e4b72 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 11 Sep 2024 14:20:13 +0530 Subject: [PATCH 016/101] feat: create swipes by organisation doughnut chart component with mock data --- .../partnerMapswipeStats/messages.js | 6 +- .../swipesByOrganisation.js | 78 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index b4a1f0c5aa..71bfb6a2d0 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -75,5 +75,9 @@ export default defineMessages({ swipesByProjectType: { id: 'management.partners.stats.mapswipe.swipesByProjectType', defaultMessage: 'Swipes by Project Type', - } + }, + swipesByOrganization: { + id: 'management.partners.stats.mapswipe.swipesByOrganisation', + defaultMessage: 'Swipes by Organisation', + }, }); diff --git a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js new file mode 100644 index 0000000000..a967e7fa0b --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js @@ -0,0 +1,78 @@ +import { useEffect, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; +import Chart from 'chart.js/auto'; + +import messages from './messages'; + +export const SwipesByOrganisation = () => { + const chartRef = useRef(null); + const chartInstance = useRef(null); + + useEffect(() => { + if (chartInstance.current) { + chartInstance.current.destroy(); + } + + if (!chartRef.current) return; + + const context = chartRef.current.getContext('2d'); + + chartInstance.current = new Chart(context, { + type: 'doughnut', + data: { + labels: [ + 'American Red Cross', + 'Arizona State University', + 'HOT', + 'Médecins Sans Frontières', + 'Others', + ], + datasets: [ + { + data: [35, 25, 20, 15, 5], + backgroundColor: [ + 'rgba(255, 159, 64, 0.8)', // Orange for American Red Cross + 'rgba(255, 205, 86, 0.8)', // Yellow for Arizona State University + 'rgba(75, 192, 192, 0.8)', // Green for HOT + 'rgba(54, 162, 235, 0.8)', // Blue for Médecins Sans Frontières + 'rgba(201, 203, 207, 0.8)', // Grey for Others + ], + borderColor: '#fff', + borderWidth: 2, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'right', + labels: { + boxWidth: 15, + padding: 15, + }, + }, + }, + }, + }); + + return () => { + if (chartInstance.current) { + chartInstance.current.destroy(); + } + }; + }, []); + + return ( +
+

+ +

+ +
+ +
+
+ ); +}; From 90b500e4803e298c78383ae74e65c4c62b6864b2 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 11 Sep 2024 14:28:07 +0530 Subject: [PATCH 017/101] feat: show swipes by project type and by organisation section --- frontend/src/views/partnersMapswipeStats.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index e40cd988a9..f00e487cce 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -6,6 +6,8 @@ import { GroupMembers } from '../components/partnerMapswipeStats/groupMembers'; import { ContributionsGrid } from '../components/partnerMapswipeStats/contributionsGrid'; import { TimeSpentContributing } from '../components/partnerMapswipeStats/timeSpentContributing'; import { TimeSpentContributingByDay } from '../components/partnerMapswipeStats/timeSpentContributingByDay'; +import { SwipesByProjectType } from '../components/partnerMapswipeStats/swipesByProjectType'; +import { SwipesByOrganisation } from '../components/partnerMapswipeStats/swipesByOrganisation'; import messages from './messages'; import './partnersMapswipeStats.css'; @@ -41,6 +43,11 @@ export const PartnersMapswipeStats = () => {
+ +
+ + +
); }; From 1a4d748d7396d79f564c8303adbe5704ce25aaaa Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 11 Sep 2024 17:00:27 +0530 Subject: [PATCH 018/101] feat: create projectTypeAreaStats component with mock data --- .../partnerMapswipeStats/messages.js | 24 ++++++++ .../projectTypeAreaStats.js | 55 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index 71bfb6a2d0..4f241fca28 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -80,4 +80,28 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.swipesByOrganisation', defaultMessage: 'Swipes by Organisation', }, + areaSwipesByProjectType: { + id: 'management.partners.stats.mapswipe.areaSwipes', + defaultMessage: 'Area Swipes', + }, + find: { + id: 'management.partners.stats.mapswipe.area.find', + defaultMessage: 'Find', + }, + featuresCheckedByProjectType: { + id: 'management.partners.stats.mapswipe.featuresChecked', + defaultMessage: 'Features Checked', + }, + validate: { + id: 'management.partners.stats.mapswipe.area.validate', + defaultMessage: 'Validate', + }, + sceneComparedByProjectType: { + id: 'management.partners.stats.mapswipe.sceneCompared', + defaultMessage: 'Area Swipes', + }, + compare: { + id: 'management.partners.stats.mapswipe.area.compare', + defaultMessage: 'Compare', + }, }); diff --git a/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js new file mode 100644 index 0000000000..d7699701d5 --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js @@ -0,0 +1,55 @@ +import { FormattedMessage } from 'react-intl'; + +import messages from './messages'; +import { StatsCardWithDelta } from '../statsCard'; +import { MappingIcon, SwipeIcon, ClockIcon } from '../svgIcons'; + +const iconClass = 'w-100'; +const iconStyle = { height: '55px' }; + +export const ProjectTypeAreaStats = () => { + return ( +
+ } + description={} + value={'213K'} + delta={ +
+ 25K Sq. KM. + + + +
+ } + className="w-100" + /> + } + description={} + value={'114K'} + delta={ +
+ +
+ } + className="w-100" + /> + } + description={} + value={'11K'} + delta={ +
+ 230 Sq. KM. + +
+ } + className="w-100" + /> +
+ ); +}; From 2ca8b51a8406a98eb7f23894d563700267089e96 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 11 Sep 2024 17:03:45 +0530 Subject: [PATCH 019/101] feat: show swipes, time spent, and project type area stats section + reorder sections --- frontend/src/views/messages.js | 8 ++++++ frontend/src/views/partnersMapswipeStats.js | 28 ++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/frontend/src/views/messages.js b/frontend/src/views/messages.js index 0bcfbf3a40..b3ce294bbb 100644 --- a/frontend/src/views/messages.js +++ b/frontend/src/views/messages.js @@ -818,4 +818,12 @@ export default defineMessages({ id: 'management.partner.stats.mapswipe.info', defaultMessage: 'It may take up to 48 hours for updates', }, + swipes: { + id: 'management.partners.stats.mapswipe.swipes', + defaultMessage: 'Swipes', + }, + timeSpentContributing: { + id: 'management.partners.stats.mapswipe.timeSpentContributing', + defaultMessage: 'Time Spent Contributing', + }, }); diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index f00e487cce..628761b59a 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -6,8 +6,10 @@ import { GroupMembers } from '../components/partnerMapswipeStats/groupMembers'; import { ContributionsGrid } from '../components/partnerMapswipeStats/contributionsGrid'; import { TimeSpentContributing } from '../components/partnerMapswipeStats/timeSpentContributing'; import { TimeSpentContributingByDay } from '../components/partnerMapswipeStats/timeSpentContributingByDay'; +import { ProjectTypeAreaStats } from '../components/partnerMapswipeStats/projectTypeAreaStats'; import { SwipesByProjectType } from '../components/partnerMapswipeStats/swipesByProjectType'; import { SwipesByOrganisation } from '../components/partnerMapswipeStats/swipesByOrganisation'; +import { StatsCardWithDelta } from '../components/statsCard'; import messages from './messages'; import './partnersMapswipeStats.css'; @@ -28,10 +30,6 @@ export const PartnersMapswipeStats = () => { -
- -
-
@@ -44,10 +42,32 @@ export const PartnersMapswipeStats = () => { +
+ +
+ +
+ } + value="338K" + style={{ width: '48.5%' }} + /> + } + value="11 days 5 hrs" + className="w-100" + style={{ width: '48.5%' }} + /> +
+
+ +
+ +
); }; From 0098693357b696b392fd7960d0b16469c2a23c14 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 11 Sep 2024 17:28:19 +0530 Subject: [PATCH 020/101] Add gray color to CHART_COLOURS, use CHART_COLOURS in chart components --- .../partnerMapswipeStats/swipesByOrganisation.js | 13 +++++++------ .../partnerMapswipeStats/swipesByProjectType.js | 11 ++++++----- .../partnerMapswipeStats/timeSpentContributing.js | 5 +++-- .../timeSpentContributingByDay.js | 10 +++------- frontend/src/config/index.js | 1 + 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js index a967e7fa0b..83bf275244 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js @@ -2,6 +2,7 @@ import { useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import Chart from 'chart.js/auto'; +import { CHART_COLOURS } from '../../config'; import messages from './messages'; export const SwipesByOrganisation = () => { @@ -31,11 +32,11 @@ export const SwipesByOrganisation = () => { { data: [35, 25, 20, 15, 5], backgroundColor: [ - 'rgba(255, 159, 64, 0.8)', // Orange for American Red Cross - 'rgba(255, 205, 86, 0.8)', // Yellow for Arizona State University - 'rgba(75, 192, 192, 0.8)', // Green for HOT - 'rgba(54, 162, 235, 0.8)', // Blue for Médecins Sans Frontières - 'rgba(201, 203, 207, 0.8)', // Grey for Others + CHART_COLOURS.red, // Orange for American Red Cross + CHART_COLOURS.orange, // Yellow for Arizona State University + CHART_COLOURS.green, // Green for HOT + CHART_COLOURS.blue, // Blue for Médecins Sans Frontières + CHART_COLOURS.gray, // Gray for Others ], borderColor: '#fff', borderWidth: 2, @@ -65,7 +66,7 @@ export const SwipesByOrganisation = () => { }, []); return ( -
+

diff --git a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js index 9fd7450bc6..d2879ff197 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js @@ -2,6 +2,7 @@ import { useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import Chart from 'chart.js/auto'; +import { CHART_COLOURS } from '../../config'; import messages from './messages'; export const SwipesByProjectType = () => { @@ -25,9 +26,9 @@ export const SwipesByProjectType = () => { { data: [75, 25], backgroundColor: [ - 'rgba(255, 159, 64, 0.8)', // Orange for Find - 'rgba(152, 251, 152, 0.8)', // Light green for Validate - 'rgba(54, 162, 235, 0.8)', // Bue for Compare + CHART_COLOURS.orange, // Orange for Find + CHART_COLOURS.green, // Green for Validate + CHART_COLOURS.blue, // Blue for Compare ], borderColor: '#fff', borderWidth: 2, @@ -53,12 +54,12 @@ export const SwipesByProjectType = () => { }, []); return ( -
+

-
+
diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js index 0939cea451..89e97f1fe0 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js @@ -3,6 +3,7 @@ import { FormattedMessage } from 'react-intl'; import { Chart } from 'chart.js/auto'; import 'chartjs-adapter-date-fns'; +import { CHART_COLOURS } from '../../config'; import messages from './messages'; const MOCK_DATA = [ @@ -29,8 +30,8 @@ export const TimeSpentContributing = () => { const context = chartRef.current.getContext('2d'); // Create gradient for the area const gradient = context.createLinearGradient(0, 0, 0, 450); - gradient.addColorStop(0, 'rgba(215, 63, 63, 1)'); - gradient.addColorStop(0.4, 'rgba(215, 63, 63, 1)'); + gradient.addColorStop(0, CHART_COLOURS.red); + gradient.addColorStop(0.4, CHART_COLOURS.red); gradient.addColorStop(1, 'rgba(215, 63, 63, 0)'); chartInstance.current = new Chart(context, { diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js index f544b497fa..9841908fa4 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js @@ -2,6 +2,7 @@ import { useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import { Chart } from 'chart.js/auto'; +import { CHART_COLOURS } from '../../config'; import messages from './messages'; const MOCK_DATA = [ @@ -23,11 +24,6 @@ export const TimeSpentContributingByDay = () => { chartInstance.current.destroy(); } - const timeSpentData = []; - for (const data of MOCK_DATA) { - timeSpentData.push(data.y); - } - chartInstance.current = new Chart(chartRef.current.getContext('2d'), { type: 'bar', data: { @@ -35,8 +31,8 @@ export const TimeSpentContributingByDay = () => { datasets: [ { label: 'Time Spent', - backgroundColor: '#d73f3f', - data: timeSpentData, + backgroundColor: CHART_COLOURS.red, + data: MOCK_DATA.map((entry) => entry.y), }, ], }, diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index b5b11362c9..bbcebb2f59 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -79,6 +79,7 @@ export const CHART_COLOURS = { blue: '#3389D6', orange: '#f09733', white: '#fff', + gray: '#C9C9C9', }; const fallbackRasterStyle = { From cebb5b7d60631291a7a4f6408ea2238171847fea Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 12 Sep 2024 17:31:40 +0530 Subject: [PATCH 021/101] Rename StatsCardWithDelta -> StatsCardWithFooter, handle delta, and add support for external style --- .../partnerMapswipeStats/overview.js | 31 ++++++++++++++----- .../projectTypeAreaStats.js | 8 ++--- frontend/src/components/statsCard.js | 22 +++++++------ frontend/src/views/partnersMapswipeStats.js | 6 ++-- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/overview.js b/frontend/src/components/partnerMapswipeStats/overview.js index 851244778d..85e7c8d0dd 100644 --- a/frontend/src/components/partnerMapswipeStats/overview.js +++ b/frontend/src/components/partnerMapswipeStats/overview.js @@ -1,7 +1,7 @@ import { FormattedMessage } from 'react-intl'; import messages from './messages'; -import { StatsCardWithDelta } from '../statsCard'; +import { StatsCardWithFooter } from '../statsCard'; import { MappingIcon, SwipeIcon, ClockIcon } from '../svgIcons'; const iconClass = 'w-100'; @@ -9,26 +9,41 @@ const iconStyle = { height: '55px' }; export const Overview = () => { return ( -
- + } description={} value={'1 M'} - delta={66k swipes in the last 30 days} + delta={ + + 66k swipes in the last 30 days + + } className="w-100" /> - } description={} value={'1 Mo 2 Days'} - delta={2 days 15 hours in the last 30 days} + delta={ + + 2 days 15 hours in the last 30 days + + } className="w-100" /> - } description={} value={'407'} - delta={33 active contributors in the last 30 days} + delta={ + + 33 active contributors in the last 30 days + + } className="w-100" />
diff --git a/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js index d7699701d5..01e1010e0d 100644 --- a/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js +++ b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js @@ -1,7 +1,7 @@ import { FormattedMessage } from 'react-intl'; import messages from './messages'; -import { StatsCardWithDelta } from '../statsCard'; +import { StatsCardWithFooter } from '../statsCard'; import { MappingIcon, SwipeIcon, ClockIcon } from '../svgIcons'; const iconClass = 'w-100'; @@ -13,7 +13,7 @@ export const ProjectTypeAreaStats = () => { className="flex justify-between items-center flex-wrap flex-nowrap-ns" style={{ gap: '1.6rem' }} > - } description={} value={'213K'} @@ -27,7 +27,7 @@ export const ProjectTypeAreaStats = () => { } className="w-100" /> - } description={} value={'114K'} @@ -38,7 +38,7 @@ export const ProjectTypeAreaStats = () => { } className="w-100" /> - } description={} value={'11K'} diff --git a/frontend/src/components/statsCard.js b/frontend/src/components/statsCard.js index 649052e455..9eae2c5deb 100644 --- a/frontend/src/components/statsCard.js +++ b/frontend/src/components/statsCard.js @@ -19,23 +19,25 @@ export const StatsCard = ({ icon, description, value, className, invertColors = ); }; -export const StatsCardWithDelta = ({ +export const StatsCardWithFooter = ({ icon, description, value, className, delta, invertColors = false, + style }) => { return (
{icon}
- : value } @@ -44,12 +46,14 @@ export const StatsCardWithDelta = ({ invertColors={invertColors} />
-
- {delta} -
+ {delta ? ( +
+ {delta} +
+ ) : null}
); }; @@ -61,7 +65,7 @@ export const StatsCardContent = ({ value, label, className, invertColors = false
); -export const StatsCardWithDeltaContent = ({ +export const StatsCardWithFooterContent = ({ value, label, className, diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 628761b59a..ea116cc620 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -9,7 +9,7 @@ import { TimeSpentContributingByDay } from '../components/partnerMapswipeStats/t import { ProjectTypeAreaStats } from '../components/partnerMapswipeStats/projectTypeAreaStats'; import { SwipesByProjectType } from '../components/partnerMapswipeStats/swipesByProjectType'; import { SwipesByOrganisation } from '../components/partnerMapswipeStats/swipesByOrganisation'; -import { StatsCardWithDelta } from '../components/statsCard'; +import { StatsCardWithFooter } from '../components/statsCard'; import messages from './messages'; import './partnersMapswipeStats.css'; @@ -47,12 +47,12 @@ export const PartnersMapswipeStats = () => {
- } value="338K" style={{ width: '48.5%' }} /> - } value="11 days 5 hrs" className="w-100" From e3c8e27398488e65567e803ac69fee0e839f6a80 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 13 Sep 2024 18:53:13 +0530 Subject: [PATCH 022/101] Add geo JSON mock data --- .../partnerMapswipeStats/geoJsonData.js | 954 ++++++++++++++++++ 1 file changed, 954 insertions(+) create mode 100644 frontend/src/components/partnerMapswipeStats/geoJsonData.js diff --git a/frontend/src/components/partnerMapswipeStats/geoJsonData.js b/frontend/src/components/partnerMapswipeStats/geoJsonData.js new file mode 100644 index 0000000000..7e9f102e79 --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/geoJsonData.js @@ -0,0 +1,954 @@ +export const geoJSON = [ + { + geojson: { + type: 'Point', + coordinates: [-85.29066571046852, 15.599525977892466], + }, + totalContribution: 958, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [17.83145975288232, 8.330671974117966], + }, + totalContribution: 5311, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-111.49991715114375, 32.90960177086646], + }, + totalContribution: 256, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [37.55277057498438, -15.428707695616959], + }, + totalContribution: 54, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-72.64747093368038, -1.361696978303415], + }, + totalContribution: 167, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-86.95286078124859, 12.834213274005663], + }, + totalContribution: 1710, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-87.0792935843515, 12.511719153924345], + }, + totalContribution: 758, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-84.77268253283326, 11.546148902370424], + }, + totalContribution: 136, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-78.55328208504923, -5.007382183145697], + }, + totalContribution: 41, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-79.59815175170993, -4.091432430493313], + }, + totalContribution: 1114, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [31.195582690216785, -16.276487330903482], + }, + totalContribution: 120, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-78.0547666676697, -2.825082939722951], + }, + totalContribution: 212, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-71.89545587098073, 1.298110173544335], + }, + totalContribution: 51, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [31.0755952578275, -16.621924168016108], + }, + totalContribution: 78, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [17.068156756986493, 9.501727907988265], + }, + totalContribution: 570, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-113.0291745899052, 35.429619930966155], + }, + totalContribution: 61199, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-85.45517170072486, 13.999002771878763], + }, + totalContribution: 3, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-88.29635175, 18.5011917], + }, + totalContribution: 259, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-73.10396834864834, 2.539712405290437], + }, + totalContribution: 117, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-114.56995240824085, 35.40558885777537], + }, + totalContribution: 242, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-74.77129309897693, -8.361615584031053], + }, + totalContribution: 48, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [21.692822242845754, 32.623341974086216], + }, + totalContribution: 33, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [31.693173872400514, -19.26958190745124], + }, + totalContribution: 109, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-114.56995242452524, 35.40558885232169], + }, + totalContribution: 5571, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-72.66913820889279, 1.188377298969117], + }, + totalContribution: 46, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [20.373907647044145, 32.00262018240991], + }, + totalContribution: 206, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-61.68237405622985, -3.810160941839017], + }, + totalContribution: 38, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [61.866132251827956, 34.546275583949544], + }, + totalContribution: 1156, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-71.17855963901002, -0.791600935042186], + }, + totalContribution: 146, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-76.71958133476728, 0.565003176010218], + }, + totalContribution: 49, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [105.16145765, -3.9464773], + }, + totalContribution: 555, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-10.65602055, 8.5784377], + }, + totalContribution: 27935, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [144.6266440884106, -5.548789486185929], + }, + totalContribution: 48, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [142.75920024697723, -3.870628843450433], + }, + totalContribution: 403, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [17.585299305735855, 8.174124394871342], + }, + totalContribution: 2015, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-76.2028327, -13.72738145], + }, + totalContribution: 37, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-73.45838763797263, 3.212288633510766], + }, + totalContribution: 56, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-84.27666033170857, 13.972490901048117], + }, + totalContribution: 17053, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [30.627553545784952, 8.588993868708355], + }, + totalContribution: 377, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-79.13114579286535, -5.11558990014555], + }, + totalContribution: 334, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-87.54121653976195, 12.907307117412893], + }, + totalContribution: 2, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [105.18227995, -3.9473116], + }, + totalContribution: 2960, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-72.52089048670294, 1.926544828731387], + }, + totalContribution: 978, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-85.5371335786593, 13.576605227349209], + }, + totalContribution: 109, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-80.22439239999999, -3.48025665], + }, + totalContribution: 74, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-78.2610077889491, -3.214116548452895], + }, + totalContribution: 1, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [90.00115255, 23.60059035], + }, + totalContribution: 666, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-91.7208309784723, 18.672273479275766], + }, + totalContribution: 96, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [22.666039189078656, 32.503308146559135], + }, + totalContribution: 13754, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-84.93675766914332, 13.902270671817577], + }, + totalContribution: 297, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-68.41429488863824, -10.658810636419465], + }, + totalContribution: 132, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [32.562545708705656, -20.24707721355555], + }, + totalContribution: 142, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [92.95309353117739, 22.9696207456173], + }, + totalContribution: 42, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-88.53803291049154, 19.554317362356283], + }, + totalContribution: 558, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [17.345159766864796, 8.091350806289423], + }, + totalContribution: 1917, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-85.1198964514573, 13.980393189284156], + }, + totalContribution: 1172, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-75.61141649329109, 1.343481984982563], + }, + totalContribution: 118, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [45.18387664443346, -24.739395546597734], + }, + totalContribution: 43, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [37.85053622258006, 1.091773186351989], + }, + totalContribution: 457, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [109.35353605000002, -7.57011055], + }, + totalContribution: 37, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [35.48272144960324, -14.415911510882315], + }, + totalContribution: 88, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [89.43787645, 23.59917345], + }, + totalContribution: 2906, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [85.14000895, 28.002809], + }, + totalContribution: 148, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-112.24826370962738, 32.909856628282334], + }, + totalContribution: 21, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-84.87894222594483, 14.11428636095636], + }, + totalContribution: 3666, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [22.81168331557454, 14.019015919841559], + }, + totalContribution: 2774, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [68.7095207013628, 36.26334834780437], + }, + totalContribution: 51, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [33.7946453, -13.97105845], + }, + totalContribution: 74, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [89.34596785, 23.7691698], + }, + totalContribution: 1406, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [32.097337697983995, -19.672736743975427], + }, + totalContribution: 102, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-113.79979149730327, 35.42005980032755], + }, + totalContribution: 12497, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [143.1167797438696, -4.656960795757978], + }, + totalContribution: 60, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-79.06916177979166, -3.034005965493468], + }, + totalContribution: 231, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [89.241669, 23.66517035], + }, + totalContribution: 76518, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [31.1615097276057, 0.777047095590563], + }, + totalContribution: 95, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-77.47483008962396, -2.671800082849541], + }, + totalContribution: 918, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [22.625851548038135, 12.668992345921207], + }, + totalContribution: 110, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-85.12586422532074, 14.627692488182635], + }, + totalContribution: 9442, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-67.77638302730661, 2.318842316135067], + }, + totalContribution: 104, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-84.91410579371339, 14.353417306945618], + }, + totalContribution: 3400, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-87.20881001860016, 12.75327604966244], + }, + totalContribution: 3530, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [37.77650094469254, -15.869041578395171], + }, + totalContribution: 142, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [27.225786837706476, -7.74059249702745], + }, + totalContribution: 43, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-83.52420997372839, 14.245248145874273], + }, + totalContribution: 157, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-100.82986828280633, 19.335069136307755], + }, + totalContribution: 34, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [17.855939639879555, 8.78452825808433], + }, + totalContribution: 864, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [82.37118483115022, 29.61829835532351], + }, + totalContribution: 456, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [142.69498962645142, -4.279295885667389], + }, + totalContribution: 283, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-76.08583221934292, 1.188235729693674], + }, + totalContribution: 115, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-78.03111022033067, -1.030023448569005], + }, + totalContribution: 1451, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [105.18473205, -3.9566782], + }, + totalContribution: 111, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [143.58635692996836, -4.368596348999677], + }, + totalContribution: 170, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-84.22885167703019, 13.533698812518912], + }, + totalContribution: 10996, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [93.22530083821907, 23.447192435994232], + }, + totalContribution: 53, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [32.755421668802896, -17.93530847459356], + }, + totalContribution: 199, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [81.75186187109553, 29.19518691339494], + }, + totalContribution: 2567, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [21.0308920603301, 32.49925302210381], + }, + totalContribution: 830, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [93.1666453156887, 23.6544015985086], + }, + totalContribution: 77, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-112.25566386169581, 34.80325861626855], + }, + totalContribution: 378, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-77.34633979411231, -3.761121621878996], + }, + totalContribution: 41, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [22.73889139426033, 14.534553645345042], + }, + totalContribution: 8003, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [35.03770801825091, -15.771260184591872], + }, + totalContribution: 1364, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-79.19674088255486, -3.256832333960287], + }, + totalContribution: 151, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [120.38377675, 16.83994845], + }, + totalContribution: 16, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-111.49691306212117, 33.54079445034419], + }, + totalContribution: 308, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [18.074765184412374, 8.257902650895652], + }, + totalContribution: 4038, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [35.06200485, -15.783931], + }, + totalContribution: 74, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-91.35947451373393, 18.3314215788839], + }, + totalContribution: 10344, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-114.54959912741137, 34.77524490124089], + }, + totalContribution: 317, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [17.585299305735855, 8.174124394871342], + }, + totalContribution: 2430, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-74.94386755939892, -9.567978301900402], + }, + totalContribution: 263, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-111.48731874510736, 35.43398171764529], + }, + totalContribution: 18925, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-74.81170044558343, -8.920617444439028], + }, + totalContribution: 158, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-73.38392646574127, 2.966663019116976], + }, + totalContribution: 226, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [39.138121600783734, -15.124347352480978], + }, + totalContribution: 378, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [22.888767367107956, 13.445056045099104], + }, + totalContribution: 201, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-91.3592784, 15.7464449], + }, + totalContribution: 484, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [-73.701759296725, 2.967536670595839], + }, + totalContribution: 58, + __typename: 'MapContributionStatsType', + }, + { + geojson: { + type: 'Point', + coordinates: [89.6161154, 23.5335064], + }, + totalContribution: 37, + __typename: 'MapContributionStatsType', + }, +]; From 935206ee00a83a0c702e0f965815c9136e6e52d0 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 13 Sep 2024 18:54:31 +0530 Subject: [PATCH 023/101] feat: create contributions heatmap component with mock data --- .../contributionsHeatmap.js | 147 ++++++++++++++++++ .../partnerMapswipeStats/messages.js | 4 + 2 files changed, 151 insertions(+) create mode 100644 frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js diff --git a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js new file mode 100644 index 0000000000..2bbd593b71 --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js @@ -0,0 +1,147 @@ +import React, { useState, useEffect, useRef } from 'react'; +import mapboxgl from 'mapbox-gl'; +import { latLngToCell, cellToBoundary } from 'h3-js'; +import { FormattedMessage } from 'react-intl'; +import 'mapbox-gl/dist/mapbox-gl.css'; + +import { MAPBOX_TOKEN, MAP_STYLE } from '../../config'; +import { CHART_COLOURS } from '../../config'; +import { geoJSON } from './geoJsonData'; +import messages from './messages'; + +mapboxgl.accessToken = MAPBOX_TOKEN; + +export const ContributionsHeatmap = () => { + const mapContainer = useRef(null); + const map = useRef(null); + const [lng, setLng] = useState(0); + const [lat, setLat] = useState(0); + const [zoom, setZoom] = useState(1.25); + + useEffect(() => { + if (map.current) return; // initialize map only once + + map.current = new mapboxgl.Map({ + container: mapContainer.current, + style: MAP_STYLE, + center: [lng, lat], + zoom: zoom, + bearing: 0, + pitch: 0, + }); + + const getStyle = (row) => { + const styles = [ + { + color: CHART_COLOURS.orange, + opacity: 0.2, + }, + { + color: CHART_COLOURS.orange, + opacity: 0.4, + }, + { + color: CHART_COLOURS.orange, + opacity: 0.6, + }, + { + color: CHART_COLOURS.orange, + opacity: 0.7, + }, + { + color: CHART_COLOURS.red, + opacity: 0.8, + }, + ]; + + if (Number(row.totalContributionCount) === 0) { + return { opacity: 0 }; + } + + if (Number(row.totalContributionCount) < 250) { + return styles[0]; + } + if (Number(row.totalContributionCount) < 500) { + return styles[1]; + } + if (Number(row.totalContributionCount) < 1000) { + return styles[2]; + } + if (Number(row.totalContributionCount) < 1500) { + return styles[3]; + } + return styles[4]; + }; + + map.current.on('load', () => { + const hexagonsArrayCopy = geoJSON.map((data) => { + const h3Index = latLngToCell(data.geojson.coordinates[1], data.geojson.coordinates[0], 1); + return { + hexindex7: h3Index, + totalContributionCount: data.totalContribution, + }; + }); + + const features = hexagonsArrayCopy.map((row) => { + const style = getStyle(row); + return { + type: 'Feature', + properties: { + color: style.color, + opacity: style.opacity, + id: row.hexindex7, + }, + geometry: { + type: 'Polygon', + coordinates: [cellToBoundary(row.hexindex7, true)], + }, + }; + }); + + map.current.addSource('hexbin', { + type: 'geojson', + data: { + type: 'FeatureCollection', + features: features, + }, + }); + + map.current.addLayer({ + id: 'polyline-layer', + type: 'fill', + source: 'hexbin', + paint: { + 'fill-outline-color': 'white', + 'fill-color': ['get', 'color'], + 'fill-opacity': ['get', 'opacity'], + }, + }); + + map.current.addLayer({ + id: 'hexbin-outline', + type: 'line', + source: 'hexbin', + paint: { + 'line-color': '#ffffff', + 'line-width': 1, + }, + }); + }); + + map.current.on('move', () => { + setLng(map.current.getCenter().lng.toFixed(4)); + setLat(map.current.getCenter().lat.toFixed(4)); + setZoom(map.current.getZoom().toFixed(2)); + }); + }, []); + + return ( +
+

+ +

+ +
+
+ ); +}; diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index 4f241fca28..7b31d18528 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -64,6 +64,10 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.contributions.tooltip', defaultMessage: 'contribution(s)', }, + contributionsHeatmap: { + id: 'management.partners.stats.mapswipe.contributions.heatmap', + defaultMessage: 'Contributions Heatmap', + }, timeSpentContributing: { id: 'management.partners.stats.mapswipe.timeSpentContributing', defaultMessage: 'Time Spent Contributing', From 31ac12560052e4abc319618198e31c99e784bf59 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 13 Sep 2024 18:55:04 +0530 Subject: [PATCH 024/101] feat: show ContributionsHeatmap section --- frontend/src/views/partnersMapswipeStats.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index ea116cc620..eed682c021 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -4,6 +4,7 @@ import { InfoIcon } from '../components/svgIcons'; import { Overview } from '../components/partnerMapswipeStats/overview'; import { GroupMembers } from '../components/partnerMapswipeStats/groupMembers'; import { ContributionsGrid } from '../components/partnerMapswipeStats/contributionsGrid'; +import { ContributionsHeatmap } from '../components/partnerMapswipeStats/contributionsHeatmap'; import { TimeSpentContributing } from '../components/partnerMapswipeStats/timeSpentContributing'; import { TimeSpentContributingByDay } from '../components/partnerMapswipeStats/timeSpentContributingByDay'; import { ProjectTypeAreaStats } from '../components/partnerMapswipeStats/projectTypeAreaStats'; @@ -34,6 +35,10 @@ export const PartnersMapswipeStats = () => {
+
+ +
+
From 5e35a61cc8a98512c20979c3157a1a32e9aaa640 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 13 Sep 2024 16:22:24 +0530 Subject: [PATCH 025/101] chore: install h3-js as a dependancy --- backend/filtered_mapswipe_stats.json | 6303 ++++++++++++++++++++++++++ backend/general_mapswipe_stats.json | 133 + frontend/package.json | 1 + frontend/yarn.lock | 5 + 4 files changed, 6442 insertions(+) create mode 100644 backend/filtered_mapswipe_stats.json create mode 100644 backend/general_mapswipe_stats.json diff --git a/backend/filtered_mapswipe_stats.json b/backend/filtered_mapswipe_stats.json new file mode 100644 index 0000000000..be70015d0d --- /dev/null +++ b/backend/filtered_mapswipe_stats.json @@ -0,0 +1,6303 @@ +{ + "data": { + "userGroupStats": { + "id": "-NL6WXPOdFyWACqwNU2O", + "filteredStats": { + "userStats": [ + { + "totalMappingProjects": 3, + "totalSwipeTime": 6288, + "totalSwipes": 1380, + "username": "jaiC", + "userId": "05AfE1oB5vY6EbPqEjkdR9o0s4D3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 69, + "totalSwipes": 37, + "username": "jainMedha", + "userId": "08zR2YnWNXh8b5sFd2xEVa2c2t53", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 18, + "totalSwipeTime": 9469, + "totalSwipes": 3455, + "username": "Micah", + "userId": "09G9UqZq9yODe5XRl1ekhchba3x1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 381, + "totalSwipes": 111, + "username": "divvs2007", + "userId": "0f1KIx0VmmRcqxdwOp2jnlZyNWx2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 2607, + "totalSwipes": 1036, + "username": "ashleycruzc", + "userId": "0N1lhg0NJAN6lvAfEBkhzXnXgv53", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 17845, + "totalSwipes": 4514, + "username": "tanvi", + "userId": "0QCwiA4kCzac6rlsedwmu5vPJb53", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 1280, + "totalSwipes": 836, + "username": "janyapatel", + "userId": "0QQCPKsz2Bh3HtmseOBqSFN6qiq2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 662, + "totalSwipes": 168, + "username": "alau01", + "userId": "0ZjtwYDEtccUoj0DVo2hTBRCeAb2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 4463, + "totalSwipes": 1147, + "username": "Caleb-Roe", + "userId": "1I5AnlHnnzcTArbrrRGeuEb5sYj2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 1667, + "totalSwipes": 783, + "username": "sree", + "userId": "1JDWIeLlLUY8LZTO7GbaoNp45kv1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 8, + "totalSwipeTime": 1251, + "totalSwipes": 847, + "username": "hasini reddy alva", + "userId": "1Y8TnMHfeUh0l43lkK9XMUO6qaq1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 360, + "totalSwipes": 74, + "username": "maryc", + "userId": "2jdrfVroUbX9PIIY1vVRa2bT1YB3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 7, + "totalSwipeTime": 3481, + "totalSwipes": 1105, + "username": "elizak", + "userId": "2nNiuV7OdFbAhhSLKm41OY2gbNR2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 3480, + "totalSwipes": 1813, + "username": "tanisha_j", + "userId": "2Orm8Un7j5e2rOq6XvqweuFDSUf2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 165, + "totalSwipes": 74, + "username": "Jeff Alan", + "userId": "2Pu6lkz3J9TYervsBjWNq7nq9Vt2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 18, + "totalSwipeTime": 26290, + "totalSwipes": 6928, + "username": "Tania Perez M.", + "userId": "2T72ImN2Lgh9Q4LfHN1C3TwX9vo2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 752, + "totalSwipes": 428, + "username": "SAF Team DE-Meade-Central MD", + "userId": "2Z2utzhZvDXxrhJYLYfMKXMOMPb2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 686, + "totalSwipes": 148, + "username": "Cookie", + "userId": "2z3CgS5aKoc5RG7ncJ0i9Zl4Tow2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 120, + "totalSwipes": 50, + "username": "NoUsername", + "userId": "3EFAn9TfckdPxknTfL1W3ZlGcc12", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 2872, + "totalSwipes": 666, + "username": "CynthiaAnna", + "userId": "3QKTiV7g2af3MEF8fhpj0Q8V8mA3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1330, + "totalSwipes": 444, + "username": "davilloyd", + "userId": "3QVA7QlH1ONgsmsTiHClGz9Jc0U2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 378, + "totalSwipes": 111, + "username": "sebettencourt", + "userId": "3Ron1P0VSqcmWfpMxMStvDdyQmS2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 186, + "totalSwipes": 37, + "username": "sarah", + "userId": "3UZYMjitTzS4QX8ZO4QW8yfF15u1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 585, + "totalSwipes": 280, + "username": "anikag", + "userId": "3wq3BJQSfvNcHKOB676aIo0ttC63", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 341, + "totalSwipes": 70, + "username": "dianav", + "userId": "4jupcphb7XPvfvYNdRPkBiUtxOd2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 259, + "totalSwipes": 47, + "username": "juliarrowan21", + "userId": "4MRAHVq0iNf9XWKMJ04mQ2PCzgk2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 430, + "totalSwipes": 316, + "username": "aeraapk", + "userId": "4PfJr5Py7VcLj32duxm52f2WXxd2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 16, + "totalSwipeTime": 5225, + "totalSwipes": 1141, + "username": "Jordan", + "userId": "4SL9TuZNCGVLCoiXiexCnKPAwo72", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 18, + "totalSwipeTime": 248149, + "totalSwipes": 62440, + "username": "ram_chitti", + "userId": "4xx7ekDmRWUA7VxyiASn7y9mXhq2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 4537, + "totalSwipes": 1739, + "username": "Jeff Lewis", + "userId": "56tUnqze11UO5TPwjEOUugHJpRV2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 892, + "totalSwipes": 185, + "username": "rafaello55555", + "userId": "57ePPh5M27OHJeoEDJ681bp8Z2D2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 354, + "totalSwipes": 92, + "username": "sarath", + "userId": "58QhlkZNbrhjz87CaD4qTTLp5Ki2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 9, + "totalSwipeTime": 6961, + "totalSwipes": 1561, + "username": "i.fecher", + "userId": "5com0SwjxJb3kRk0zLxJgKA5joH2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 16, + "totalSwipeTime": 14163, + "totalSwipes": 4898, + "username": "sm_21", + "userId": "5GcQFhvkEUZ4gOx6QRqYfDakLrG3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 324, + "totalSwipes": 184, + "username": "siddhik", + "userId": "5GkjMU7ROlfu9Vh6Xw0U2umip6m2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 14, + "totalSwipeTime": 20440, + "totalSwipes": 10046, + "username": "lily", + "userId": "5qB1GynAGtOXvz3cROsEAsLGtn82", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 319, + "totalSwipes": 74, + "username": "dhorn", + "userId": "5sH0UWbwzsYXaEgzArpN8mfKhbC2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 1817, + "totalSwipes": 507, + "username": "peacefulrita", + "userId": "5sJ2b98uXBNCkF3whJoH9oOuDuT2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 46159, + "totalSwipes": 20608, + "username": "Megha081", + "userId": "5vO3OZZ1HWgckkWr45rueF0GknD3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 10, + "totalSwipeTime": 1876, + "totalSwipes": 601, + "username": "AvivaLiu", + "userId": "6dkxMKQ2AtMesNj3SU4RCZbIDPw1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 362, + "totalSwipes": 74, + "username": "eeshanz", + "userId": "6h26Qyw0zzajnPJUy9n0dMnAeP22", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 466, + "totalSwipes": 120, + "username": "AryaJha", + "userId": "6nq5SHkCArZKiUh8h8BiYwl5qP12", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 60, + "totalSwipes": 37, + "username": "matthew.albano1", + "userId": "6XvRfvIC93gQr3d0nMAR0TG1sgO2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 622, + "totalSwipes": 148, + "username": "JSolerPA", + "userId": "722Zcqgo7tQ9fTseAmlDXC99cFV2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "JLee", + "userId": "79JKCvG9zXUNv4LSEelrbUZSCrz1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 2979, + "totalSwipes": 502, + "username": "mare", + "userId": "7akDlemMCFURcKChqs8c3ua3Rr22", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 75, + "totalSwipes": 37, + "username": "zgolds", + "userId": "7OzoUURgjoOlWzvA77mKEPEdtcY2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 466, + "totalSwipes": 111, + "username": "calimapnerd", + "userId": "7xfrcUFjw7WNrsVKUeENexN6cx42", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 4287, + "totalSwipes": 1809, + "username": "sanjana", + "userId": "7ZmwkRAV1YYaqAgA9GrY1BT3nir2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 2866, + "totalSwipes": 555, + "username": "eagle46", + "userId": "81cQDKwGzvW4IGVwIZdvZOHHtqi1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 390, + "totalSwipes": 148, + "username": "sandyrav", + "userId": "8bhk4Fb0MWadotpzO216ye4DUYO2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 209, + "totalSwipes": 37, + "username": "PORKCHOP", + "userId": "8P7XEirWiCa5EwvIuvxkTaJ6Bzo1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 623, + "totalSwipes": 148, + "username": "Meirina Hutabarat", + "userId": "8UOBmJHCCGX45buJgF6LBtl9nXz2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1410, + "totalSwipes": 259, + "username": "jpaduveris", + "userId": "98Y1CEGCofURSQIJhF1HGJHf2mC2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 63, + "totalSwipeTime": 20148, + "totalSwipes": 11011, + "username": "vmvm", + "userId": "9EiZFxEi7TXCaYGo8awsjZ1KdPI3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 784, + "totalSwipes": 447, + "username": "alaynaprice ", + "userId": "9ivwOjO4rUNCRxRLxQpIjqHiiju1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1005, + "totalSwipes": 185, + "username": "kjuvan", + "userId": "a04pYbvwD6fzMyaymZyIBOY7jTQ2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1888, + "totalSwipes": 592, + "username": "JoeGillian", + "userId": "A1JzBh0ZYPPaz0MX7quuVEN62Lw1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 913, + "totalSwipes": 132, + "username": "vmsomisetty", + "userId": "A3DnjcRrTpR8ST6nPFyI8bXPUHT2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 358, + "totalSwipes": 1295, + "username": "akashkumar", + "userId": "A9cjlAQiYvhZnxXezVQ0AsxVRxy1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 188, + "totalSwipes": 77, + "username": "tenia", + "userId": "aiwOkak1gsbPt90bfTbrIV2Ogr83", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 273, + "totalSwipes": 106, + "username": "mrod201000", + "userId": "ajnTnLHGnubB3LJ6lrCzjKaitsg1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 378, + "totalSwipes": 102, + "username": "Michael_T", + "userId": "ak3F5vvqUPbyyiGcyjrmMikU1su1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 125, + "totalSwipes": 79, + "username": "campbell_m", + "userId": "aKeR7PTAfofSG3jwM7E2ha2kWrj1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 17, + "totalSwipeTime": 13529, + "totalSwipes": 5736, + "username": "AsmiR1", + "userId": "aPQISKN6ypd9xC05aGzncCOnhFO2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 1436, + "totalSwipes": 378, + "username": "Adhithya", + "userId": "asnaSyGScKXdjKTbgeaJghd36lR2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 336, + "totalSwipes": 111, + "username": "francesca_otiniano", + "userId": "b9KhIjfRhaVWmyaez8TG1RXXphs2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 320, + "totalSwipes": 74, + "username": "ND04", + "userId": "bcWRCgpotRPbg6AaZcUg7uCntLD2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 7, + "totalSwipeTime": 350, + "totalSwipes": 260, + "username": "neervi", + "userId": "BEh2F32cBXgh1W4B40vBu1gL0ji1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 7, + "totalSwipeTime": 2015, + "totalSwipes": 984, + "username": "lrumple", + "userId": "BKblaH18kMaFuwwYLZyoMxkc0r32", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 134, + "totalSwipes": 16, + "username": "wolfegeo", + "userId": "BkMUEHyMBSYI938sbAUx2SmlUJf2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 278, + "totalSwipes": 188, + "username": "Vidhi Patel", + "userId": "bvuUhuPBXSTS6Kla94eVpVwQTcR2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 784, + "totalSwipes": 103, + "username": "allison.lalik", + "userId": "bVuxnsL7mueANiYNUTUNbfRIRf42", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 13, + "totalSwipeTime": 6517, + "totalSwipes": 3977, + "username": "sai_dirisina", + "userId": "c9EA7diVIxPwQfI4fWvy6Kguo2h1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "Stoic_Historian", + "userId": "cBZpfJzrVkau62jG2RTCxpqFKMo2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 186, + "totalSwipes": 37, + "username": "Widlande08", + "userId": "CClMxFYW8aMAg9hZf5YlrrLrYOk1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 225, + "totalSwipes": 68, + "username": "mattlee", + "userId": "CcVgipm7vGQXjY2ZSExMlKhwiQF3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 451, + "totalSwipes": 74, + "username": "autumnj0nes", + "userId": "CE960vgetUOAse82G2Vr81pEhO23", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 812, + "totalSwipes": 444, + "username": "anijahsayles", + "userId": "cFjV3WL06HbNUmhcyfBnVRPYTG83", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 2194, + "totalSwipes": 541, + "username": "Olivia ann Shibu", + "userId": "chnIwT1gpnggeDvmdv9gHrf0Zam2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 23, + "totalSwipeTime": 20933, + "totalSwipes": 11596, + "username": "sachchit", + "userId": "COCQwgVRREd8wAlhW2iJxU0I4L92", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 14, + "totalSwipeTime": 18448, + "totalSwipes": 8310, + "username": "saraht2007", + "userId": "CqA73FsHuJbLjmvvZG5w3p2QgO42", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 3016, + "totalSwipes": 542, + "username": "FrederikeH", + "userId": "crcnFe6tFFPfj6lhbVEJmQ8MF1U2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 174, + "totalSwipes": 37, + "username": "jpiehl34", + "userId": "d0jwYgZC62eU0WmVQQpMBMDsrO03", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 2214, + "totalSwipes": 1134, + "username": "Victoria", + "userId": "d8MZEpFNpqbmEFBXnxMdRYfuvNY2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 10107, + "totalSwipes": 2812, + "username": "kpolanco1120", + "userId": "DdhHvENYxXf0FusTdeGhju887HF2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 631, + "totalSwipes": 111, + "username": "KGartland", + "userId": "dFeAYxrlzMTDc2jUlJSMOB4fTbS2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 1933, + "totalSwipes": 921, + "username": "JLR NIC TIERNEY", + "userId": "DIu7VALwoTOTvm0aqkIXPvgl9x73", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 523, + "totalSwipes": 148, + "username": "JLR Beth", + "userId": "DNABjetEMZX5ILEa0mYb1pcztxx1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 352, + "totalSwipes": 96, + "username": "sruthi", + "userId": "DOgDzCQkjmSlfMkYwfLuwkYNOtg2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 163, + "totalSwipes": 37, + "username": "camiguzman", + "userId": "Dpd239EWM0R659nP7HzYYu55DB32", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 550, + "totalSwipes": 111, + "username": "mzlita", + "userId": "Dw5npFDv3Ed3GerOJaspyZCgpCA3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 5122, + "totalSwipes": 3700, + "username": "Abu-ammar Ghakhar", + "userId": "e5GMgxFHvXfdQOL7xa5PeeXQ12C2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1132, + "totalSwipes": 518, + "username": "aryav09", + "userId": "EIXbzVCl10cTOuISfhUCfDCqodH2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 5103, + "totalSwipes": 1924, + "username": "cnietsche", + "userId": "eLTIeNq1IDfs0dSgDFmUw6uuc1v1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1535, + "totalSwipes": 481, + "username": "Geo305", + "userId": "Etc2BwBAeuQNmUAKtJAIBc86i6V2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 34, + "totalSwipeTime": 149514, + "totalSwipes": 28596, + "username": "naseeha_masood", + "userId": "eVMRAVOUkrWkk3OCOi5qS8EYNhi2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 14, + "totalSwipeTime": 3843, + "totalSwipes": 1961, + "username": "justahelper", + "userId": "EzLDPAmne8WuLTxNPVb6RMTucWp1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 677, + "totalSwipes": 111, + "username": "lamaybin", + "userId": "f4jUTgeqlTdjNmHfH8J8FuO12xW2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 14, + "totalSwipeTime": 6311, + "totalSwipes": 2681, + "username": "neevneevv", + "userId": "FAg1dm3D4ffi4K5d2FSRE9p4rOe2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 517, + "totalSwipes": 156, + "username": "malysha19", + "userId": "fDxfYip0CEPukVK0IeMrCDpQgeO2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 11, + "totalSwipeTime": 22124, + "totalSwipes": 10148, + "username": "Benjam", + "userId": "fLgqmdP8HNZ6zatJ7eFKYPeEtPq2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 334, + "totalSwipes": 215, + "username": "BLESSING SONG", + "userId": "fYeVvqSYwMYQTcb5N0y03j5br692", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 310, + "totalSwipes": 88, + "username": "Saviana", + "userId": "G9SMvPNTeUf7CV5yjLpvFTEFRYz2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 193, + "totalSwipes": 37, + "username": "Garciaju037", + "userId": "geIABDUK0xdYh32bN4ozIUpCLDA3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1715, + "totalSwipes": 1055, + "username": "iangoley", + "userId": "GGHNqWxj5yaunOG1kcXjwif9WE83", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 17197, + "totalSwipes": 5365, + "username": "merlynaranha", + "userId": "gKUVPz01DDUQlhHeWffW1XZVlxX2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 3135, + "totalSwipes": 1459, + "username": "maneli", + "userId": "GpJ9IegUOvQX9qPtS3xjaKbLBNV2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 462, + "totalSwipes": 58, + "username": "pedrobueno", + "userId": "GPjgVef9DWaXAnUZnmk7ue2EQzj2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "erynkmoney", + "userId": "Gvoanxx3oNOPdBPZnuK1rQsyfrz2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 508, + "totalSwipes": 230, + "username": "Yuvraj", + "userId": "H1tQghMFYee6woH8qY1RUInj3Tm1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 558, + "totalSwipes": 227, + "username": "sharonanton21", + "userId": "hd2qQhwwGpN29OvucOlkJk75cVl2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 167, + "totalSwipes": 37, + "username": "emi.molla", + "userId": "HjUmmdZsPUgjiPdZOYNakqQUkjt2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1595, + "totalSwipes": 190, + "username": "saraa__s", + "userId": "hOoUVVU6vpfIfcO8V0t3YebLuZ03", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 262, + "totalSwipes": 81, + "username": "mgavin", + "userId": "HP4hRk6lFWQuRvFBoZZs2b4DIJ92", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 341, + "totalSwipes": 74, + "username": "mlayne29", + "userId": "hSxZ2jSEyVVJuxAHNtmJdPSswmC2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 108, + "totalSwipes": 37, + "username": "eliandres", + "userId": "huHyBy4WcPW5bBrpC0mp5COFwUx1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 8, + "totalSwipeTime": 1790, + "totalSwipes": 566, + "username": "stephmbwilson", + "userId": "HxUHd2R0pohG2bE1c3dw1CM8jfv2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 764, + "totalSwipes": 148, + "username": "Paulette Brown", + "userId": "HzTdhvKEwQeb4VRCfZgbpzewZL63", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 3267, + "totalSwipes": 703, + "username": "Kevi", + "userId": "i6ItLfEEX8Py5pdhZb3x9IZpCoq1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 5391, + "totalSwipes": 1184, + "username": "sss1447", + "userId": "IFhUqsPlqRNbLXAzc9vQFntXjQQ2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 130, + "totalSwipes": 58, + "username": "Izzy2crazy ", + "userId": "IHaZ6GYprVeeMHZNm09En4xtAcL2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 1227, + "totalSwipes": 527, + "username": "kvellatur", + "userId": "inuzkmShesYsvunH9uERhEn65qr2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 11, + "totalSwipeTime": 3222, + "totalSwipes": 1741, + "username": "BenjaminPoore", + "userId": "IY5F5WcChNOoLaf5mjb6WWzd4E93", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "Kittie", + "userId": "J9xpHpTi2IdrbU3YZF4mAgB0VT12", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 446, + "totalSwipes": 111, + "username": "DGGreen", + "userId": "jF7xnwm1ezaCH0o3brk4ilbuli82", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 434, + "totalSwipes": 169, + "username": "danbjoseph", + "userId": "JftvKNTCnBZzSq0LDoMNTP7lVLq2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 1625, + "totalSwipes": 287, + "username": "VAISHNIKA1711", + "userId": "jHASsntBjjfp7w3YwfHLihW4G792", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 646, + "totalSwipes": 407, + "username": "lyssa", + "userId": "ji06JiDZPdgckAqXS8pPpkZNfet1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 520, + "totalSwipes": 133, + "username": "Jelynz", + "userId": "JjuXTheJCQZ3Ma5z1XZeFCyk6v12", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1709, + "totalSwipes": 481, + "username": "mwcoulter", + "userId": "jMdaGHrVoWOSZAAmgaqQUAX04dS2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 9, + "totalSwipeTime": 8467, + "totalSwipes": 23581, + "username": "Charan", + "userId": "jZVHsz5uTahDDUqseTF9vrsPv1x1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 6, + "totalSwipeTime": 591, + "totalSwipes": 147, + "username": "Katbaloo ", + "userId": "kBa3j56BxJYflKZoyGi4ckDqcb63", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 187, + "totalSwipes": 37, + "username": "helloitsme", + "userId": "KJ3M05ezjuWxO6DvvLIxGaiDSwN2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 86, + "totalSwipes": 48, + "username": "Puja_K", + "userId": "kMAPG0Rgh8NxN4dVSEGCtX9qQpC3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 932, + "totalSwipes": 111, + "username": "llorigold", + "userId": "knPQQGuPvSW4FC1EmU8ORIPBlS73", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 300, + "totalSwipes": 111, + "username": "Tammy Walker", + "userId": "kQgzR3CXBAOl8VdBJrN6DiZfttH2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 1042, + "totalSwipes": 333, + "username": "atalia17", + "userId": "ks2e7UMObiTgfqhfdO6WsBPteWw1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 839, + "totalSwipes": 282, + "username": "", + "userId": "kWaffzrUCjclg3PTuJP8FoRSRev1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 193, + "totalSwipes": 99, + "username": "anushka", + "userId": "l1rCq4U5rEPn7PsOmMaXd9bNoYi2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 276, + "totalSwipes": 148, + "username": "JLRLAURAWOOD", + "userId": "LgR0G6F1XWfbp83T6MxkHJV473M2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 441, + "totalSwipes": 276, + "username": "luke2023", + "userId": "LMhZk387pXSMNaceVO4YmLDO5vk1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1565, + "totalSwipes": 777, + "username": "bhaveshmit", + "userId": "LSw2PHUUjQNPxvRgmUfFeljv8t83", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 93, + "totalSwipes": 45, + "username": "kgiamett", + "userId": "LuwOjIbYIjUecPDtrZntEuEBC6A2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 12591, + "totalSwipes": 3451, + "username": "mcreynolds", + "userId": "LWDO6xpaU1TjkjBEeaMeCen2WSR2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 30859, + "totalSwipes": 14614, + "username": "jacobk", + "userId": "LyKNT8FXQHhFNbhVfYqckoZTsIo2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 5388, + "totalSwipes": 2621, + "username": "Jazmin L", + "userId": "m4ak0ewiJQSjQ7i01CfTHNxVOkF2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 4235, + "totalSwipes": 753, + "username": "Priya D ", + "userId": "Ma1oiVuWy5UhqLZFNcH8nidciI23", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 3395, + "totalSwipes": 1043, + "username": "m.dunya", + "userId": "mFzfQtCMHgd66hr0MvGjHSTBtBE3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 7094, + "totalSwipes": 2988, + "username": "Aniket Mathur", + "userId": "MnoF74VqJOaHYni9Y7TrsimHsIj2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 85, + "totalSwipes": 37, + "username": "Chintan0530", + "userId": "mqMy55HR0OV1aGVRYTB4c5B0Bz63", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 328, + "totalSwipes": 74, + "username": "shanikquat", + "userId": "MqX4LrEM3xX0rP565ddSmO2rDLg2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 479, + "totalSwipes": 57, + "username": "quyenclifton", + "userId": "muGPqe0exSQOnxlVbX3gLol8oTw2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 268, + "totalSwipes": 90, + "username": "jparrino1", + "userId": "MUxz0uTTMCPPCoKfcQvVXewESBF2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 716, + "totalSwipes": 156, + "username": "mediaxina", + "userId": "my1FarXc6jbMtuSDyeblQfDMMbv2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 58, + "totalSwipes": 60, + "username": "KyleLehrfeld", + "userId": "mZKhfN9FQ9NotH3QJD5hdiIxjL63", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 389, + "totalSwipes": 111, + "username": "maj1874", + "userId": "N3x0i8zmwbZJS6CZoeo1WVezVv42", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 3732, + "totalSwipes": 1221, + "username": "Ashley J", + "userId": "N6onlKXBThb6CPElQJsTvLhc9yF3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 594, + "totalSwipes": 148, + "username": "Liza ", + "userId": "NBAfMZ2YMOgFpxH2ANFMd3OhHxU2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 7, + "totalSwipeTime": 7797, + "totalSwipes": 1836, + "username": "T_Wang", + "userId": "Nfib1B3MFqhqjbi3Ljr4AagRPDV2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 356, + "totalSwipes": 111, + "username": "JLR Mark Beamont", + "userId": "nH9r8YGAHvggVcHxLlj0Ca4SYI13", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 6, + "totalSwipeTime": 10152, + "totalSwipes": 2232, + "username": "anvita", + "userId": "NN1HPb2o7wVoEx5bDwgWBdytMwZ2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 536, + "totalSwipes": 96, + "username": "LHeaton", + "userId": "NraKWREgqBdcIfOTKbW9lg3R5dp2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "mina", + "userId": "nRjTrhQFU4Voq0I3ndfESQzWt1p2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 386, + "totalSwipes": 81, + "username": "elgurl", + "userId": "nVvqLkfYnERzRkQAwgJn5sQRI8k1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 412, + "totalSwipes": 164, + "username": "tindel02", + "userId": "Nyg0FmzjIth6gKe8FyT1miaAZKH3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 10, + "totalSwipeTime": 2053, + "totalSwipes": 625, + "username": "Puff", + "userId": "oFI5vcDzK6ZfH72jDkrAKYQPNkD3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 106, + "totalSwipes": 106, + "username": "ss09", + "userId": "oGfQitabVYWJ7cXpAPLPj6bZ2dq1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 7273, + "totalSwipes": 4255, + "username": "Linh Tran", + "userId": "oKuNEXIdmmhjAYziyEQKzuA2F8g1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 6, + "totalSwipeTime": 3548, + "totalSwipes": 906, + "username": "", + "userId": "osm:13675489", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 10, + "totalSwipeTime": 965, + "totalSwipes": 471, + "username": "", + "userId": "osm:16034655", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 29, + "totalSwipeTime": 84450, + "totalSwipes": 23745, + "username": "", + "userId": "osm:16361194", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "", + "userId": "osm:17082842", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 6993, + "totalSwipes": 2635, + "username": "", + "userId": "osm:19988894", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 153, + "totalSwipes": 74, + "username": "", + "userId": "osm:21700068", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 24, + "totalSwipeTime": 10713, + "totalSwipes": 6388, + "username": "", + "userId": "osm:3590711", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 334, + "totalSwipes": 74, + "username": "kalkidan", + "userId": "OYquaTFGOANuwHYQZ2hTCBxORgw1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 202, + "totalSwipes": 37, + "username": "JLR Amber Hickey", + "userId": "P0V8hpfwF9W1D0sTU8wV9gOPqB22", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 3336, + "totalSwipes": 710, + "username": "ihuckstorf", + "userId": "p1VeIV4k2Dfswvz5qOgGfgP9Qea2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1333, + "totalSwipes": 333, + "username": "kayakgirl", + "userId": "pINhNGLfuzUOE7LgDyTuk3B47dk1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 6, + "totalSwipeTime": 953, + "totalSwipes": 280, + "username": "TAsh2", + "userId": "PJtKS6RLJbXlWsKT3vtbXuK4U9r2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 8, + "totalSwipeTime": 23865, + "totalSwipes": 9168, + "username": "sunshine", + "userId": "pkUllUB7SDV97tI1xrT0Jher1jA3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "kaleb", + "userId": "PNIwioFW5mXodvlqS8hwTHQB0U32", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 1038, + "totalSwipes": 449, + "username": "helkam-2307", + "userId": "PORyLDshKCQ6A0wJke9LyggsAxz1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 570, + "totalSwipes": 444, + "username": "Cguerrero", + "userId": "Ptrq0b8LGYVLzH2yxrm2AMdmjrx1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 6, + "totalSwipeTime": 4091, + "totalSwipes": 1314, + "username": "sreebalaji", + "userId": "Qb6LA8r6vJMJiXIHW8vPAsUEET13", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 191, + "totalSwipes": 111, + "username": "vera.b.09", + "userId": "Qkf149DsN8WkaKyIzYCxTJFOEUD2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 522, + "totalSwipes": 150, + "username": "NakshatraN.", + "userId": "QTbLt8SOF4elbKZUHpzTG0bQ6dx2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 17, + "totalSwipeTime": 26769, + "totalSwipes": 6507, + "username": "ShreyaD", + "userId": "RjvXjOXh2PdvH8CpN3NoH27VWpq2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 959, + "totalSwipes": 395, + "username": "vikareel", + "userId": "rryJH5a63pg1e6hV9bN3qvlC8rX2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 2505, + "totalSwipes": 844, + "username": "Simran", + "userId": "RW5Gie2SN4eyxxxKuDDvZ5eI1N63", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 678, + "totalSwipes": 165, + "username": "MattHillsberg", + "userId": "rznLCLRp8ycsINpteJSX7FVePJZ2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1331, + "totalSwipes": 481, + "username": "MontielMA", + "userId": "s1R2jE1CIEWoAG60Gyf1R2QY1VS2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 983, + "totalSwipes": 222, + "username": "rico_yuen", + "userId": "S5LptVCLaCMz11AWbZhjgqJD1bk1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 450, + "totalSwipes": 298, + "username": "MalakB", + "userId": "SaSQM9g5lvh3SGXHxmLrZBYVhuA2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 8720, + "totalSwipes": 4592, + "username": "twood6", + "userId": "sfBcSvyIpXRRttvftwxrXwkqrw22", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 1972, + "totalSwipes": 673, + "username": "kerstin Luteijn", + "userId": "sfBRxfIugWgDAatAU6i39a0npSP2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 5612, + "totalSwipes": 3330, + "username": "jlong", + "userId": "SvoK1vssXCRxPO3DgbG9Fk1C16G2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 235, + "totalSwipes": 106, + "username": "hannahjhaslam", + "userId": "sWNuVEw2e7aKZW0Um51bzpzq1cD2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 2717, + "totalSwipes": 1295, + "username": "sarvagna", + "userId": "SWsHXNFPdDYknROrSEfU81yQXSj1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 2458, + "totalSwipes": 407, + "username": "ladypatrick", + "userId": "t7Bg020ct9NiitO3HOYUxuAIz9q2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 8, + "totalSwipeTime": 3896, + "totalSwipes": 767, + "username": "nchase", + "userId": "t9ro1FN82gTkAyfRMqcFLggWG5B2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 847, + "totalSwipes": 327, + "username": "aditi", + "userId": "tD71Y5BgxqONKoOiOphPqfVvT893", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 27, + "totalSwipes": 22, + "username": "Clevin", + "userId": "thPAcQ6Ez0UUDXNcBeI7TmlmWBv1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 41133, + "totalSwipes": 15927, + "username": "vnyc", + "userId": "TWrJa4vmdvRAW5fDtJ3YzjFViBi2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 27, + "totalSwipes": 22, + "username": "AnyaT", + "userId": "tZ7VXVwCj4MMrMJIwKuXiA5wP8j1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 103, + "totalSwipes": 42, + "username": "giaia.12", + "userId": "U9ZTuMgCpqbFZzHLpxn9eGNozwJ3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 2729, + "totalSwipes": 582, + "username": "hxbiscus", + "userId": "ub9xeBlXN6gozVV2ECQi0ClXwLF3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 8, + "totalSwipeTime": 8610, + "totalSwipes": 1910, + "username": "averymcreynolds1", + "userId": "UCbEXpe7oQVDdVSqkcHqcPw7vB93", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 1413, + "totalSwipes": 532, + "username": "lilly", + "userId": "uDtHTz3reeV7lhIHjAReWetsqFi1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 2327, + "totalSwipes": 444, + "username": "bergquistm10", + "userId": "UfPPaUGqoIeaYH5XuVR2VKoBCcK2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 6, + "totalSwipeTime": 2278, + "totalSwipes": 816, + "username": "pat_a", + "userId": "ujNA1G6HCFQrbj7T3owHY4waX7R2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 647, + "totalSwipes": 77, + "username": "kcrane29", + "userId": "UmtXMBT4rIfr8sGzg0icAcML7wt2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 73, + "totalSwipes": 37, + "username": "tamia", + "userId": "URGRsPnZE8WHbsvJ03m2IejqhB92", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "Jack", + "userId": "URKEiIRRwjck5fyhkla0cJoTao42", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 84, + "totalSwipes": 37, + "username": "kevindpatino", + "userId": "VAWoMvFzUSdrJqjQEpk7nRXcocL2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 719, + "totalSwipes": 104, + "username": "bcook", + "userId": "vbSZj8YWnjPb1lKXMCO2nkcbhzF3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 3952, + "totalSwipes": 691, + "username": "yash", + "userId": "vDooLtNIHZhtJu5XI90qBSR9yKp1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "LChiu14", + "userId": "VfA2C2e26bfm9ZFbd90byY37Yty1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 6, + "totalSwipeTime": 1943, + "totalSwipes": 471, + "username": "lizzymueller", + "userId": "vfaRCDHc8ScYibRWDMrIRsWeOKv1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 485, + "totalSwipes": 184, + "username": "chicken", + "userId": "VioR8A3qccTg8j3iOtbDotd64N13", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 17, + "totalSwipeTime": 11692, + "totalSwipes": 5276, + "username": "madelinesosa", + "userId": "VN5ufyAusDPskgb2zuAjIgrGbZe2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 136, + "totalSwipes": 37, + "username": "vashM17", + "userId": "VU9owscq8lPdSk48XfHYeXgJd5v1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 10, + "totalSwipeTime": 71164, + "totalSwipes": 16241, + "username": "allisonbooth", + "userId": "vUws3v2spThYkXTy5AupZ89QYBq1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 8, + "totalSwipeTime": 3664, + "totalSwipes": 1816, + "username": "rubybellin", + "userId": "w3wy48mTezQC2ZGg3iv4403QFG03", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 10, + "totalSwipeTime": 15434, + "totalSwipes": 5488, + "username": "maddi", + "userId": "W5G3QlDWtSh7wJKB5zx2gkAiKp43", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 4, + "totalSwipeTime": 22629, + "totalSwipes": 7565, + "username": "Anirudh Bhaskar ", + "userId": "W6jHlIpD1lbncTYGVkNQ2dNSnLR2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 377, + "totalSwipes": 162, + "username": "jparrino2", + "userId": "w87yDuUuZQWd3KIa5YH4sQv6ZCg2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 903, + "totalSwipes": 148, + "username": "KGiles", + "userId": "wCkYa2jqHoXHisAzasgAutlBJW42", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 1140, + "totalSwipes": 588, + "username": "Ananya Gokulakrishnan ", + "userId": "wd36liv3uvbNxEqqn9LwDcsY5OF3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 8, + "totalSwipeTime": 7686, + "totalSwipes": 2193, + "username": "Dulce", + "userId": "WJISWqxoqQVqqFNRh96h7Tj1kx03", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 14, + "totalSwipeTime": 10444, + "totalSwipes": 4123, + "username": "Joshua", + "userId": "WjJGpIo1dbRycL85DfgEzrC1GIx1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 2984, + "totalSwipes": 979, + "username": "ashleighkb1", + "userId": "WljutX8WXobSdLQyF47vgQX7C7N2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 1441, + "totalSwipes": 814, + "username": "mariajose_paredes", + "userId": "WqBnG6rYgjgIAANDrGXQON0XzXU2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 655, + "totalSwipes": 222, + "username": "kristian155", + "userId": "Ws96DLcxm1esEoI31ro35miZHru1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 192, + "totalSwipes": 56, + "username": "melin", + "userId": "wSK864BEb4QhG2ZnCk4wfiX2DzP2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 550, + "totalSwipes": 156, + "username": "Samara ", + "userId": "WsMV7uAly1PUQsjfIS8rN2Hrm1g1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 16, + "totalSwipes": 2, + "username": "ARiser90", + "userId": "WxRqWpZoV3UkerMSrnwnJSQy1Tk1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 473, + "totalSwipes": 185, + "username": "LouisDavidGH", + "userId": "x0LVOPt821cUe6YIJnp34aFOYYt2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 1459, + "totalSwipes": 407, + "username": "GabyM", + "userId": "XEjpAWkBIbdU6EtpLqKVt0NNVHI2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 468, + "totalSwipes": 57, + "username": "adidesai001", + "userId": "XpXTboEfF1SN5lu6gFuL2sKA9812", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 1096, + "totalSwipes": 465, + "username": "rohita", + "userId": "xt2VciINzqVYJsQcui7ySeEKJTD2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 5, + "totalSwipeTime": 2499, + "totalSwipes": 978, + "username": "Bella Gupta", + "userId": "xthkJfsVO6XoKTqVaJplDMUDd2j1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 844, + "totalSwipes": 206, + "username": "nurselynds", + "userId": "Y3t4CFNmxuO734dPSbwufg5gUtC2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1538, + "totalSwipes": 370, + "username": "alyssaclement", + "userId": "y8CVrhWml2dZC9WFMaYHrmRqxpv2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 3247, + "totalSwipes": 1110, + "username": "Rama", + "userId": "y9qEwjLW4YUtOvqYEBwD2abcyUk2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 2540, + "totalSwipes": 555, + "username": "enzopaesano", + "userId": "yfHPTMNbLRSjsS6lLOOwc3YiFlA3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 208, + "totalSwipes": 148, + "username": "Nur Harun", + "userId": "YI4azbugi1Qw5hGDplrU1Sh6JIH3", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 1123, + "totalSwipes": 256, + "username": "ogh854", + "userId": "YngZiAw9aSQFu5Tk8b0CGbWdDHN2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 3569, + "totalSwipes": 1243, + "username": "ghazala_safi", + "userId": "yqGqGfFc62UxSpeDDZhvRUmC5GZ2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 479, + "totalSwipes": 57, + "username": "DenverDonna", + "userId": "YVTMOfOldKXEV55u8bYYpHHheub2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "sluggirl729", + "userId": "z5CHvw22pCXXIjn4EHT7Q1oISF03", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 1334, + "totalSwipes": 2664, + "username": "Deborah Jackson ", + "userId": "zChmJkC5qjWTJ6RKzYI977fv3vX2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 7, + "totalSwipeTime": 4840, + "totalSwipes": 1138, + "username": "vebo", + "userId": "ZCXKTgbvvrVGmWFBiXpdIaMKbMs2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 3, + "totalSwipeTime": 1852, + "totalSwipes": 558, + "username": "juliamoss", + "userId": "ZlOwTghsAaXdpDqwWDrlTEXWTeg2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 371, + "totalSwipes": 120, + "username": "Owen_Linder", + "userId": "ZnUS1CRxGmRBBriZUPfJeKLVhi52", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 240, + "totalSwipes": 99, + "username": "lokelanim", + "userId": "ZRU4k1c2AJZvgpJ2DswOijSopdu1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 2, + "totalSwipeTime": 3716, + "totalSwipes": 3885, + "username": "klausullm", + "userId": "ZXbv6GVKx0bE5QVkymOjUmMcybh1", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 454, + "totalSwipes": 54, + "username": "RedCrossSophia", + "userId": "ZxgrRrgvS4atvs1tougXDmOAIKq2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "Bhavi3125", + "userId": "ZY9po8URjHa7iUa3irBDQzlfCOT2", + "__typename": "UserGroupUserStatsType" + }, + { + "totalMappingProjects": 1, + "totalSwipeTime": 226, + "totalSwipes": 37, + "username": "Leca", + "userId": "zyAUPW37LrXILyeSkCi2wkR2lYK2", + "__typename": "UserGroupUserStatsType" + } + ], + "contributionByGeo": [ + { + "geojson": { + "type": "Point", + "coordinates": [ + 92.73140175431497, + 23.499270969630363 + ] + }, + "totalContribution": 397, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -70.84958517762927, + -1.355191715444946 + ] + }, + "totalContribution": 416, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -85.1198964514573, + 13.980393189284156 + ] + }, + "totalContribution": 1446, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 92.92127382802134, + 24.286809287816997 + ] + }, + "totalContribution": 372, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -75.61141649329109, + 1.343481984982563 + ] + }, + "totalContribution": 160, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 143.61353859029757, + -3.952259450082971 + ] + }, + "totalContribution": 10533, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 37.85053622258006, + 1.091773186351989 + ] + }, + "totalContribution": 755, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 26.61876826407039, + 9.423529594125787 + ] + }, + "totalContribution": 460, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -90.2894587857745, + 19.59991189757706 + ] + }, + "totalContribution": 642, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -52.75324318273184, + 47.6591391076756 + ] + }, + "totalContribution": 53, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 38.66164175514844, + -15.399619311380487 + ] + }, + "totalContribution": 1236, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 37.55277057498438, + -15.428707695616959 + ] + }, + "totalContribution": 6929, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -72.31600045, + 18.68403465 + ] + }, + "totalContribution": 8000, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 171.20578760000004, + 7.1366634 + ] + }, + "totalContribution": 1610, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -72.64747093368038, + -1.361696978303415 + ] + }, + "totalContribution": 1919, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -78.8843802763227, + -4.010091566306465 + ] + }, + "totalContribution": 3, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -62.95436691259595, + -4.021184656245459 + ] + }, + "totalContribution": 462, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -89.14110606622654, + 15.970939244815773 + ] + }, + "totalContribution": 410, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 34.95859115, + 1.50696485 + ] + }, + "totalContribution": 37, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 68.7095207013628, + 36.26334834780437 + ] + }, + "totalContribution": 762, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -68.56794167938004, + -10.518198604197824 + ] + }, + "totalContribution": 46, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 144.59421611842083, + -5.797188668500342 + ] + }, + "totalContribution": 199, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -51.317165991121456, + -29.970770738273576 + ] + }, + "totalContribution": 25, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 82.21697136250522, + 29.271747744332938 + ] + }, + "totalContribution": 2457, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -83.55559415389867, + 14.739167328368252 + ] + }, + "totalContribution": 535, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -75.29435521590999, + 1.021080400450334 + ] + }, + "totalContribution": 32, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 89.34596785, + 23.7691698 + ] + }, + "totalContribution": 9990, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -84.77268253283326, + 11.546148902370424 + ] + }, + "totalContribution": 3432, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 32.097337697983995, + -19.672736743975427 + ] + }, + "totalContribution": 5632, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -78.55328208504923, + -5.007382183145697 + ] + }, + "totalContribution": 5493, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 69.49271542106688, + 35.96371220019021 + ] + }, + "totalContribution": 7, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -79.0948214265807, + -5.621316804838495 + ] + }, + "totalContribution": 567, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 143.1167797438696, + -4.656960795757978 + ] + }, + "totalContribution": 3432, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -78.0547666676697, + -2.825082939722951 + ] + }, + "totalContribution": 7499, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -79.06916177979166, + -3.034005965493468 + ] + }, + "totalContribution": 102, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -71.89545587098073, + 1.298110173544335 + ] + }, + "totalContribution": 3664, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -77.47483008962396, + -2.671800082849541 + ] + }, + "totalContribution": 1828, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -81.23746142111578, + 48.509127771876024 + ] + }, + "totalContribution": 300, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -72.50470642563677, + 2.549833320740754 + ] + }, + "totalContribution": 2863, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 34.32541349999999, + -14.3831562 + ] + }, + "totalContribution": 14400, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -67.77638302730661, + 2.318842316135067 + ] + }, + "totalContribution": 918, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -71.95473727023082, + 2.59894179622379 + ] + }, + "totalContribution": 4648, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -61.684001119372546, + 12.113960230297222 + ] + }, + "totalContribution": 1450, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -85.45517170072486, + 13.999002771878763 + ] + }, + "totalContribution": 1499, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 32.721361204259374, + -19.781151286275083 + ] + }, + "totalContribution": 652, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -83.95687139107329, + 14.409415061907099 + ] + }, + "totalContribution": 198, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 69.05683960392936, + 35.51029701534008 + ] + }, + "totalContribution": 206, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -88.29635175, + 18.5011917 + ] + }, + "totalContribution": 4440, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 37.77650094469254, + -15.869041578395171 + ] + }, + "totalContribution": 2509, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -70.39905739931731, + -3.075525011594448 + ] + }, + "totalContribution": 11, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 68.33781411579383, + 35.32722110463547 + ] + }, + "totalContribution": 1372, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -74.18843171205127, + 1.838209088567622 + ] + }, + "totalContribution": 2615, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 143.152434612217, + -5.5812882243965 + ] + }, + "totalContribution": 224, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -83.52420997372839, + 14.245248145874273 + ] + }, + "totalContribution": 2546, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -73.10396834864834, + 2.539712405290437 + ] + }, + "totalContribution": 4794, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -100.82986828280633, + 19.335069136307755 + ] + }, + "totalContribution": 4648, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -102.1680905738908, + 19.56164698137183 + ] + }, + "totalContribution": 743, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -85.90812230086513, + 13.486127873926645 + ] + }, + "totalContribution": 395, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -114.56995240824085, + 35.40558885777537 + ] + }, + "totalContribution": 4602, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -74.77129309897693, + -8.361615584031053 + ] + }, + "totalContribution": 7801, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 82.37118483115022, + 29.61829835532351 + ] + }, + "totalContribution": 964, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 92.78453223277066, + 22.739613534421945 + ] + }, + "totalContribution": 2755, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 31.693173872400514, + -19.26958190745124 + ] + }, + "totalContribution": 6820, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -70.61099417706933, + 1.280501916282569 + ] + }, + "totalContribution": 389, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 142.69498962645142, + -4.279295885667389 + ] + }, + "totalContribution": 1794, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -76.08583221934292, + 1.188235729693674 + ] + }, + "totalContribution": 976, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -72.66913820889279, + 1.188377298969117 + ] + }, + "totalContribution": 1619, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 105.18473205, + -3.9566782 + ] + }, + "totalContribution": 82927, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 92.5258853750819, + 23.232898358393314 + ] + }, + "totalContribution": 1929, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 143.58635692996836, + -4.368596348999677 + ] + }, + "totalContribution": 1747, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 80.72860198207461, + 6.417804846704624 + ] + }, + "totalContribution": 1477, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -61.68237405622985, + -3.810160941839017 + ] + }, + "totalContribution": 370, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 144.66991719031637, + -5.793820739253102 + ] + }, + "totalContribution": 262, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 101.70452494312204, + 4.962856375896156 + ] + }, + "totalContribution": 1396, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -71.17855963901002, + -0.791600935042186 + ] + }, + "totalContribution": 2441, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -76.71958133476728, + 0.565003176010218 + ] + }, + "totalContribution": 3774, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 105.16145765, + -3.9464773 + ] + }, + "totalContribution": 24281, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 144.6266440884106, + -5.548789486185929 + ] + }, + "totalContribution": 53, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 142.75920024697723, + -3.870628843450433 + ] + }, + "totalContribution": 2412, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 92.85326093882071, + 23.268100438516683 + ] + }, + "totalContribution": 1501, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -74.34180167241547, + 2.51128934143724 + ] + }, + "totalContribution": 3997, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 143.82751221044325, + -5.291182706201177 + ] + }, + "totalContribution": 380, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 30.64303568483182, + 9.324525428092427 + ] + }, + "totalContribution": 425, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 93.22530083821907, + 23.447192435994232 + ] + }, + "totalContribution": 1360, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -75.61529423316709, + 1.019424583700923 + ] + }, + "totalContribution": 700, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 102.08069452181265, + 5.67114619510633 + ] + }, + "totalContribution": 182, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 32.755421668802896, + -17.93530847459356 + ] + }, + "totalContribution": 1751, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 144.54650766979148, + -6.040632954067647 + ] + }, + "totalContribution": 2544, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -62.95436691259595, + -4.021184656245459 + ] + }, + "totalContribution": 158, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -73.45838763797263, + 3.212288633510766 + ] + }, + "totalContribution": 309, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 32.352349559096695, + -20.849241579120466 + ] + }, + "totalContribution": 56, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 93.1666453156887, + 23.6544015985086 + ] + }, + "totalContribution": 95, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -61.31402437345745, + 12.814700648940763 + ] + }, + "totalContribution": 387, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 30.627553545784952, + 8.588993868708355 + ] + }, + "totalContribution": 60, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -62.33763406010854, + -3.717180796936141 + ] + }, + "totalContribution": 273, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -88.99009388228792, + 16.967918154079996 + ] + }, + "totalContribution": 171, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -79.13114579286535, + -5.11558990014555 + ] + }, + "totalContribution": 3564, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -85.10877408131385, + 14.361584805486794 + ] + }, + "totalContribution": 6285, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -77.34633979411231, + -3.761121621878996 + ] + }, + "totalContribution": 3005, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -61.68237405622985, + -3.810160941839017 + ] + }, + "totalContribution": 555, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 92.92129956718755, + 23.45036122659758 + ] + }, + "totalContribution": 4378, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 35.03770801825091, + -15.771260184591872 + ] + }, + "totalContribution": 2688, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -85.23541754178115, + 12.251953989338162 + ] + }, + "totalContribution": 2596, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 105.18227995, + -3.9473116 + ] + }, + "totalContribution": 30856, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -72.52089048670294, + 1.926544828731387 + ] + }, + "totalContribution": 6468, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 120.38377675, + 16.83994845 + ] + }, + "totalContribution": 4207, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -78.2610077889491, + -3.214116548452895 + ] + }, + "totalContribution": 922, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 22.003747077124736, + 13.426280745812956 + ] + }, + "totalContribution": 107, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 144.59421611842083, + -5.797188668500342 + ] + }, + "totalContribution": 1101, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -74.820469509669, + 1.201661027388751 + ] + }, + "totalContribution": 2408, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 90.00115255, + 23.60059035 + ] + }, + "totalContribution": 59250, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -68.07932565216437, + 2.618340494921993 + ] + }, + "totalContribution": 1451, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 93.01364059226972, + 23.973280563762692 + ] + }, + "totalContribution": 325, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -68.41429488863824, + -10.658810636419465 + ] + }, + "totalContribution": 1252, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 32.562545708705656, + -20.24707721355555 + ] + }, + "totalContribution": 2681, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -85.72679359927517, + 13.364263828813872 + ] + }, + "totalContribution": 338, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -114.54959912741137, + 34.77524490124089 + ] + }, + "totalContribution": 1503, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 92.95309353117739, + 22.9696207456173 + ] + }, + "totalContribution": 1553, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 92.86176603973098, + 23.736902703039004 + ] + }, + "totalContribution": 965, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -62.95436691259595, + -4.021184656245459 + ] + }, + "totalContribution": 135, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 123.09181583505686, + 10.44638206428209 + ] + }, + "totalContribution": 1414, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -74.94386755939892, + -9.567978301900402 + ] + }, + "totalContribution": 2248, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -75.29126082683655, + 1.342510326407644 + ] + }, + "totalContribution": 115, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 0.676875419618685, + 7.209097585678581 + ] + }, + "totalContribution": 365, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 93.23453293584782, + 23.208784155687574 + ] + }, + "totalContribution": 687, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -78.541114166535, + -3.474992601520388 + ] + }, + "totalContribution": 195, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 93.06846162489438, + 23.14066680769323 + ] + }, + "totalContribution": 154, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -89.06064147641405, + 16.205240264487035 + ] + }, + "totalContribution": 518, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -74.81170044558343, + -8.920617444439028 + ] + }, + "totalContribution": 1165, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 68.54999621353622, + 35.783279392246335 + ] + }, + "totalContribution": 1783, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 4.477416918046016, + 7.729973758854705 + ] + }, + "totalContribution": 80, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -100.8300346140192, + 19.33592913127203 + ] + }, + "totalContribution": 841, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -73.38392646574127, + 2.966663019116976 + ] + }, + "totalContribution": 1831, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 39.138121600783734, + -15.124347352480978 + ] + }, + "totalContribution": 3706, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -78.42156759295283, + -0.443712017669453 + ] + }, + "totalContribution": 38, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 143.70645523271023, + -5.718813652819843 + ] + }, + "totalContribution": 1482, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 30.605391623974732, + 8.968571271460986 + ] + }, + "totalContribution": 268, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -74.81467757761713, + 1.833465408502024 + ] + }, + "totalContribution": 1139, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -61.188799541680694, + 13.250572531442755 + ] + }, + "totalContribution": 836, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -73.66162470802598, + 3.329978677963408 + ] + }, + "totalContribution": 948, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -77.94109306086266, + -2.183614308686258 + ] + }, + "totalContribution": 3044, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + -73.701759296725, + 2.967536670595839 + ] + }, + "totalContribution": 1983, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 143.17302026267424, + -3.554419496601139 + ] + }, + "totalContribution": 2867, + "__typename": "MapContributionStatsType" + }, + { + "geojson": { + "type": "Point", + "coordinates": [ + 92.6016404005226, + 22.98291123896723 + ] + }, + "totalContribution": 2757, + "__typename": "MapContributionStatsType" + } + ], + "areaSwipedByProjectType": [ + { + "totalArea": 42444.46410970643, + "projectTypeDisplay": "Find", + "projectType": "BUILD_AREA", + "__typename": "ProjectTypeAreaStatsType" + }, + { + "totalArea": 13.23154020152575, + "projectTypeDisplay": "Compare", + "projectType": "CHANGE_DETECTION", + "__typename": "ProjectTypeAreaStatsType" + }, + { + "totalArea": 0.0, + "projectTypeDisplay": "Validate", + "projectType": "FOOTPRINT", + "__typename": "ProjectTypeAreaStatsType" + } + ], + "swipeByDate": [ + { + "taskDate": "2024-01-01", + "totalSwipes": 150, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-02", + "totalSwipes": 53, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-03", + "totalSwipes": 177, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-04", + "totalSwipes": 490, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-05", + "totalSwipes": 217, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-06", + "totalSwipes": 117, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-08", + "totalSwipes": 535, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-09", + "totalSwipes": 712, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-10", + "totalSwipes": 437, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-11", + "totalSwipes": 1874, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-12", + "totalSwipes": 89, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-13", + "totalSwipes": 1702, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-14", + "totalSwipes": 2413, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-15", + "totalSwipes": 1317, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-16", + "totalSwipes": 1051, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-17", + "totalSwipes": 4032, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-18", + "totalSwipes": 991, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-19", + "totalSwipes": 1331, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-20", + "totalSwipes": 1845, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-21", + "totalSwipes": 240, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-22", + "totalSwipes": 567, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-23", + "totalSwipes": 236, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-24", + "totalSwipes": 691, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-25", + "totalSwipes": 392, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-26", + "totalSwipes": 912, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-27", + "totalSwipes": 1279, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-28", + "totalSwipes": 1324, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-29", + "totalSwipes": 683, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-30", + "totalSwipes": 917, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-01-31", + "totalSwipes": 596, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-01", + "totalSwipes": 833, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-02", + "totalSwipes": 1202, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-03", + "totalSwipes": 267, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-04", + "totalSwipes": 2934, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-05", + "totalSwipes": 1619, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-06", + "totalSwipes": 164, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-07", + "totalSwipes": 104, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-08", + "totalSwipes": 4893, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-09", + "totalSwipes": 740, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-10", + "totalSwipes": 3039, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-11", + "totalSwipes": 851, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-12", + "totalSwipes": 333, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-13", + "totalSwipes": 791, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-14", + "totalSwipes": 1381, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-15", + "totalSwipes": 355, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-16", + "totalSwipes": 1644, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-17", + "totalSwipes": 6664, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-18", + "totalSwipes": 673, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-19", + "totalSwipes": 538, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-20", + "totalSwipes": 888, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-21", + "totalSwipes": 1471, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-22", + "totalSwipes": 3016, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-23", + "totalSwipes": 148, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-24", + "totalSwipes": 795, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-25", + "totalSwipes": 2476, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-26", + "totalSwipes": 2247, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-27", + "totalSwipes": 839, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-28", + "totalSwipes": 1357, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-02-29", + "totalSwipes": 2109, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-01", + "totalSwipes": 1660, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-02", + "totalSwipes": 100, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-03", + "totalSwipes": 268, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-04", + "totalSwipes": 1131, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-05", + "totalSwipes": 1095, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-06", + "totalSwipes": 164, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-07", + "totalSwipes": 1784, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-08", + "totalSwipes": 962, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-09", + "totalSwipes": 96, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-11", + "totalSwipes": 824, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-12", + "totalSwipes": 464, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-13", + "totalSwipes": 999, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-14", + "totalSwipes": 378, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-15", + "totalSwipes": 823, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-16", + "totalSwipes": 1409, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-17", + "totalSwipes": 1224, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-18", + "totalSwipes": 543, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-19", + "totalSwipes": 443, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-20", + "totalSwipes": 2266, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-21", + "totalSwipes": 1162, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-22", + "totalSwipes": 968, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-23", + "totalSwipes": 581, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-24", + "totalSwipes": 259, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-25", + "totalSwipes": 581, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-26", + "totalSwipes": 1299, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-27", + "totalSwipes": 1363, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-28", + "totalSwipes": 1517, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-29", + "totalSwipes": 1653, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-30", + "totalSwipes": 826, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-03-31", + "totalSwipes": 2431, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-01", + "totalSwipes": 2379, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-02", + "totalSwipes": 3936, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-03", + "totalSwipes": 1483, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-05", + "totalSwipes": 1103, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-06", + "totalSwipes": 1049, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-07", + "totalSwipes": 390, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-08", + "totalSwipes": 600, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-09", + "totalSwipes": 244, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-10", + "totalSwipes": 630, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-11", + "totalSwipes": 270, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-12", + "totalSwipes": 129, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-13", + "totalSwipes": 1284, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-14", + "totalSwipes": 81, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-15", + "totalSwipes": 2248, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-16", + "totalSwipes": 2263, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-17", + "totalSwipes": 2646, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-18", + "totalSwipes": 11581, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-19", + "totalSwipes": 4650, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-20", + "totalSwipes": 1445, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-21", + "totalSwipes": 3370, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-22", + "totalSwipes": 977, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-23", + "totalSwipes": 1698, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-24", + "totalSwipes": 733, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-25", + "totalSwipes": 3778, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-26", + "totalSwipes": 1484, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-27", + "totalSwipes": 992, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-28", + "totalSwipes": 495, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-29", + "totalSwipes": 1134, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-04-30", + "totalSwipes": 988, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-01", + "totalSwipes": 927, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-02", + "totalSwipes": 1212, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-03", + "totalSwipes": 1004, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-04", + "totalSwipes": 833, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-05", + "totalSwipes": 452, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-06", + "totalSwipes": 329, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-07", + "totalSwipes": 1039, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-08", + "totalSwipes": 889, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-09", + "totalSwipes": 1731, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-10", + "totalSwipes": 1150, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-11", + "totalSwipes": 1077, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-12", + "totalSwipes": 1047, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-13", + "totalSwipes": 1431, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-14", + "totalSwipes": 2849, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-15", + "totalSwipes": 148, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-16", + "totalSwipes": 1751, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-17", + "totalSwipes": 129, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-18", + "totalSwipes": 123, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-19", + "totalSwipes": 203, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-20", + "totalSwipes": 295, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-21", + "totalSwipes": 439, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-22", + "totalSwipes": 339, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-23", + "totalSwipes": 1345, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-24", + "totalSwipes": 327, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-25", + "totalSwipes": 604, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-26", + "totalSwipes": 421, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-27", + "totalSwipes": 3735, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-28", + "totalSwipes": 251, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-29", + "totalSwipes": 1286, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-30", + "totalSwipes": 1342, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-05-31", + "totalSwipes": 1194, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-01", + "totalSwipes": 320, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-02", + "totalSwipes": 1384, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-03", + "totalSwipes": 1429, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-04", + "totalSwipes": 835, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-05", + "totalSwipes": 1323, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-06", + "totalSwipes": 1337, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-07", + "totalSwipes": 2549, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-08", + "totalSwipes": 1520, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-09", + "totalSwipes": 502, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-10", + "totalSwipes": 2661, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-11", + "totalSwipes": 4624, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-12", + "totalSwipes": 4448, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-13", + "totalSwipes": 2256, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-14", + "totalSwipes": 4423, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-15", + "totalSwipes": 1487, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-16", + "totalSwipes": 2861, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-17", + "totalSwipes": 2434, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-18", + "totalSwipes": 4536, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-19", + "totalSwipes": 546, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-20", + "totalSwipes": 8978, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-21", + "totalSwipes": 8285, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-22", + "totalSwipes": 3629, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-23", + "totalSwipes": 3291, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-24", + "totalSwipes": 6122, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-25", + "totalSwipes": 2843, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-26", + "totalSwipes": 2070, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-27", + "totalSwipes": 3498, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-28", + "totalSwipes": 5176, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-29", + "totalSwipes": 4510, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-06-30", + "totalSwipes": 7587, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-01", + "totalSwipes": 3460, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-02", + "totalSwipes": 2952, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-03", + "totalSwipes": 2155, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-04", + "totalSwipes": 3203, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-05", + "totalSwipes": 6370, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-06", + "totalSwipes": 5166, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-07", + "totalSwipes": 2195, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-08", + "totalSwipes": 2534, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-09", + "totalSwipes": 2240, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-10", + "totalSwipes": 3782, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-11", + "totalSwipes": 3770, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-12", + "totalSwipes": 7828, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-13", + "totalSwipes": 2914, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-14", + "totalSwipes": 3095, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-15", + "totalSwipes": 930, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-16", + "totalSwipes": 4203, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-17", + "totalSwipes": 2179, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-18", + "totalSwipes": 1572, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-19", + "totalSwipes": 4367, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-20", + "totalSwipes": 5931, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-21", + "totalSwipes": 6307, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-22", + "totalSwipes": 790, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-23", + "totalSwipes": 1600, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-24", + "totalSwipes": 4985, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-25", + "totalSwipes": 1678, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-26", + "totalSwipes": 2750, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-27", + "totalSwipes": 3452, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-28", + "totalSwipes": 1035, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-29", + "totalSwipes": 1327, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-30", + "totalSwipes": 1606, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-07-31", + "totalSwipes": 963, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-01", + "totalSwipes": 2370, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-02", + "totalSwipes": 1724, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-03", + "totalSwipes": 3060, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-04", + "totalSwipes": 344, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-05", + "totalSwipes": 3560, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-06", + "totalSwipes": 4064, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-07", + "totalSwipes": 3505, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-08", + "totalSwipes": 3457, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-09", + "totalSwipes": 3198, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-10", + "totalSwipes": 1355, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-11", + "totalSwipes": 858, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-12", + "totalSwipes": 2877, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-13", + "totalSwipes": 1722, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-14", + "totalSwipes": 333, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-15", + "totalSwipes": 1217, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-16", + "totalSwipes": 3517, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-17", + "totalSwipes": 2471, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-18", + "totalSwipes": 3448, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-19", + "totalSwipes": 2057, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-20", + "totalSwipes": 767, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-21", + "totalSwipes": 1107, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-22", + "totalSwipes": 2119, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-23", + "totalSwipes": 1027, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-24", + "totalSwipes": 1959, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-25", + "totalSwipes": 2311, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-26", + "totalSwipes": 3513, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-27", + "totalSwipes": 3308, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-28", + "totalSwipes": 898, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-29", + "totalSwipes": 740, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-30", + "totalSwipes": 868, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-08-31", + "totalSwipes": 1317, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-01", + "totalSwipes": 1118, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-02", + "totalSwipes": 2675, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-03", + "totalSwipes": 1854, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-04", + "totalSwipes": 1241, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-05", + "totalSwipes": 2058, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-06", + "totalSwipes": 4152, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-07", + "totalSwipes": 4136, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-08", + "totalSwipes": 2227, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-09", + "totalSwipes": 993, + "__typename": "ContributorSwipeStatType" + }, + { + "taskDate": "2024-09-10", + "totalSwipes": 1864, + "__typename": "ContributorSwipeStatType" + } + ], + "swipeTimeByDate": [ + { + "date": "2024-01-01", + "totalSwipeTime": 175, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-02", + "totalSwipeTime": 180, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-03", + "totalSwipeTime": 603, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-04", + "totalSwipeTime": 1230, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-05", + "totalSwipeTime": 953, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-06", + "totalSwipeTime": 273, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-08", + "totalSwipeTime": 797, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-09", + "totalSwipeTime": 1731, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-10", + "totalSwipeTime": 1305, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-11", + "totalSwipeTime": 5174, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-12", + "totalSwipeTime": 545, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-13", + "totalSwipeTime": 4021, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-14", + "totalSwipeTime": 4618, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-15", + "totalSwipeTime": 1908, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-16", + "totalSwipeTime": 2604, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-17", + "totalSwipeTime": 11725, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-18", + "totalSwipeTime": 1719, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-19", + "totalSwipeTime": 5267, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-20", + "totalSwipeTime": 3517, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-21", + "totalSwipeTime": 480, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-22", + "totalSwipeTime": 2051, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-23", + "totalSwipeTime": 422, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-24", + "totalSwipeTime": 1964, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-25", + "totalSwipeTime": 1847, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-26", + "totalSwipeTime": 2799, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-27", + "totalSwipeTime": 3395, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-28", + "totalSwipeTime": 3548, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-29", + "totalSwipeTime": 1653, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-30", + "totalSwipeTime": 4544, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-01-31", + "totalSwipeTime": 1741, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-01", + "totalSwipeTime": 2979, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-02", + "totalSwipeTime": 4675, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-03", + "totalSwipeTime": 568, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-04", + "totalSwipeTime": 1838, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-05", + "totalSwipeTime": 3298, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-06", + "totalSwipeTime": 146, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-07", + "totalSwipeTime": 374, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-08", + "totalSwipeTime": 9621, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-09", + "totalSwipeTime": 218, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-10", + "totalSwipeTime": 1475, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-11", + "totalSwipeTime": 263, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-12", + "totalSwipeTime": 349, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-13", + "totalSwipeTime": 678, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-14", + "totalSwipeTime": 2285, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-15", + "totalSwipeTime": 1899, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-16", + "totalSwipeTime": 3960, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-17", + "totalSwipeTime": 3420, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-18", + "totalSwipeTime": 315, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-19", + "totalSwipeTime": 1535, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-20", + "totalSwipeTime": 1605, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-21", + "totalSwipeTime": 2864, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-22", + "totalSwipeTime": 2317, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-23", + "totalSwipeTime": 488, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-24", + "totalSwipeTime": 1037, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-25", + "totalSwipeTime": 5768, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-26", + "totalSwipeTime": 5507, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-27", + "totalSwipeTime": 2143, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-28", + "totalSwipeTime": 2008, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-02-29", + "totalSwipeTime": 8980, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-01", + "totalSwipeTime": 7311, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-02", + "totalSwipeTime": 131, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-03", + "totalSwipeTime": 813, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-04", + "totalSwipeTime": 1621, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-05", + "totalSwipeTime": 2564, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-06", + "totalSwipeTime": 378, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-07", + "totalSwipeTime": 4012, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-08", + "totalSwipeTime": 655, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-09", + "totalSwipeTime": 254, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-11", + "totalSwipeTime": 1196, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-12", + "totalSwipeTime": 589, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-13", + "totalSwipeTime": 1229, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-14", + "totalSwipeTime": 1867, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-15", + "totalSwipeTime": 3201, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-16", + "totalSwipeTime": 4574, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-17", + "totalSwipeTime": 3264, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-18", + "totalSwipeTime": 1980, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-19", + "totalSwipeTime": 3248, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-20", + "totalSwipeTime": 6519, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-21", + "totalSwipeTime": 2882, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-22", + "totalSwipeTime": 4142, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-23", + "totalSwipeTime": 2329, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-24", + "totalSwipeTime": 548, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-25", + "totalSwipeTime": 1931, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-26", + "totalSwipeTime": 3759, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-27", + "totalSwipeTime": 7180, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-28", + "totalSwipeTime": 4524, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-29", + "totalSwipeTime": 6138, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-30", + "totalSwipeTime": 4782, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-03-31", + "totalSwipeTime": 10235, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-01", + "totalSwipeTime": 6856, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-02", + "totalSwipeTime": 13181, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-03", + "totalSwipeTime": 8477, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-05", + "totalSwipeTime": 3117, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-06", + "totalSwipeTime": 5354, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-07", + "totalSwipeTime": 2685, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-08", + "totalSwipeTime": 2257, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-09", + "totalSwipeTime": 1233, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-10", + "totalSwipeTime": 3565, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-11", + "totalSwipeTime": 1409, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-12", + "totalSwipeTime": 395, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-13", + "totalSwipeTime": 4462, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-14", + "totalSwipeTime": 141, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-15", + "totalSwipeTime": 8057, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-16", + "totalSwipeTime": 6532, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-17", + "totalSwipeTime": 8466, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-18", + "totalSwipeTime": 34980, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-19", + "totalSwipeTime": 13203, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-20", + "totalSwipeTime": 2570, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-21", + "totalSwipeTime": 6570, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-22", + "totalSwipeTime": 2479, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-23", + "totalSwipeTime": 2905, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-24", + "totalSwipeTime": 1829, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-25", + "totalSwipeTime": 7019, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-26", + "totalSwipeTime": 4947, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-27", + "totalSwipeTime": 1968, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-28", + "totalSwipeTime": 1577, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-29", + "totalSwipeTime": 3244, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-04-30", + "totalSwipeTime": 2058, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-01", + "totalSwipeTime": 1885, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-02", + "totalSwipeTime": 3917, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-03", + "totalSwipeTime": 3221, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-04", + "totalSwipeTime": 2782, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-05", + "totalSwipeTime": 2058, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-06", + "totalSwipeTime": 1173, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-07", + "totalSwipeTime": 3351, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-08", + "totalSwipeTime": 1455, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-09", + "totalSwipeTime": 6819, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-10", + "totalSwipeTime": 3070, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-11", + "totalSwipeTime": 2573, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-12", + "totalSwipeTime": 1431, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-13", + "totalSwipeTime": 4996, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-14", + "totalSwipeTime": 7824, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-15", + "totalSwipeTime": 178, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-16", + "totalSwipeTime": 5207, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-17", + "totalSwipeTime": 258, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-18", + "totalSwipeTime": 543, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-19", + "totalSwipeTime": 863, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-20", + "totalSwipeTime": 614, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-21", + "totalSwipeTime": 1583, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-22", + "totalSwipeTime": 1442, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-23", + "totalSwipeTime": 5442, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-24", + "totalSwipeTime": 901, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-25", + "totalSwipeTime": 2669, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-26", + "totalSwipeTime": 1480, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-27", + "totalSwipeTime": 9517, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-28", + "totalSwipeTime": 1034, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-29", + "totalSwipeTime": 3909, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-30", + "totalSwipeTime": 4091, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-05-31", + "totalSwipeTime": 2089, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-01", + "totalSwipeTime": 1184, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-02", + "totalSwipeTime": 3877, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-03", + "totalSwipeTime": 4290, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-04", + "totalSwipeTime": 3118, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-05", + "totalSwipeTime": 4371, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-06", + "totalSwipeTime": 1998, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-07", + "totalSwipeTime": 9040, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-08", + "totalSwipeTime": 4567, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-09", + "totalSwipeTime": 1716, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-10", + "totalSwipeTime": 12063, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-11", + "totalSwipeTime": 19046, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-12", + "totalSwipeTime": 10074, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-13", + "totalSwipeTime": 8658, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-14", + "totalSwipeTime": 14809, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-15", + "totalSwipeTime": 3727, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-16", + "totalSwipeTime": 8686, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-17", + "totalSwipeTime": 6617, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-18", + "totalSwipeTime": 11928, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-19", + "totalSwipeTime": 2108, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-20", + "totalSwipeTime": 30040, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-21", + "totalSwipeTime": 21793, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-22", + "totalSwipeTime": 8964, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-23", + "totalSwipeTime": 9414, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-24", + "totalSwipeTime": 20416, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-25", + "totalSwipeTime": 10238, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-26", + "totalSwipeTime": 8446, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-27", + "totalSwipeTime": 10738, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-28", + "totalSwipeTime": 12460, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-29", + "totalSwipeTime": 15712, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-06-30", + "totalSwipeTime": 18091, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-01", + "totalSwipeTime": 15220, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-02", + "totalSwipeTime": 10968, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-03", + "totalSwipeTime": 5917, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-04", + "totalSwipeTime": 9310, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-05", + "totalSwipeTime": 17967, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-06", + "totalSwipeTime": 14844, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-07", + "totalSwipeTime": 5976, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-08", + "totalSwipeTime": 9517, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-09", + "totalSwipeTime": 5972, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-10", + "totalSwipeTime": 14638, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-11", + "totalSwipeTime": 13358, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-12", + "totalSwipeTime": 20049, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-13", + "totalSwipeTime": 10459, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-14", + "totalSwipeTime": 11672, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-15", + "totalSwipeTime": 5405, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-16", + "totalSwipeTime": 7126, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-17", + "totalSwipeTime": 10973, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-18", + "totalSwipeTime": 7712, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-19", + "totalSwipeTime": 18500, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-20", + "totalSwipeTime": 16097, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-21", + "totalSwipeTime": 24523, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-22", + "totalSwipeTime": 2400, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-23", + "totalSwipeTime": 2593, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-24", + "totalSwipeTime": 13028, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-25", + "totalSwipeTime": 5378, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-26", + "totalSwipeTime": 6092, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-27", + "totalSwipeTime": 17566, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-28", + "totalSwipeTime": 6585, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-29", + "totalSwipeTime": 2190, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-30", + "totalSwipeTime": 5133, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-07-31", + "totalSwipeTime": 4210, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-01", + "totalSwipeTime": 7426, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-02", + "totalSwipeTime": 8271, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-03", + "totalSwipeTime": 12001, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-04", + "totalSwipeTime": 1396, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-05", + "totalSwipeTime": 12396, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-06", + "totalSwipeTime": 14951, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-07", + "totalSwipeTime": 14278, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-08", + "totalSwipeTime": 13270, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-09", + "totalSwipeTime": 11120, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-10", + "totalSwipeTime": 6014, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-11", + "totalSwipeTime": 2031, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-12", + "totalSwipeTime": 9368, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-13", + "totalSwipeTime": 5273, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-14", + "totalSwipeTime": 1859, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-15", + "totalSwipeTime": 3563, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-16", + "totalSwipeTime": 9241, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-17", + "totalSwipeTime": 6071, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-18", + "totalSwipeTime": 14260, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-19", + "totalSwipeTime": 6553, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-20", + "totalSwipeTime": 3339, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-21", + "totalSwipeTime": 3789, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-22", + "totalSwipeTime": 6823, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-23", + "totalSwipeTime": 3343, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-24", + "totalSwipeTime": 7411, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-25", + "totalSwipeTime": 6651, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-26", + "totalSwipeTime": 8180, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-27", + "totalSwipeTime": 5361, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-28", + "totalSwipeTime": 2144, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-29", + "totalSwipeTime": 2146, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-30", + "totalSwipeTime": 2178, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-08-31", + "totalSwipeTime": 5897, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-01", + "totalSwipeTime": 4184, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-02", + "totalSwipeTime": 7407, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-03", + "totalSwipeTime": 5471, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-04", + "totalSwipeTime": 2321, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-05", + "totalSwipeTime": 4905, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-06", + "totalSwipeTime": 8804, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-07", + "totalSwipeTime": 8469, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-08", + "totalSwipeTime": 9397, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-09", + "totalSwipeTime": 2233, + "__typename": "ContributorTimeStatType" + }, + { + "date": "2024-09-10", + "totalSwipeTime": 4204, + "__typename": "ContributorTimeStatType" + } + ], + "swipeByProjectType": [ + { + "projectType": "BUILD_AREA", + "projectTypeDisplay": "Find", + "totalSwipes": 226528, + "__typename": "ProjectTypeSwipeStatsType" + }, + { + "projectType": "CHANGE_DETECTION", + "projectTypeDisplay": "Compare", + "totalSwipes": 642, + "__typename": "ProjectTypeSwipeStatsType" + }, + { + "projectType": "FOOTPRINT", + "projectTypeDisplay": "Validate", + "totalSwipes": 239998, + "__typename": "ProjectTypeSwipeStatsType" + } + ], + "swipeByOrganizationName": [ + { + "organizationName": "Malawi Liverpool Wellcome", + "totalSwipes": 2688, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "American Red Cross", + "totalSwipes": 235791, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "Arizona State University", + "totalSwipes": 6105, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "OpenStreetMap Togo", + "totalSwipes": 365, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "Open Mapping Hub - AP", + "totalSwipes": 10066, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "Canadian Red Cross", + "totalSwipes": 353, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "OpenMappingHub - LAC", + "totalSwipes": 21199, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "OSM Papua New Guinea", + "totalSwipes": 22004, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "M\u00e9decins Sans Fronti\u00e8res", + "totalSwipes": 24707, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "OSM Afghanistan", + "totalSwipes": 4130, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "Ladies In Maps Zimbabwe ", + "totalSwipes": 17592, + "__typename": "OrganizationSwipeStatsType" + }, + { + "organizationName": "HOT", + "totalSwipes": 122168, + "__typename": "OrganizationSwipeStatsType" + } + ], + "__typename": "UserGroupFilteredStats" + }, + "__typename": "UserGroupStats" + } + } +} \ No newline at end of file diff --git a/backend/general_mapswipe_stats.json b/backend/general_mapswipe_stats.json new file mode 100644 index 0000000000..a950ee14fc --- /dev/null +++ b/backend/general_mapswipe_stats.json @@ -0,0 +1,133 @@ +{ + "data": { + "userGroup": { + "id": "-NL6WXPOdFyWACqwNU2O", + "userGroupId": "-NL6WXPOdFyWACqwNU2O", + "name": "American Red Cross", + "description": "", + "userMemberships": { + "count": 531, + "items": [ + { + "id": "05AfE1oB5vY6EbPqEjkdR9o0s4D3--NL6WXPOdFyWACqwNU2O", + "userId": "05AfE1oB5vY6EbPqEjkdR9o0s4D3", + "username": "jaiC", + "isActive": true, + "totalMappingProjects": 3, + "totalSwipeTime": 6288, + "totalSwipes": 1380, + "__typename": "UserGroupUserMembershipType" + }, + { + "id": "08zR2YnWNXh8b5sFd2xEVa2c2t53--NL6WXPOdFyWACqwNU2O", + "userId": "08zR2YnWNXh8b5sFd2xEVa2c2t53", + "username": "jainMedha", + "isActive": true, + "totalMappingProjects": 3, + "totalSwipeTime": 1580, + "totalSwipes": 518, + "__typename": "UserGroupUserMembershipType" + }, + { + "id": "09G9UqZq9yODe5XRl1ekhchba3x1--NL6WXPOdFyWACqwNU2O", + "userId": "09G9UqZq9yODe5XRl1ekhchba3x1", + "username": "Micah", + "isActive": true, + "totalMappingProjects": 18, + "totalSwipeTime": 9469, + "totalSwipes": 3455, + "__typename": "UserGroupUserMembershipType" + }, + { + "id": "0f1KIx0VmmRcqxdwOp2jnlZyNWx2--NL6WXPOdFyWACqwNU2O", + "userId": "0f1KIx0VmmRcqxdwOp2jnlZyNWx2", + "username": "divvs2007", + "isActive": true, + "totalMappingProjects": 1, + "totalSwipeTime": 381, + "totalSwipes": 111, + "__typename": "UserGroupUserMembershipType" + }, + { + "id": "0fXvDI22YrUuiCOGbQPbln5Bbru2--NL6WXPOdFyWACqwNU2O", + "userId": "0fXvDI22YrUuiCOGbQPbln5Bbru2", + "username": "tannu365", + "isActive": true, + "totalMappingProjects": 1, + "totalSwipeTime": 1611, + "totalSwipes": 407, + "__typename": "UserGroupUserMembershipType" + }, + { + "id": "0k4JHPdKrFNJhRgn7BOZX14MU2v1--NL6WXPOdFyWACqwNU2O", + "userId": "0k4JHPdKrFNJhRgn7BOZX14MU2v1", + "username": "arfah234", + "isActive": true, + "totalMappingProjects": 2, + "totalSwipeTime": 1459, + "totalSwipes": 561, + "__typename": "UserGroupUserMembershipType" + }, + { + "id": "0kSwoyz6jgXhicD9r0FDsmExtk32--NL6WXPOdFyWACqwNU2O", + "userId": "0kSwoyz6jgXhicD9r0FDsmExtk32", + "username": "Gabrielle Sarsok", + "isActive": true, + "totalMappingProjects": 0, + "totalSwipeTime": 0, + "totalSwipes": 0, + "__typename": "UserGroupUserMembershipType" + }, + { + "id": "0N1lhg0NJAN6lvAfEBkhzXnXgv53--NL6WXPOdFyWACqwNU2O", + "userId": "0N1lhg0NJAN6lvAfEBkhzXnXgv53", + "username": "ashleycruzc", + "isActive": true, + "totalMappingProjects": 2, + "totalSwipeTime": 3273, + "totalSwipes": 1406, + "__typename": "UserGroupUserMembershipType" + }, + { + "id": "0QCwiA4kCzac6rlsedwmu5vPJb53--NL6WXPOdFyWACqwNU2O", + "userId": "0QCwiA4kCzac6rlsedwmu5vPJb53", + "username": "tanvi", + "isActive": true, + "totalMappingProjects": 7, + "totalSwipeTime": 28628, + "totalSwipes": 7103, + "__typename": "UserGroupUserMembershipType" + }, + { + "id": "0QQCPKsz2Bh3HtmseOBqSFN6qiq2--NL6WXPOdFyWACqwNU2O", + "userId": "0QQCPKsz2Bh3HtmseOBqSFN6qiq2", + "username": "janyapatel", + "isActive": true, + "totalMappingProjects": 5, + "totalSwipeTime": 1280, + "totalSwipes": 836, + "__typename": "UserGroupUserMembershipType" + } + ], + "__typename": "UserGroupUserMembershipTypeCountList" + }, + "__typename": "UserGroupType" + }, + "userGroupStats": { + "id": "-NL6WXPOdFyWACqwNU2O", + "stats": { + "totalContributors": 432, + "totalSwipes": 1095815, + "totalSwipeTime": 2902427, + "__typename": "UserGroupStatsType" + }, + "statsLatest": { + "totalContributors": 56, + "totalSwipeTime": 178588, + "totalSwipes": 65212, + "__typename": "UserGroupLatestStatsType" + }, + "__typename": "UserGroupStats" + } + } +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 4834b88423..db755b6012 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,6 +35,7 @@ "downshift-hooks": "^0.8.1", "final-form": "^4.20.10", "fromentries": "^1.3.2", + "h3-js": "^4.1.0", "humanize-duration": "^3.31.0", "mapbox-gl": "^1.13.3", "mapbox-gl-draw-rectangle-mode": "^1.0.4", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 8b7906c261..d62612116b 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -7677,6 +7677,11 @@ gzip-size@^6.0.0: dependencies: duplexer "^0.1.2" +h3-js@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/h3-js/-/h3-js-4.1.0.tgz#f8c4a8ad36612489a954f1a0bb3f4b7657d364e5" + integrity sha512-LQhmMl1dRQQjMXPzJc7MpZ/CqPOWWuAvVEoVJM9n/s7vHypj+c3Pd5rLQCkAsOgAoAYKbNCsYFE++LF7MvSfCQ== + handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" From 709c35e3c506204b273e3805e642f5718f8cce6a Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 18 Sep 2024 16:45:04 +0530 Subject: [PATCH 026/101] Handle page loading with placeholder --- frontend/src/views/partnersMapswipeStats.js | 113 +++++++++++++------- 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index eed682c021..43065193d9 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -1,4 +1,5 @@ import { FormattedMessage } from 'react-intl'; +import ReactPlaceholder from 'react-placeholder'; import { InfoIcon } from '../components/svgIcons'; import { Overview } from '../components/partnerMapswipeStats/overview'; @@ -14,6 +15,38 @@ import { StatsCardWithFooter } from '../components/statsCard'; import messages from './messages'; import './partnersMapswipeStats.css'; +const PagePlaceholder = () => ( +
+ +
+ + + +
+
+ + +
+
+ + +
+
+); + const InfoBanner = () => { return (
@@ -26,53 +59,57 @@ const InfoBanner = () => { }; export const PartnersMapswipeStats = () => { + const isPageReady = true; + return ( -
- - + } ready={isPageReady}> +
+ + -
- -
+
+ +
-
- -
+
+ +
-
- -
+
+ +
-
- -
+
+ +
-
- -
+
+ +
-
- } - value="338K" - style={{ width: '48.5%' }} - /> - } - value="11 days 5 hrs" - className="w-100" - style={{ width: '48.5%' }} - /> -
+
+ } + value="338K" + style={{ width: '48.5%' }} + /> + } + value="11 days 5 hrs" + className="w-100" + style={{ width: '48.5%' }} + /> +
-
- - -
+
+ + +
-
- +
+ +
-
+
); }; From ec13e37b58924ab1dea2725976e52bbe591a8ae9 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 18 Sep 2024 17:30:58 +0530 Subject: [PATCH 027/101] feat: add dropdown to update x axis time frame --- .../timeSpentContributing.js | 183 +++++++++++------- 1 file changed, 111 insertions(+), 72 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js index 89e97f1fe0..f00c7caaee 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js @@ -1,9 +1,10 @@ -import { useEffect, useRef } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import { Chart } from 'chart.js/auto'; import 'chartjs-adapter-date-fns'; import { CHART_COLOURS } from '../../config'; +import { Dropdown } from '../dropdown'; import messages from './messages'; const MOCK_DATA = [ @@ -19,13 +20,12 @@ const MOCK_DATA = [ ]; export const TimeSpentContributing = () => { + const [chartDistribution, setChartDistribution] = useState('day'); // "day" or "month" const chartRef = useRef(null); const chartInstance = useRef(null); useEffect(() => { - if (chartInstance.current) { - chartInstance.current.destroy(); - } + if (!chartRef.current) return; const context = chartRef.current.getContext('2d'); // Create gradient for the area @@ -34,73 +34,24 @@ export const TimeSpentContributing = () => { gradient.addColorStop(0.4, CHART_COLOURS.red); gradient.addColorStop(1, 'rgba(215, 63, 63, 0)'); - chartInstance.current = new Chart(context, { - type: 'line', - data: { - labels: MOCK_DATA.map((entry) => entry.date), - datasets: [ - { - label: 'Time Spent', - backgroundColor: gradient, - data: MOCK_DATA.map((entry) => entry.minutesSpent), - fill: true, - tension: 0.4, - }, - ], - }, - options: { - responsive: true, - maintainAspectRatio: false, - legend: { display: false }, - scales: { - x: { - type: 'time', - time: { - unit: 'day', - tooltipFormat: 'MMM d, yyyy', + if (!chartInstance.current) { + chartInstance.current = new Chart(context, { + type: 'line', + data: { + labels: MOCK_DATA.map((entry) => entry.date), + datasets: [ + { + label: 'Time Spent', + backgroundColor: gradient, + data: MOCK_DATA.map((entry) => entry.minutesSpent), + fill: true, + tension: 0.4, }, - }, - y: { - beginAtZero: true, - ticks: { - callback: function (value) { - const days = Math.floor(value / (24 * 60)); - const hours = Math.floor((value % (24 * 60)) / 60); - const minutes = value % 60; - - let label = ''; - if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; - if (hours > 0 || days > 0) label += `${hours} hr${hours !== 1 ? 's' : ''}`; - if (minutes > 0 && days === 0) - label += ` ${minutes} min${minutes !== 1 ? 's' : ''}`; - - return label.trim(); - }, - stepSize: 60, - }, - }, + ], }, - plugins: { - tooltip: { - callbacks: { - label: function (context) { - const value = context.parsed.y; - const days = Math.floor(value / (24 * 60)); - const hours = Math.floor((value % (24 * 60)) / 60); - const minutes = value % 60; - - let label = 'Time Spent: '; - if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; - if (hours > 0 || days > 0) label += `${hours} hour${hours !== 1 ? 's' : ''} `; - if (minutes > 0) label += `${minutes} minute${minutes !== 1 ? 's' : ''}`; - - return label.trim(); - }, - }, - }, - }, - }, - }); + options: getChartOptions(), + }); + } return () => { if (chartInstance.current) { @@ -109,11 +60,99 @@ export const TimeSpentContributing = () => { }; }, []); + useEffect(() => { + if (chartInstance.current) { + chartInstance.current.options = getChartOptions(); + chartInstance.current.update(); + } + }, [chartDistribution]); + + const getChartOptions = () => { + const xAxisTime = + chartDistribution === 'day' + ? { + unit: 'day', + tooltipFormat: 'MMM d, yyyy', + } + : { + unit: 'month', + tooltipFormat: 'MMM, yyyy', + }; + + return { + responsive: true, + maintainAspectRatio: false, + legend: { display: false }, + scales: { + x: { + type: 'time', + time: xAxisTime, + }, + y: { + beginAtZero: true, + ticks: { + callback: function (value) { + const days = Math.floor(value / (24 * 60)); + const hours = Math.floor((value % (24 * 60)) / 60); + const minutes = value % 60; + + let label = ''; + if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; + if (hours > 0 || days > 0) label += `${hours} hr${hours !== 1 ? 's' : ''}`; + if (minutes > 0 && days === 0) label += ` ${minutes} min${minutes !== 1 ? 's' : ''}`; + + return label.trim(); + }, + stepSize: 60, + }, + }, + }, + plugins: { + tooltip: { + callbacks: { + label: function (context) { + const value = context.parsed.y; + const days = Math.floor(value / (24 * 60)); + const hours = Math.floor((value % (24 * 60)) / 60); + const minutes = value % 60; + + let label = 'Time Spent: '; + if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; + if (hours > 0 || days > 0) label += `${hours} hour${hours !== 1 ? 's' : ''} `; + if (minutes > 0) label += `${minutes} minute${minutes !== 1 ? 's' : ''}`; + + return label.trim(); + }, + }, + }, + }, + }; + }; + + const dropdownOptions = [ + { + label: 'Day', + value: 'day', + }, + { + label: 'Month', + value: 'month', + }, + ]; + return (
-

- -

+
+

+ +

+ setChartDistribution(options[0].value)} + options={dropdownOptions} + value={chartDistribution} + className="ba b--grey-light bg-white mr1 v-mid pv2 ph2" + /> +
From 72f9c1399b584347fec0c8a07b099592730a1fb5 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 18 Sep 2024 17:59:56 +0530 Subject: [PATCH 028/101] Handle page error UI and update messages --- .../partnerMapswipeStats/messages.js | 8 ++ .../timeSpentContributing.js | 4 +- frontend/src/views/messages.js | 4 + frontend/src/views/partnersMapswipeStats.js | 89 +++++++++++-------- 4 files changed, 65 insertions(+), 40 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index 7b31d18528..c8dfdf753d 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -72,6 +72,14 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.timeSpentContributing', defaultMessage: 'Time Spent Contributing', }, + timeSpentContributingDayOption: { + id: 'management.partners.stats.mapswipe.timeSpentContributing.options.day', + defaultMessage: 'Day', + }, + timeSpentContributingMonthOption: { + id: 'management.partners.stats.mapswipe.timeSpentContributing.options.month', + defaultMessage: 'Month', + }, timeSpentContributingByDay: { id: 'management.partners.stats.mapswipe.timeSpentContributingByDay', defaultMessage: 'Time Spent Contributing by Day of Week', diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js index f00c7caaee..9ca9009438 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js @@ -131,11 +131,11 @@ export const TimeSpentContributing = () => { const dropdownOptions = [ { - label: 'Day', + label: , value: 'day', }, { - label: 'Month', + label: , value: 'month', }, ]; diff --git a/frontend/src/views/messages.js b/frontend/src/views/messages.js index b3ce294bbb..072281cd87 100644 --- a/frontend/src/views/messages.js +++ b/frontend/src/views/messages.js @@ -826,4 +826,8 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.timeSpentContributing', defaultMessage: 'Time Spent Contributing', }, + partnersMapswipeStatsError: { + id: 'management.partners.stats.mapswipe.page.error', + defaultMessage: 'Something went wrong! Could not load data.', + }, }); diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 43065193d9..5d375a62df 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -13,6 +13,7 @@ import { SwipesByProjectType } from '../components/partnerMapswipeStats/swipesBy import { SwipesByOrganisation } from '../components/partnerMapswipeStats/swipesByOrganisation'; import { StatsCardWithFooter } from '../components/statsCard'; import messages from './messages'; +import { BanIcon } from '../components/svgIcons'; import './partnersMapswipeStats.css'; const PagePlaceholder = () => ( @@ -60,56 +61,68 @@ const InfoBanner = () => { export const PartnersMapswipeStats = () => { const isPageReady = true; + const isError = false; return ( } ready={isPageReady}> -
- - - -
- + {isPageReady && isError ? ( +
+
+ +

+ +

+
+ ) : ( +
+ + -
- -
+
+ +
-
- -
+
+ +
-
- -
+
+ +
-
- -
+
+ +
-
- } - value="338K" - style={{ width: '48.5%' }} - /> - } - value="11 days 5 hrs" - className="w-100" - style={{ width: '48.5%' }} - /> -
+
+ +
-
- - -
+
+ } + value="338K" + style={{ width: '48.5%' }} + /> + } + value="11 days 5 hrs" + className="w-100" + style={{ width: '48.5%' }} + /> +
+ +
+ + +
-
- +
+ +
-
+ )} ); }; From fe7f00767335f636cda2e8284e6d8340058eb44a Mon Sep 17 00:00:00 2001 From: bshankar Date: Tue, 3 Sep 2024 08:03:10 +0530 Subject: [PATCH 029/101] Add queries for mapswipe user group stats --- backend/models/dtos/partner_stats_dto.py | 2 + backend/services/mapswipe_service.py | 112 +++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 backend/models/dtos/partner_stats_dto.py create mode 100644 backend/services/mapswipe_service.py diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py new file mode 100644 index 0000000000..dde6a4c904 --- /dev/null +++ b/backend/models/dtos/partner_stats_dto.py @@ -0,0 +1,2 @@ +class ProjectStatsDTO: + pass diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py new file mode 100644 index 0000000000..e21e9b5d20 --- /dev/null +++ b/backend/services/mapswipe_service.py @@ -0,0 +1,112 @@ +from backend.models.dtos.partner_stats_dto import ProjectStatsDTO + + +class MapswipeService: + @staticmethod + def _build_query_user_group_stats(group_id: str): + """A private method to build a graphQl query for fetching a user group's stats from Mapswipe.""" + + operationName = "UserGroupStats" + query = """ + query UserGroupStats($pk: ID!, $limit: Int!, $offset: Int!) { + userGroup(pk: $pk) { + id + userGroupId + name + description + userMemberships(pagination: {limit: $limit, offset: $offset}) { + count + items { + id + userId + username + isActive + totalMappingProjects + totalSwipeTime + totalSwipes + __typename + } + __typename + } + __typename + } + userGroupStats(userGroupId: $pk) { + id + stats { + totalContributors + totalSwipes + totalSwipeTime + __typename + } + statsLatest { + totalContributors + totalSwipeTime + totalSwipes + __typename + } + __typename + } + } + """ + variables = {"limit": 10, "offset": 0, "pk": group_id} + return {operationName, query, variables} + + def _build_query_filtered_user_group_stats( + group_id: str, from_date: str, to_date: str + ): + """A private method to build a graphQl query for fetching a user group's stats within a timerange from Mapswipe.""" + + operationName = "FilteredUserGroupStats" + query = """ + query FilteredUserGroupStats($pk: ID!, $fromDate: DateTime!, $toDate: DateTime!) { + userGroupStats(userGroupId: $pk) { + id + filteredStats(dateRange: {fromDate: $fromDate, toDate: $toDate}) { + userStats { + totalMappingProjects + totalSwipeTime + totalSwipes + username + userId + __typename + } + contributionByGeo { + geojson + totalContribution + __typename + } + areaSwipedByProjectType { + totalArea + projectTypeDisplay + projectType + __typename + } + swipeByDate { + taskDate + totalSwipes + __typename + } + swipeTimeByDate { + date + totalSwipeTime + __typename + } + swipeByProjectType { + projectType + projectTypeDisplay + totalSwipes + __typename + } + swipeByOrganizationName { + organizationName + totalSwipes + __typename + } + __typename + } + __typename + } + } + """ + variables = {"fromDate": from_date, "toDate": to_date, "pk": group_id} + return {operationName, query, variables} From a92be2883395a2bec5adff51b061fc2967adbe4b Mon Sep 17 00:00:00 2001 From: bshankar Date: Wed, 11 Sep 2024 14:03:34 +0530 Subject: [PATCH 030/101] Add DTO for partner stats --- backend/models/dtos/partner_stats_dto.py | 90 +++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index dde6a4c904..c1c0525bc8 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -1,2 +1,88 @@ -class ProjectStatsDTO: - pass +from schematics import Model +from schematics.types import ( + StringType, + LongType, + IntType, + ListType, + UTCDateTimeType, + ModelType, + FloatType, +) + + +class OrganizationContributionsDTO(Model): + organization_name = StringType(serialized_name="organizationName") + total_constributions: IntType(serialized_name="totalconstributions") + + +class UserContributionsDTO(Model): + total_mapping_projects = IntType(serialized_name="totalMappingProjects") + total_constribution_time: IntType(serialized_name="totalconstributionTime") + total_constributions: IntType(serialized_name="totalconstributions") + username = StringType() + user_id = StringType(serialized_name="userId") + + +class GeojsonDTO(Model): + type = StringType() + coordinates = ListType(FloatType) + + +class GeoContributionsDTO(Model): + geojson = ModelType(GeojsonDTO) + total_constributions = IntType(serialized_name="totalconstributions") + + +class ContributionsByDateDTO(Model): + task_date = StringType(serialized_name="taskDate") + total_constributions = IntType(serialized_name="totalconstributions") + + +class ContributionTimeByDateDTO(Model): + date = StringType(serialized_name="date") + total_constribution_time: IntType(serialized_name="totalconstributionTime") + + +class ContributionsByProjectTypeDTO(Model): + project_type = StringType(serialized_name="projectType") + project_type_display = StringType(serialized_name="projectTypeDisplay") + total_constributions = IntType(serialized_name="totalconstributions") + + +class PartnerStatsDTO(Model): + id: LongType() + provider: StringType() + id_inside_provider: StringType(serialized_name="idInsideProvider") + name_inside_provider: StringType(serialized_name="nameInsideProvider") + total_members: IntType(serialized_name="totalMembers") + + # General stats of partner + + total_contributors: IntType(serialized_name="totalContributors") + total_constributions: IntType(serialized_name="totalconstributions") + total_constribution_time: IntType(serialized_name="totalconstributionTime") + + # Stats filtered by time range + + from_date = UTCDateTimeType(serialized_name="fromDate") + to_date = UTCDateTimeType(serialized_name="toDate") + constributions_by_user = ListType( + ModelType(UserContributionsDTO), serialized_name="contributionsByUser" + ) + constributions_by_geo = ListType( + ModelType(GeoContributionsDTO), serialized_name="contributionsByGeo" + ) + constributions_by_project_type = ListType( + ModelType(ContributionsByProjectTypeDTO), + serialized_name="contributionsByProjectType", + ) + constributions_by_date = ListType( + ModelType(ContributionsByDateDTO), serialized_name="contributionsByDate" + ) + constributions_by_organization_name = ListType( + ModelType(OrganizationContributionsDTO), + serialized_name="contributionsByorganizationName", + ) + constribution_time_by_date = ListType( + ModelType(ContributionTimeByDateDTO), serialized_name="contributionTimeByDate" + ) From 7e558570b7e93999f95ab0a9b0e3ee0396fdab21 Mon Sep 17 00:00:00 2001 From: bshankar Date: Wed, 11 Sep 2024 19:31:27 +0530 Subject: [PATCH 031/101] [WIP] Implement a caching service for fetching partner stats --- backend/services/mapswipe_service.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index e21e9b5d20..7db4a70ecc 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -1,9 +1,14 @@ -from backend.models.dtos.partner_stats_dto import ProjectStatsDTO +from backend.models.dtos.partner_stats_dto import PartnerStatsDTO +from cachetools import TTLCache, cached +import requests + +partner_stats_cache = TTLCache(maxsize=10_000, ttl=60 * 60 * 2) +MAPSWIPE_API_URL = "https://api.mapswipe.org/graphql/" class MapswipeService: @staticmethod - def _build_query_user_group_stats(group_id: str): + def __build_query_user_group_stats(group_id: str): """A private method to build a graphQl query for fetching a user group's stats from Mapswipe.""" operationName = "UserGroupStats" @@ -51,7 +56,7 @@ def _build_query_user_group_stats(group_id: str): variables = {"limit": 10, "offset": 0, "pk": group_id} return {operationName, query, variables} - def _build_query_filtered_user_group_stats( + def __build_query_filtered_user_group_stats( group_id: str, from_date: str, to_date: str ): """A private method to build a graphQl query for fetching a user group's stats within a timerange from Mapswipe.""" @@ -110,3 +115,18 @@ def _build_query_filtered_user_group_stats( """ variables = {"fromDate": from_date, "toDate": to_date, "pk": group_id} return {operationName, query, variables} + + @cached(partner_stats_cache) + def fetch_stats( + self, group_id: str, from_date: str, to_date: str + ) -> PartnerStatsDTO: + group_stats = requests.post( + MAPSWIPE_API_URL, self.__build_query_user_group_stats(group_id) + ) + filtered_group_stats = requests.post( + MAPSWIPE_API_URL, + self.__build_query_filtered_user_group_stats(group_id, from_date, to_date), + ) + + # Load fetched stats into the DTO + parter_stats_dto = PartnerStatsDTO() From 21ebdb48d7923a4e86036c97685a87558c0dafde Mon Sep 17 00:00:00 2001 From: bshankar Date: Thu, 12 Sep 2024 11:55:09 +0530 Subject: [PATCH 032/101] Tweak partner stats caching: TTL 24h, max size: 16 --- backend/services/mapswipe_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 7db4a70ecc..69e1737c7e 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -2,7 +2,7 @@ from cachetools import TTLCache, cached import requests -partner_stats_cache = TTLCache(maxsize=10_000, ttl=60 * 60 * 2) +partner_stats_cache = TTLCache(maxsize=16, ttl=60 * 60 * 24) MAPSWIPE_API_URL = "https://api.mapswipe.org/graphql/" From 6a81cd8d0aeaf57ae480047716b96e07bd18a03e Mon Sep 17 00:00:00 2001 From: bshankar Date: Thu, 12 Sep 2024 11:57:13 +0530 Subject: [PATCH 033/101] Add recent contributions to the partner stats DTO --- backend/models/dtos/partner_stats_dto.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index c1c0525bc8..86d968e17f 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -50,7 +50,6 @@ class ContributionsByProjectTypeDTO(Model): class PartnerStatsDTO(Model): - id: LongType() provider: StringType() id_inside_provider: StringType(serialized_name="idInsideProvider") name_inside_provider: StringType(serialized_name="nameInsideProvider") @@ -62,6 +61,12 @@ class PartnerStatsDTO(Model): total_constributions: IntType(serialized_name="totalconstributions") total_constribution_time: IntType(serialized_name="totalconstributionTime") + # Recent contributions during the last 1 month + + total_recent_contributors: IntType(serialized_name="totalRecentContributors") + total_recent_constributions: IntType(serialized_name="totalRecentconstributions") + total_recent_constribution_time: IntType(serialized_name="totalRecentconstributionTime") + # Stats filtered by time range from_date = UTCDateTimeType(serialized_name="fromDate") From d4b577e6f5d358ec49f606de15d9bc892bd002ae Mon Sep 17 00:00:00 2001 From: bshankar Date: Thu, 12 Sep 2024 12:43:56 +0530 Subject: [PATCH 034/101] Add API endpoint to fetch partner statistics --- backend/__init__.py | 8 +++ backend/api/partners/statistics.py | 79 ++++++++++++++++++++++++ backend/models/dtos/partner_stats_dto.py | 1 + backend/services/mapswipe_service.py | 20 ++++-- 4 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 backend/api/partners/statistics.py diff --git a/backend/__init__.py b/backend/__init__.py index c72913f422..8e6e27b6fd 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -248,6 +248,9 @@ def add_api_endpoints(app): PartnersByProjectAPI, ) + # Partner statistics API + from backend.api.partners.statistics import PartnerStatisticsAPI + # Tasks API import from backend.api.tasks.resources import ( TasksRestAPI, @@ -590,6 +593,11 @@ def add_api_endpoints(app): format_url("partners//"), methods=["GET", "DELETE", "PUT"], ) + api.add_resource( + PartnerStatisticsAPI, + format_url("/partners//statistics"), + methods=["GET"], + ) api.add_resource( PartnerPermalinkRestAPI, format_url("partners//"), diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py new file mode 100644 index 0000000000..2b6b534918 --- /dev/null +++ b/backend/api/partners/statistics.py @@ -0,0 +1,79 @@ +from flask_restful import Resource, request + +from backend.services.users.authentication_service import token_auth +from backend.models.postgis.user import User +from backend.services.partner_service import PartnerService +from backend.services.users.user_service import UserRole +from backend.exceptions import BadRequest + +# Replaceable by another service which implements the method: +# fetch_partner_stats(id_inside_service, from_date, to_date) -> PartnerStatsDTO +from backend.services.mapswipe_service import MapswipeService + + +class PartnerStatisticsAPI(Resource): + @token_auth.login_required + def get(self, partner_id): + """ + Get partner statistics by id + --- + tags: + - partners + produces: + - application/json + parameters: + - in: header + name: Authorization + description: Base64 encoded session token + required: true + type: string + default: Token sessionTokenHere== + - in: query + name: fromDate + type: string + example: 2024-01-01 + - in: query + name: toDate + type: string + example: 2024-09-01 + - name: partner_id + in: path + description: The id of the partner + required: true + type: integer + default: 1 + responses: + 200: + description: Partner found + 401: + description: Unauthorized - Invalid credentials + 404: + description: Partner not found + 500: + description: Internal Server Error + """ + + request_user = User.get_by_id(token_auth.current_user()) + if request_user.role != UserRole.ADMIN.value: + return { + "Error": "Only admin users can manage partners.", + "SubCode": "OnlyAdminAccess", + }, 403 + + mapswipe = MapswipeService() + from_date = request.args.get("fromDate") + to_date = request.args.get("toDate") + + if from_date is not None and to_date is not None and from_date > to_date: + raise BadRequest( + sub_code="INVALID_TIME_RANGE", + message="fromDate should be less than toDate", + from_date=from_date, + to_date=to_date, + ) + + partner = PartnerService.get_partner_by_id(partner_id) + + return mapswipe.fetch_partner_stats( + partner.mapswipe_group_id, from_date, to_date + ).to_primitive(), 200 diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index 86d968e17f..e87f2ca486 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -50,6 +50,7 @@ class ContributionsByProjectTypeDTO(Model): class PartnerStatsDTO(Model): + id: LongType() provider: StringType() id_inside_provider: StringType(serialized_name="idInsideProvider") name_inside_provider: StringType(serialized_name="nameInsideProvider") diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 69e1737c7e..168b7fc471 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -1,8 +1,9 @@ from backend.models.dtos.partner_stats_dto import PartnerStatsDTO from cachetools import TTLCache, cached import requests +from typing import Optional -partner_stats_cache = TTLCache(maxsize=16, ttl=60 * 60 * 24) +partner_stats_cache = TTLCache(maxsize=128, ttl=60 * 60 * 24) MAPSWIPE_API_URL = "https://api.mapswipe.org/graphql/" @@ -117,16 +118,25 @@ def __build_query_filtered_user_group_stats( return {operationName, query, variables} @cached(partner_stats_cache) - def fetch_stats( - self, group_id: str, from_date: str, to_date: str + def fetch_partner_stats( + self, group_id: str, from_date: Optional[str], to_date: Optional[str] ) -> PartnerStatsDTO: group_stats = requests.post( - MAPSWIPE_API_URL, self.__build_query_user_group_stats(group_id) + MAPSWIPE_API_URL, data=self.__build_query_user_group_stats(group_id) ) filtered_group_stats = requests.post( MAPSWIPE_API_URL, - self.__build_query_filtered_user_group_stats(group_id, from_date, to_date), + data=self.__build_query_filtered_user_group_stats( + group_id, from_date, to_date + ), ) + print("group_stats", group_stats) + print("filtered_group_stats", filtered_group_stats) + # Load fetched stats into the DTO parter_stats_dto = PartnerStatsDTO() + parter_stats_dto.provider = "mapswipe" + parter_stats_dto.id_inside_provider = group_id + + return parter_stats_dto From 6ade934233d52b59dcbea0d85ea112eb3e720e71 Mon Sep 17 00:00:00 2001 From: bshankar Date: Thu, 12 Sep 2024 17:03:58 +0530 Subject: [PATCH 035/101] Split partner statistics service into general and filtered --- backend/api/partners/statistics.py | 9 +++-- backend/models/dtos/partner_stats_dto.py | 18 +++++++--- backend/services/mapswipe_service.py | 43 +++++++++++++++--------- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index 2b6b534918..1ee2eaf622 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -74,6 +74,9 @@ def get(self, partner_id): partner = PartnerService.get_partner_by_id(partner_id) - return mapswipe.fetch_partner_stats( - partner.mapswipe_group_id, from_date, to_date - ).to_primitive(), 200 + return ( + mapswipe.fetch_partner_stats( + partner.mapswipe_group_id, from_date, to_date + ).to_primitive(), + 200, + ) diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index e87f2ca486..ed91f19f6b 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -49,7 +49,9 @@ class ContributionsByProjectTypeDTO(Model): total_constributions = IntType(serialized_name="totalconstributions") -class PartnerStatsDTO(Model): +class GroupedPartnerStatsDTO(Model): + """General statistics of a partner and its members.""" + id: LongType() provider: StringType() id_inside_provider: StringType(serialized_name="idInsideProvider") @@ -57,7 +59,6 @@ class PartnerStatsDTO(Model): total_members: IntType(serialized_name="totalMembers") # General stats of partner - total_contributors: IntType(serialized_name="totalContributors") total_constributions: IntType(serialized_name="totalconstributions") total_constribution_time: IntType(serialized_name="totalconstributionTime") @@ -66,9 +67,18 @@ class PartnerStatsDTO(Model): total_recent_contributors: IntType(serialized_name="totalRecentContributors") total_recent_constributions: IntType(serialized_name="totalRecentconstributions") - total_recent_constribution_time: IntType(serialized_name="totalRecentconstributionTime") + total_recent_constribution_time: IntType( + serialized_name="totalRecentconstributionTime" + ) + + +class FilteredPartnerStatsDTO(Model): + """Statistics of a partner contributions filtered by time range.""" - # Stats filtered by time range + id: LongType() + provider: StringType() + id_inside_provider: StringType(serialized_name="idInsideProvider") + name_inside_provider: StringType(serialized_name="nameInsideProvider") from_date = UTCDateTimeType(serialized_name="fromDate") to_date = UTCDateTimeType(serialized_name="toDate") diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 168b7fc471..72549042d0 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -1,4 +1,7 @@ -from backend.models.dtos.partner_stats_dto import PartnerStatsDTO +from backend.models.dtos.partner_stats_dto import ( + GroupedPartnerStatsDTO, + FilteredPartnerStatsDTO, +) from cachetools import TTLCache, cached import requests from typing import Optional @@ -9,7 +12,7 @@ class MapswipeService: @staticmethod - def __build_query_user_group_stats(group_id: str): + def __build_query_user_group_stats(group_id: str, limit: int, offset: int): """A private method to build a graphQl query for fetching a user group's stats from Mapswipe.""" operationName = "UserGroupStats" @@ -54,7 +57,7 @@ def __build_query_user_group_stats(group_id: str): } } """ - variables = {"limit": 10, "offset": 0, "pk": group_id} + variables = {"limit": limit, "offset": offset, "pk": group_id} return {operationName, query, variables} def __build_query_filtered_user_group_stats( @@ -118,25 +121,35 @@ def __build_query_filtered_user_group_stats( return {operationName, query, variables} @cached(partner_stats_cache) - def fetch_partner_stats( - self, group_id: str, from_date: Optional[str], to_date: Optional[str] - ) -> PartnerStatsDTO: + def fetch_grouped_partner_stats( + self, group_id: str, limit: int, offset: int + ) -> GroupedPartnerStatsDTO: group_stats = requests.post( - MAPSWIPE_API_URL, data=self.__build_query_user_group_stats(group_id) + MAPSWIPE_API_URL, + data=self.__build_query_user_group_stats(group_id, limit, offset), ) + print("group_stats", group_stats) + + grouped_parter_stats_dto = GroupedPartnerStatsDTO() + grouped_parter_stats_dto.provider = "mapswipe" + grouped_parter_stats_dto.id_inside_provider = group_id + + return grouped_parter_stats_dto + + @cached(partner_stats_cache) + def fetch_filtered_partner_stats( + self, group_id: str, from_date: Optional[str], to_date: Optional[str] + ) -> FilteredPartnerStatsDTO: filtered_group_stats = requests.post( MAPSWIPE_API_URL, data=self.__build_query_filtered_user_group_stats( group_id, from_date, to_date ), ) - - print("group_stats", group_stats) print("filtered_group_stats", filtered_group_stats) - # Load fetched stats into the DTO - parter_stats_dto = PartnerStatsDTO() - parter_stats_dto.provider = "mapswipe" - parter_stats_dto.id_inside_provider = group_id - - return parter_stats_dto + filtered_parter_stats_dto = FilteredPartnerStatsDTO() + filtered_parter_stats_dto.provider = "mapswipe" + filtered_parter_stats_dto.id_inside_provider = group_id + + return filtered_parter_stats_dto From 6fcb1b2db769baf6fa7ca31d3dcce8447b49a02f Mon Sep 17 00:00:00 2001 From: bshankar Date: Thu, 12 Sep 2024 17:15:33 +0530 Subject: [PATCH 036/101] Split partner statistics API into group, filtered endpoints --- backend/__init__.py | 14 ++++-- backend/api/partners/statistics.py | 66 ++++++++++++++++++++++++++-- backend/services/mapswipe_service.py | 7 +-- 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/backend/__init__.py b/backend/__init__.py index 8e6e27b6fd..567af8ff02 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -249,7 +249,10 @@ def add_api_endpoints(app): ) # Partner statistics API - from backend.api.partners.statistics import PartnerStatisticsAPI + from backend.api.partners.statistics import ( + GroupPartnerStatisticsAPI, + FilteredPartnerStatisticsAPI, + ) # Tasks API import from backend.api.tasks.resources import ( @@ -594,8 +597,13 @@ def add_api_endpoints(app): methods=["GET", "DELETE", "PUT"], ) api.add_resource( - PartnerStatisticsAPI, - format_url("/partners//statistics"), + GroupPartnerStatisticsAPI, + format_url("/partners//group-statistics"), + methods=["GET"], + ) + api.add_resource( + FilteredPartnerStatisticsAPI, + format_url("/partners//filtered-statistics"), methods=["GET"], ) api.add_resource( diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index 1ee2eaf622..fc230746b3 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -11,11 +11,11 @@ from backend.services.mapswipe_service import MapswipeService -class PartnerStatisticsAPI(Resource): +class FilteredPartnerStatisticsAPI(Resource): @token_auth.login_required def get(self, partner_id): """ - Get partner statistics by id + Get partner statistics by id and time range --- tags: - partners @@ -75,8 +75,68 @@ def get(self, partner_id): partner = PartnerService.get_partner_by_id(partner_id) return ( - mapswipe.fetch_partner_stats( + mapswipe.fetch_filtered_partner_stats( partner.mapswipe_group_id, from_date, to_date ).to_primitive(), 200, ) + + +class GroupPartnerStatisticsAPI(Resource): + @token_auth.login_required + def get(self, partner_id): + """ + Get partner statistics by id and broken down by each contributor. + This API is paginated with limit and offset query parameters. + --- + tags: + - partners + produces: + - application/json + parameters: + - in: header + name: Authorization + description: Base64 encoded session token + required: true + type: string + default: Token sessionTokenHere== + - in: query + name: limit + type: integer + example: 10 + - in: query + name: offset + type: integer + example: 0 + - name: partner_id + in: path + description: The id of the partner + required: true + type: integer + default: 1 + responses: + 200: + description: Partner found + 401: + description: Unauthorized - Invalid credentials + 404: + description: Partner not found + 500: + description: Internal Server Error + """ + + request_user = User.get_by_id(token_auth.current_user()) + if request_user.role != UserRole.ADMIN.value: + return { + "Error": "Only admin users can manage partners.", + "SubCode": "OnlyAdminAccess", + }, 403 + + mapswipe = MapswipeService() + partner = PartnerService.get_partner_by_id(partner_id) + limit = request.args.get("limit", 10) + offset = request.args.get("offset", 0) + + return mapswipe.fetch_grouped_partner_stats( + partner.mapswipe_group_id, limit, offset + ), 200 diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 72549042d0..93975dd2ae 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -6,7 +6,8 @@ import requests from typing import Optional -partner_stats_cache = TTLCache(maxsize=128, ttl=60 * 60 * 24) +grouped_partner_stats_cache = TTLCache(maxsize=128, ttl=60 * 60 * 24) +filtered_partner_stats_cache = TTLCache(maxsize=128, ttl=60 * 60 * 24) MAPSWIPE_API_URL = "https://api.mapswipe.org/graphql/" @@ -120,7 +121,7 @@ def __build_query_filtered_user_group_stats( variables = {"fromDate": from_date, "toDate": to_date, "pk": group_id} return {operationName, query, variables} - @cached(partner_stats_cache) + @cached(grouped_partner_stats_cache) def fetch_grouped_partner_stats( self, group_id: str, limit: int, offset: int ) -> GroupedPartnerStatsDTO: @@ -136,7 +137,7 @@ def fetch_grouped_partner_stats( return grouped_parter_stats_dto - @cached(partner_stats_cache) + @cached(filtered_partner_stats_cache) def fetch_filtered_partner_stats( self, group_id: str, from_date: Optional[str], to_date: Optional[str] ) -> FilteredPartnerStatsDTO: From 67ed4f51ed1063e08a93d101a65a234481da9a0f Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 13 Sep 2024 10:31:02 +0530 Subject: [PATCH 037/101] Add user memberships to PartnerStatisticsDTO --- backend/models/dtos/partner_stats_dto.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index ed91f19f6b..287cf9ee01 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -7,9 +7,25 @@ UTCDateTimeType, ModelType, FloatType, + BooleanType, ) +class MemberDTO(Model): + id: StringType() + user_id: StringType(serialized_name="userId") + username: StringType() + is_active: BooleanType(serialized_name="isActive") + total_mapping_projects = IntType(serialized_name="totalMappingProjects") + total_constribution_time: IntType(serialized_name="totalconstributionTime") + total_constributions: IntType(serialized_name="totalconstributions") + + +class UserMembershipsDTO(Model): + count: IntType() + users: ListType(ModelType(MemberDTO)) + + class OrganizationContributionsDTO(Model): organization_name = StringType(serialized_name="organizationName") total_constributions: IntType(serialized_name="totalconstributions") @@ -79,6 +95,7 @@ class FilteredPartnerStatsDTO(Model): provider: StringType() id_inside_provider: StringType(serialized_name="idInsideProvider") name_inside_provider: StringType(serialized_name="nameInsideProvider") + users = ModelType(UserMembershipsDTO) from_date = UTCDateTimeType(serialized_name="fromDate") to_date = UTCDateTimeType(serialized_name="toDate") From 1b292eb75c494a95c59c66e7a469416184705294 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 13 Sep 2024 11:06:06 +0530 Subject: [PATCH 038/101] Partner statistics: Improve API docs --- backend/api/partners/statistics.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index fc230746b3..981ad92e96 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -31,11 +31,13 @@ def get(self, partner_id): - in: query name: fromDate type: string - example: 2024-01-01 + description: Fetch partner statistics from date as yyyy-mm-dd + example: "2024-01-01" - in: query name: toDate type: string - example: 2024-09-01 + example: "2024-09-01" + description: Fetch partner statistics to date as yyyy-mm-dd - name: partner_id in: path description: The id of the partner @@ -102,10 +104,12 @@ def get(self, partner_id): default: Token sessionTokenHere== - in: query name: limit + description: The number of partner members to fetch type: integer example: 10 - in: query name: offset + description: The starting index from which to fetch partner members type: integer example: 0 - name: partner_id From c1254ac4fdedc56297f7abff4dd1acd0c1926319 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 13 Sep 2024 11:32:00 +0530 Subject: [PATCH 039/101] Fix: dicts returned by partner stats query builders --- backend/services/mapswipe_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 93975dd2ae..73cc95f123 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -59,7 +59,7 @@ def __build_query_user_group_stats(group_id: str, limit: int, offset: int): } """ variables = {"limit": limit, "offset": offset, "pk": group_id} - return {operationName, query, variables} + return {"operationName": operationName, "query": query, "variables": variables} def __build_query_filtered_user_group_stats( group_id: str, from_date: str, to_date: str @@ -119,7 +119,7 @@ def __build_query_filtered_user_group_stats( } """ variables = {"fromDate": from_date, "toDate": to_date, "pk": group_id} - return {operationName, query, variables} + return {"operationName": operationName, "query": query, "variables": variables} @cached(grouped_partner_stats_cache) def fetch_grouped_partner_stats( From 0c169c1f60fc6bc91045f913939a3e6da56c58b3 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 13 Sep 2024 11:32:47 +0530 Subject: [PATCH 040/101] Fix: Provide content-type to mapswipe requests --- backend/services/mapswipe_service.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 73cc95f123..16bea6ebf1 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -128,6 +128,7 @@ def fetch_grouped_partner_stats( group_stats = requests.post( MAPSWIPE_API_URL, data=self.__build_query_user_group_stats(group_id, limit, offset), + headers={"Content-Type": "application/json"}, ) print("group_stats", group_stats) @@ -145,6 +146,7 @@ def fetch_filtered_partner_stats( MAPSWIPE_API_URL, data=self.__build_query_filtered_user_group_stats( group_id, from_date, to_date + headers={"Content-Type": "application/json"}, ), ) print("filtered_group_stats", filtered_group_stats) From 1d1dcd151157d4944e35a83f5d1867b7934870d3 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 13 Sep 2024 11:33:41 +0530 Subject: [PATCH 041/101] Fix: Partner stats: request body format --- backend/services/mapswipe_service.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 16bea6ebf1..73c4f1bb9e 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -1,3 +1,4 @@ +import json from backend.models.dtos.partner_stats_dto import ( GroupedPartnerStatsDTO, FilteredPartnerStatsDTO, @@ -127,10 +128,12 @@ def fetch_grouped_partner_stats( ) -> GroupedPartnerStatsDTO: group_stats = requests.post( MAPSWIPE_API_URL, - data=self.__build_query_user_group_stats(group_id, limit, offset), headers={"Content-Type": "application/json"}, + data=json.dumps( + self.__build_query_user_group_stats(group_id, limit, offset) + ), ) - print("group_stats", group_stats) + print("group_stats", group_stats.text) grouped_parter_stats_dto = GroupedPartnerStatsDTO() grouped_parter_stats_dto.provider = "mapswipe" @@ -144,9 +147,11 @@ def fetch_filtered_partner_stats( ) -> FilteredPartnerStatsDTO: filtered_group_stats = requests.post( MAPSWIPE_API_URL, - data=self.__build_query_filtered_user_group_stats( - group_id, from_date, to_date headers={"Content-Type": "application/json"}, + data=json.dumps( + self.__build_query_filtered_user_group_stats( + group_id, from_date, to_date + ) ), ) print("filtered_group_stats", filtered_group_stats) From 3d21e5cfdfa24bb43dd132d4ca1473b33ce02742 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 13 Sep 2024 12:00:01 +0530 Subject: [PATCH 042/101] Fix: Move users to GroupPartnerStatisticsDTO --- backend/models/dtos/partner_stats_dto.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index 287cf9ee01..2551a0fa73 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -74,13 +74,14 @@ class GroupedPartnerStatsDTO(Model): name_inside_provider: StringType(serialized_name="nameInsideProvider") total_members: IntType(serialized_name="totalMembers") + users = ModelType(UserMembershipsDTO) + # General stats of partner total_contributors: IntType(serialized_name="totalContributors") total_constributions: IntType(serialized_name="totalconstributions") total_constribution_time: IntType(serialized_name="totalconstributionTime") # Recent contributions during the last 1 month - total_recent_contributors: IntType(serialized_name="totalRecentContributors") total_recent_constributions: IntType(serialized_name="totalRecentconstributions") total_recent_constribution_time: IntType( @@ -95,7 +96,6 @@ class FilteredPartnerStatsDTO(Model): provider: StringType() id_inside_provider: StringType(serialized_name="idInsideProvider") name_inside_provider: StringType(serialized_name="nameInsideProvider") - users = ModelType(UserMembershipsDTO) from_date = UTCDateTimeType(serialized_name="fromDate") to_date = UTCDateTimeType(serialized_name="toDate") From 0951da4f1bd0f3212b4d5cf370c05ef86a95a5a6 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 13 Sep 2024 13:44:30 +0530 Subject: [PATCH 043/101] Fix: DTO fields and refactor grouped partner statistics API --- backend/api/partners/statistics.py | 4 +- backend/models/dtos/partner_stats_dto.py | 83 ++++++++++++------------ backend/services/mapswipe_service.py | 63 +++++++++++++++--- 3 files changed, 95 insertions(+), 55 deletions(-) diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index 981ad92e96..7ae5c6090e 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -142,5 +142,5 @@ def get(self, partner_id): offset = request.args.get("offset", 0) return mapswipe.fetch_grouped_partner_stats( - partner.mapswipe_group_id, limit, offset - ), 200 + partner.id, partner.mapswipe_group_id, limit, offset + ).to_primitive(), 200 diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index 2551a0fa73..58dcb02d60 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -11,30 +11,25 @@ ) -class MemberDTO(Model): - id: StringType() - user_id: StringType(serialized_name="userId") - username: StringType() - is_active: BooleanType(serialized_name="isActive") +class UserGroupMemberDTO(Model): + id = StringType() + user_id = StringType(serialized_name="userId") + username = StringType() + is_active = BooleanType(serialized_name="isActive") total_mapping_projects = IntType(serialized_name="totalMappingProjects") - total_constribution_time: IntType(serialized_name="totalconstributionTime") - total_constributions: IntType(serialized_name="totalconstributions") - - -class UserMembershipsDTO(Model): - count: IntType() - users: ListType(ModelType(MemberDTO)) + total_contribution_time = IntType(serialized_name="totalcontributionTime") + total_contributions = IntType(serialized_name="totalcontributions") class OrganizationContributionsDTO(Model): organization_name = StringType(serialized_name="organizationName") - total_constributions: IntType(serialized_name="totalconstributions") + total_contributions = IntType(serialized_name="totalcontributions") class UserContributionsDTO(Model): total_mapping_projects = IntType(serialized_name="totalMappingProjects") - total_constribution_time: IntType(serialized_name="totalconstributionTime") - total_constributions: IntType(serialized_name="totalconstributions") + total_contribution_time: IntType(serialized_name="totalcontributionTime") + total_contributions: IntType(serialized_name="totalcontributions") username = StringType() user_id = StringType(serialized_name="userId") @@ -46,76 +41,78 @@ class GeojsonDTO(Model): class GeoContributionsDTO(Model): geojson = ModelType(GeojsonDTO) - total_constributions = IntType(serialized_name="totalconstributions") + total_contributions = IntType(serialized_name="totalcontributions") class ContributionsByDateDTO(Model): task_date = StringType(serialized_name="taskDate") - total_constributions = IntType(serialized_name="totalconstributions") + total_contributions = IntType(serialized_name="totalcontributions") class ContributionTimeByDateDTO(Model): date = StringType(serialized_name="date") - total_constribution_time: IntType(serialized_name="totalconstributionTime") + total_contribution_time: IntType(serialized_name="totalcontributionTime") class ContributionsByProjectTypeDTO(Model): project_type = StringType(serialized_name="projectType") project_type_display = StringType(serialized_name="projectTypeDisplay") - total_constributions = IntType(serialized_name="totalconstributions") + total_contributions = IntType(serialized_name="totalcontributions") class GroupedPartnerStatsDTO(Model): """General statistics of a partner and its members.""" - id: LongType() - provider: StringType() - id_inside_provider: StringType(serialized_name="idInsideProvider") - name_inside_provider: StringType(serialized_name="nameInsideProvider") - total_members: IntType(serialized_name="totalMembers") - - users = ModelType(UserMembershipsDTO) + id = LongType() + provider = StringType() + id_inside_provider = StringType(serialized_name="idInsideProvider") + name_inside_provider = StringType(serialized_name="nameInsideProvider") + description_inside_provider = StringType( + serialized_name="descriptionInsideProvider" + ) + members_count = IntType(serialized_name="membersCount") + members = ListType(ModelType(UserGroupMemberDTO)) # General stats of partner - total_contributors: IntType(serialized_name="totalContributors") - total_constributions: IntType(serialized_name="totalconstributions") - total_constribution_time: IntType(serialized_name="totalconstributionTime") + total_contributors = IntType(serialized_name="totalContributors") + total_contributions = IntType(serialized_name="totalcontributions") + total_contribution_time = IntType(serialized_name="totalcontributionTime") # Recent contributions during the last 1 month - total_recent_contributors: IntType(serialized_name="totalRecentContributors") - total_recent_constributions: IntType(serialized_name="totalRecentconstributions") - total_recent_constribution_time: IntType( - serialized_name="totalRecentconstributionTime" + total_recent_contributors = IntType(serialized_name="totalRecentContributors") + total_recent_contributions = IntType(serialized_name="totalRecentcontributions") + total_recent_contribution_time = IntType( + serialized_name="totalRecentcontributionTime" ) class FilteredPartnerStatsDTO(Model): """Statistics of a partner contributions filtered by time range.""" - id: LongType() - provider: StringType() - id_inside_provider: StringType(serialized_name="idInsideProvider") - name_inside_provider: StringType(serialized_name="nameInsideProvider") + id = LongType() + provider = StringType() + id_inside_provider = StringType(serialized_name="idInsideProvider") + name_inside_provider = StringType(serialized_name="nameInsideProvider") from_date = UTCDateTimeType(serialized_name="fromDate") to_date = UTCDateTimeType(serialized_name="toDate") - constributions_by_user = ListType( + contributions_by_user = ListType( ModelType(UserContributionsDTO), serialized_name="contributionsByUser" ) - constributions_by_geo = ListType( + contributions_by_geo = ListType( ModelType(GeoContributionsDTO), serialized_name="contributionsByGeo" ) - constributions_by_project_type = ListType( + contributions_by_project_type = ListType( ModelType(ContributionsByProjectTypeDTO), serialized_name="contributionsByProjectType", ) - constributions_by_date = ListType( + contributions_by_date = ListType( ModelType(ContributionsByDateDTO), serialized_name="contributionsByDate" ) - constributions_by_organization_name = ListType( + contributions_by_organization_name = ListType( ModelType(OrganizationContributionsDTO), serialized_name="contributionsByorganizationName", ) - constribution_time_by_date = ListType( + contribution_time_by_date = ListType( ModelType(ContributionTimeByDateDTO), serialized_name="contributionTimeByDate" ) diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 73c4f1bb9e..87179eeaea 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -2,6 +2,7 @@ from backend.models.dtos.partner_stats_dto import ( GroupedPartnerStatsDTO, FilteredPartnerStatsDTO, + UserGroupMemberDTO, ) from cachetools import TTLCache, cached import requests @@ -123,23 +124,65 @@ def __build_query_filtered_user_group_stats( return {"operationName": operationName, "query": query, "variables": variables} @cached(grouped_partner_stats_cache) + def setup_group_dto( + self, partner_id: str, group_id: str, resp_body: str + ) -> GroupedPartnerStatsDTO: + group_stats = json.loads(resp_body)["data"] + group_dto = GroupedPartnerStatsDTO() + group_dto.id = partner_id + group_dto.provider = "mapswipe" + group_dto.id_inside_provider = group_id + group_dto.name_inside_provider = group_stats["userGroup"]["name"] + group_dto.description_inside_provider = group_stats["userGroup"]["description"] + + group_dto.members_count = group_stats["userGroup"]["userMemberships"]["count"] + group_dto.members = [] + for user_resp in group_stats["userGroup"]["userMemberships"]["items"]: + user = UserGroupMemberDTO() + user.id = user_resp["id"] + user.is_active = user_resp["isActive"] + user.user_id = user_resp["userId"] + user.username = user_resp["username"] + user.total_contributions = user_resp["totalSwipes"] + user.total_contribution_time = user_resp["totalSwipeTime"] + user.total_mapping_projects = user_resp["totalMappingProjects"] + group_dto.members.append(user) + + group_dto.total_contributors = group_stats["userGroupStats"]["stats"][ + "totalContributors" + ] + group_dto.total_contributions = group_stats["userGroupStats"]["stats"][ + "totalSwipes" + ] + group_dto.total_contribution_time = group_stats["userGroupStats"]["stats"][ + "totalSwipeTime" + ] + group_dto.total_recent_contributors = group_stats["userGroupStats"][ + "statsLatest" + ]["totalContributors"] + group_dto.total_recent_contributions = group_stats["userGroupStats"][ + "statsLatest" + ]["totalSwipes"] + group_dto.total_recent_contribution_time = group_stats["userGroupStats"][ + "statsLatest" + ]["totalSwipeTime"] + + print("final group dto", group_dto.total_contributors) + return group_dto + def fetch_grouped_partner_stats( - self, group_id: str, limit: int, offset: int + self, partner_id: int, group_id: str, limit: int = 10, offset: int = 0 ) -> GroupedPartnerStatsDTO: - group_stats = requests.post( + """Service to fetch user group statistics by each member with pagination""" + + resp_body = requests.post( MAPSWIPE_API_URL, headers={"Content-Type": "application/json"}, data=json.dumps( self.__build_query_user_group_stats(group_id, limit, offset) ), - ) - print("group_stats", group_stats.text) - - grouped_parter_stats_dto = GroupedPartnerStatsDTO() - grouped_parter_stats_dto.provider = "mapswipe" - grouped_parter_stats_dto.id_inside_provider = group_id - - return grouped_parter_stats_dto + ).text + return self.setup_group_dto(partner_id, group_id, resp_body) @cached(filtered_partner_stats_cache) def fetch_filtered_partner_stats( From 30963daf8e5312e1605d8c6a9b7d0ff0cceb81a8 Mon Sep 17 00:00:00 2001 From: bshankar Date: Mon, 16 Sep 2024 19:37:32 +0530 Subject: [PATCH 044/101] Fix: Partner statistics API limit should be int --- backend/api/partners/statistics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index 7ae5c6090e..c249de349f 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -138,8 +138,8 @@ def get(self, partner_id): mapswipe = MapswipeService() partner = PartnerService.get_partner_by_id(partner_id) - limit = request.args.get("limit", 10) - offset = request.args.get("offset", 0) + limit = int(request.args.get("limit", 10)) + offset = int(request.args.get("offset", 0)) return mapswipe.fetch_grouped_partner_stats( partner.id, partner.mapswipe_group_id, limit, offset From 4d7d2a2a6f13c6ef17e074bfbbe69ccb052aa8f9 Mon Sep 17 00:00:00 2001 From: bshankar Date: Tue, 17 Sep 2024 09:00:57 +0530 Subject: [PATCH 045/101] Add API to download grouped statistics as CSV --- backend/api/partners/statistics.py | 28 +++++++++++++++++++++--- backend/models/dtos/partner_stats_dto.py | 5 +++++ backend/services/mapswipe_service.py | 19 ++++++++++++---- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index c249de349f..ddeaacc130 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -1,3 +1,5 @@ +import io +from flask import send_file from flask_restful import Resource, request from backend.services.users.authentication_service import token_auth @@ -112,6 +114,11 @@ def get(self, partner_id): description: The starting index from which to fetch partner members type: integer example: 0 + - in: query + name: downloadAsCSV + description: Download users in this group as CSV + type: boolean + example: false - name: partner_id in: path description: The id of the partner @@ -140,7 +147,22 @@ def get(self, partner_id): partner = PartnerService.get_partner_by_id(partner_id) limit = int(request.args.get("limit", 10)) offset = int(request.args.get("offset", 0)) + downloadAsCSV = bool(request.args.get("downloadAsCSV", "false") == "true") + + group_dto = mapswipe.fetch_grouped_partner_stats( + partner.id, + partner.mapswipe_group_id, + limit, + offset, + downloadAsCSV, + ) + + if downloadAsCSV: + return send_file( + io.BytesIO(group_dto.to_csv().encode()), + mimetype="text/csv", + as_attachment=True, + download_name="partner_members.csv", + ) - return mapswipe.fetch_grouped_partner_stats( - partner.id, partner.mapswipe_group_id, limit, offset - ).to_primitive(), 200 + return group_dto.to_primitive(), 200 diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index 58dcb02d60..02fa191b3b 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -1,3 +1,4 @@ +import pandas as pd from schematics import Model from schematics.types import ( StringType, @@ -85,6 +86,10 @@ class GroupedPartnerStatsDTO(Model): serialized_name="totalRecentcontributionTime" ) + def to_csv(self): + df = pd.json_normalize(self.to_primitive()["members"]) + return df.to_csv(index=False) + class FilteredPartnerStatsDTO(Model): """Statistics of a partner contributions filtered by time range.""" diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 87179eeaea..06198c27ed 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -123,7 +123,6 @@ def __build_query_filtered_user_group_stats( variables = {"fromDate": from_date, "toDate": to_date, "pk": group_id} return {"operationName": operationName, "query": query, "variables": variables} - @cached(grouped_partner_stats_cache) def setup_group_dto( self, partner_id: str, group_id: str, resp_body: str ) -> GroupedPartnerStatsDTO: @@ -170,11 +169,21 @@ def setup_group_dto( print("final group dto", group_dto.total_contributors) return group_dto + # @cached(grouped_partner_stats_cache) def fetch_grouped_partner_stats( - self, partner_id: int, group_id: str, limit: int = 10, offset: int = 0 + self, + partner_id: int, + group_id: str, + limit: int, + offset: int, + downloadAsCSV: bool, ) -> GroupedPartnerStatsDTO: """Service to fetch user group statistics by each member with pagination""" + if downloadAsCSV: + limit = 1_000_000 + offset = 0 + resp_body = requests.post( MAPSWIPE_API_URL, headers={"Content-Type": "application/json"}, @@ -182,9 +191,11 @@ def fetch_grouped_partner_stats( self.__build_query_user_group_stats(group_id, limit, offset) ), ).text - return self.setup_group_dto(partner_id, group_id, resp_body) - @cached(filtered_partner_stats_cache) + group_dto = self.setup_group_dto(partner_id, group_id, resp_body) + return group_dto + + # @cached(filtered_partner_stats_cache) def fetch_filtered_partner_stats( self, group_id: str, from_date: Optional[str], to_date: Optional[str] ) -> FilteredPartnerStatsDTO: From 14b3465ad8432762e230caa513c0d110eae64c75 Mon Sep 17 00:00:00 2001 From: bshankar Date: Tue, 17 Sep 2024 09:11:07 +0530 Subject: [PATCH 046/101] Rename partner stats API -> general-statistics --- backend/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/__init__.py b/backend/__init__.py index 567af8ff02..5d927d5ae3 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -598,7 +598,7 @@ def add_api_endpoints(app): ) api.add_resource( GroupPartnerStatisticsAPI, - format_url("/partners//group-statistics"), + format_url("/partners//general-statistics"), methods=["GET"], ) api.add_resource( From 4a3297d804037a930a5b5d9a63134ce179e76c2b Mon Sep 17 00:00:00 2001 From: bshankar Date: Tue, 17 Sep 2024 15:08:10 +0530 Subject: [PATCH 047/101] Implement API to fetch filtered statistics over time range --- backend/api/partners/statistics.py | 20 +++- backend/models/dtos/partner_stats_dto.py | 22 ++-- backend/services/mapswipe_service.py | 128 +++++++++++++++++++++-- 3 files changed, 150 insertions(+), 20 deletions(-) diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index ddeaacc130..0d70dcba8b 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -68,7 +68,23 @@ def get(self, partner_id): from_date = request.args.get("fromDate") to_date = request.args.get("toDate") - if from_date is not None and to_date is not None and from_date > to_date: + if from_date is None: + raise BadRequest( + sub_code="INVALID_TIME_RANGE", + message="fromDate is missing", + from_date=from_date, + to_date=to_date, + ) + + if to_date is None: + raise BadRequest( + sub_code="INVALID_TIME_RANGE", + message="toDate is missing", + from_date=from_date, + to_date=to_date, + ) + + if from_date > to_date: raise BadRequest( sub_code="INVALID_TIME_RANGE", message="fromDate should be less than toDate", @@ -80,7 +96,7 @@ def get(self, partner_id): return ( mapswipe.fetch_filtered_partner_stats( - partner.mapswipe_group_id, from_date, to_date + partner.id, partner.mapswipe_group_id, from_date, to_date ).to_primitive(), 200, ) diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index 02fa191b3b..74162b0a7f 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -5,7 +5,6 @@ LongType, IntType, ListType, - UTCDateTimeType, ModelType, FloatType, BooleanType, @@ -29,8 +28,8 @@ class OrganizationContributionsDTO(Model): class UserContributionsDTO(Model): total_mapping_projects = IntType(serialized_name="totalMappingProjects") - total_contribution_time: IntType(serialized_name="totalcontributionTime") - total_contributions: IntType(serialized_name="totalcontributions") + total_contribution_time = IntType(serialized_name="totalcontributionTime") + total_contributions = IntType(serialized_name="totalcontributions") username = StringType() user_id = StringType(serialized_name="userId") @@ -52,7 +51,7 @@ class ContributionsByDateDTO(Model): class ContributionTimeByDateDTO(Model): date = StringType(serialized_name="date") - total_contribution_time: IntType(serialized_name="totalcontributionTime") + total_contribution_time = IntType(serialized_name="totalcontributionTime") class ContributionsByProjectTypeDTO(Model): @@ -61,6 +60,12 @@ class ContributionsByProjectTypeDTO(Model): total_contributions = IntType(serialized_name="totalcontributions") +class AreaSwipedByProjectTypeDTO(Model): + total_area = FloatType(serialized_name="totalArea") + project_type = StringType(serialized_name="projectType") + project_type_display = StringType(serialized_name="projectTypeDisplay") + + class GroupedPartnerStatsDTO(Model): """General statistics of a partner and its members.""" @@ -97,16 +102,19 @@ class FilteredPartnerStatsDTO(Model): id = LongType() provider = StringType() id_inside_provider = StringType(serialized_name="idInsideProvider") - name_inside_provider = StringType(serialized_name="nameInsideProvider") - from_date = UTCDateTimeType(serialized_name="fromDate") - to_date = UTCDateTimeType(serialized_name="toDate") + from_date = StringType(serialized_name="fromDate") + to_date = StringType(serialized_name="toDate") contributions_by_user = ListType( ModelType(UserContributionsDTO), serialized_name="contributionsByUser" ) contributions_by_geo = ListType( ModelType(GeoContributionsDTO), serialized_name="contributionsByGeo" ) + area_swiped_by_project_type = ListType( + ModelType(AreaSwipedByProjectTypeDTO), serialized_name="areaSwipedByProjectType" + ) + contributions_by_project_type = ListType( ModelType(ContributionsByProjectTypeDTO), serialized_name="contributionsByProjectType", diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 06198c27ed..506c6c3273 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -3,10 +3,17 @@ GroupedPartnerStatsDTO, FilteredPartnerStatsDTO, UserGroupMemberDTO, + UserContributionsDTO, + GeojsonDTO, + GeoContributionsDTO, + AreaSwipedByProjectTypeDTO, + ContributionsByDateDTO, + ContributionTimeByDateDTO, + ContributionsByProjectTypeDTO, + OrganizationContributionsDTO, ) from cachetools import TTLCache, cached import requests -from typing import Optional grouped_partner_stats_cache = TTLCache(maxsize=128, ttl=60 * 60 * 24) filtered_partner_stats_cache = TTLCache(maxsize=128, ttl=60 * 60 * 24) @@ -64,7 +71,7 @@ def __build_query_user_group_stats(group_id: str, limit: int, offset: int): return {"operationName": operationName, "query": query, "variables": variables} def __build_query_filtered_user_group_stats( - group_id: str, from_date: str, to_date: str + self, group_id: str, from_date: str, to_date: str ): """A private method to build a graphQl query for fetching a user group's stats within a timerange from Mapswipe.""" @@ -166,9 +173,106 @@ def setup_group_dto( "statsLatest" ]["totalSwipeTime"] - print("final group dto", group_dto.total_contributors) return group_dto + @staticmethod + def setup_filtered_dto( + partner_id: str, + group_id: str, + from_date: str, + to_date: str, + resp_body: str, + ): + filtered_stats_dto = FilteredPartnerStatsDTO() + filtered_stats_dto.id = partner_id + filtered_stats_dto.provider = "mapswipe" + filtered_stats_dto.id_inside_provider = group_id + filtered_stats_dto.from_date = from_date + filtered_stats_dto.to_date = to_date + + filtered_stats = json.loads(resp_body)["data"]["userGroupStats"][ + "filteredStats" + ] + + stats_by_user = [] + for user_stats in filtered_stats["userStats"]: + user_contributions = UserContributionsDTO() + user_contributions.user_id = user_stats["userId"] + user_contributions.username = user_stats["username"] + user_contributions.total_contributions = user_stats["totalSwipes"] + user_contributions.total_contribution_time = user_stats["totalSwipeTime"] + user_contributions.total_mapping_projects = user_stats[ + "totalMappingProjects" + ] + stats_by_user.append(user_contributions) + filtered_stats_dto.contributions_by_user = stats_by_user + + contributions_by_geo = [] + for geo_stats in filtered_stats["contributionByGeo"]: + geo_contributions = GeoContributionsDTO() + geo_contributions.total_contributions = geo_stats["totalContribution"] + geojson = GeojsonDTO() + geojson.type = geo_stats["geojson"]["type"] + geojson.coordinates = geo_stats["geojson"]["coordinates"] + geo_contributions.geojson = geojson + contributions_by_geo.append(geo_contributions) + filtered_stats_dto.contributions_by_geo = contributions_by_geo + + areas_swiped = [] + for area_swiped in filtered_stats["areaSwipedByProjectType"]: + area_swiped_by_project_type = AreaSwipedByProjectTypeDTO() + area_swiped_by_project_type.project_type = area_swiped["projectType"] + area_swiped_by_project_type.project_type_display = area_swiped[ + "projectTypeDisplay" + ] + area_swiped_by_project_type.total_area = area_swiped["totalArea"] + areas_swiped.append(area_swiped_by_project_type) + filtered_stats_dto.area_swiped_by_project_type = areas_swiped + + dates = [] + for by_date in filtered_stats["swipeByDate"]: + contributions_by_date = ContributionsByDateDTO() + contributions_by_date.task_date = by_date["taskDate"] + contributions_by_date.total_contributions = by_date["totalSwipes"] + dates.append(contributions_by_date) + filtered_stats_dto.contributions_by_date = dates + + dates = [] + for by_date in filtered_stats["swipeTimeByDate"]: + contribution_time_by_date = ContributionTimeByDateDTO() + contribution_time_by_date.date = by_date["date"] + contribution_time_by_date.total_contribution_time = by_date[ + "totalSwipeTime" + ] + dates.append(contribution_time_by_date) + filtered_stats_dto.contribution_time_by_date = dates + + project_types = [] + for project_type in filtered_stats["swipeByProjectType"]: + contributions_by_project_type = ContributionsByProjectTypeDTO() + contributions_by_project_type.project_type = project_type["projectType"] + contributions_by_project_type.project_type_display = project_type[ + "projectTypeDisplay" + ] + contributions_by_project_type.total_contributions = project_type[ + "totalSwipes" + ] + project_types.append(contributions_by_project_type) + filtered_stats_dto.contributions_by_project_type = project_types + + organizations = [] + for organization in filtered_stats["swipeByOrganizationName"]: + contributions_by_organization_name = OrganizationContributionsDTO() + contributions_by_organization_name.organization_name = organization[ + "organizationName" + ] + contributions_by_organization_name.total_contributions = organization[ + "totalSwipes" + ] + organizations.append(contributions_by_organization_name) + filtered_stats_dto.contributions_by_organization_name = organizations + return filtered_stats_dto + # @cached(grouped_partner_stats_cache) def fetch_grouped_partner_stats( self, @@ -197,9 +301,13 @@ def fetch_grouped_partner_stats( # @cached(filtered_partner_stats_cache) def fetch_filtered_partner_stats( - self, group_id: str, from_date: Optional[str], to_date: Optional[str] + self, + partner_id: str, + group_id: str, + from_date: str, + to_date: str, ) -> FilteredPartnerStatsDTO: - filtered_group_stats = requests.post( + resp = requests.post( MAPSWIPE_API_URL, headers={"Content-Type": "application/json"}, data=json.dumps( @@ -208,10 +316,8 @@ def fetch_filtered_partner_stats( ) ), ) - print("filtered_group_stats", filtered_group_stats) - filtered_parter_stats_dto = FilteredPartnerStatsDTO() - filtered_parter_stats_dto.provider = "mapswipe" - filtered_parter_stats_dto.id_inside_provider = group_id - - return filtered_parter_stats_dto + filtered_dto = self.setup_filtered_dto( + partner_id, group_id, from_date, to_date, resp.text + ) + return filtered_dto From 49c1b8fc577fd0be182b1d34e44c2de09e05f31d Mon Sep 17 00:00:00 2001 From: bshankar Date: Wed, 18 Sep 2024 13:33:57 +0530 Subject: [PATCH 048/101] Enable caching mapswipe requests --- backend/services/mapswipe_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 506c6c3273..543c6af832 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -273,7 +273,7 @@ def setup_filtered_dto( filtered_stats_dto.contributions_by_organization_name = organizations return filtered_stats_dto - # @cached(grouped_partner_stats_cache) + @cached(grouped_partner_stats_cache) def fetch_grouped_partner_stats( self, partner_id: int, @@ -299,7 +299,7 @@ def fetch_grouped_partner_stats( group_dto = self.setup_group_dto(partner_id, group_id, resp_body) return group_dto - # @cached(filtered_partner_stats_cache) + @cached(filtered_partner_stats_cache) def fetch_filtered_partner_stats( self, partner_id: str, From 68d79c8ec5b7575c3d7ce283625b4c4c18bc1a3c Mon Sep 17 00:00:00 2001 From: bshankar Date: Wed, 18 Sep 2024 13:55:55 +0530 Subject: [PATCH 049/101] Fix: flake8 line too long error --- backend/services/mapswipe_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 543c6af832..68aa4b87cc 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -73,7 +73,7 @@ def __build_query_user_group_stats(group_id: str, limit: int, offset: int): def __build_query_filtered_user_group_stats( self, group_id: str, from_date: str, to_date: str ): - """A private method to build a graphQl query for fetching a user group's stats within a timerange from Mapswipe.""" + """A private method to build a graphQl query to fetch a mapswipe group's stats within a timerange.""" operationName = "FilteredUserGroupStats" query = """ From 0333dae6fb6ab055f4cdfd80eddaddbc7ca0bbde Mon Sep 17 00:00:00 2001 From: bshankar Date: Wed, 18 Sep 2024 19:56:24 +0530 Subject: [PATCH 050/101] Fix: Handle API partner stats api call without group id --- backend/api/partners/statistics.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index 0d70dcba8b..175b7db2e5 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -1,6 +1,8 @@ import io from flask import send_file from flask_restful import Resource, request +from typing import Optional + from backend.services.users.authentication_service import token_auth from backend.models.postgis.user import User @@ -12,6 +14,13 @@ # fetch_partner_stats(id_inside_service, from_date, to_date) -> PartnerStatsDTO from backend.services.mapswipe_service import MapswipeService +MAPSWIPE_GROUP_EMPTY_SUBCODE = "EMPTY_MAPSWIPE_GROUP" +MAPSWIPE_GROUP_EMPTY_MESSAGE = "Mapswipe group is not set for this partner." + + +def is_valid_group_id(group_id: Optional[str]) -> bool: + return group_id is not None and len(group_id) > 0 + class FilteredPartnerStatisticsAPI(Resource): @token_auth.login_required @@ -94,6 +103,12 @@ def get(self, partner_id): partner = PartnerService.get_partner_by_id(partner_id) + if not is_valid_group_id(partner.mapswipe_group_id): + raise BadRequest( + sub_code=MAPSWIPE_GROUP_EMPTY_SUBCODE, + message=MAPSWIPE_GROUP_EMPTY_MESSAGE, + ) + return ( mapswipe.fetch_filtered_partner_stats( partner.id, partner.mapswipe_group_id, from_date, to_date @@ -161,6 +176,13 @@ def get(self, partner_id): mapswipe = MapswipeService() partner = PartnerService.get_partner_by_id(partner_id) + + if not is_valid_group_id(partner.mapswipe_group_id): + raise BadRequest( + sub_code=MAPSWIPE_GROUP_EMPTY_SUBCODE, + message=MAPSWIPE_GROUP_EMPTY_MESSAGE, + ) + limit = int(request.args.get("limit", 10)) offset = int(request.args.get("offset", 0)) downloadAsCSV = bool(request.args.get("downloadAsCSV", "false") == "true") From 345e14a42a70ceb01fbe95d3c3b6de2b813265b1 Mon Sep 17 00:00:00 2001 From: bshankar Date: Thu, 19 Sep 2024 10:04:25 +0530 Subject: [PATCH 051/101] Fix: Remove token auth for general, filtered partner stats --- backend/api/partners/statistics.py | 32 ------------------------------ 1 file changed, 32 deletions(-) diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index 175b7db2e5..15b221e9f0 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -4,10 +4,7 @@ from typing import Optional -from backend.services.users.authentication_service import token_auth -from backend.models.postgis.user import User from backend.services.partner_service import PartnerService -from backend.services.users.user_service import UserRole from backend.exceptions import BadRequest # Replaceable by another service which implements the method: @@ -23,7 +20,6 @@ def is_valid_group_id(group_id: Optional[str]) -> bool: class FilteredPartnerStatisticsAPI(Resource): - @token_auth.login_required def get(self, partner_id): """ Get partner statistics by id and time range @@ -33,12 +29,6 @@ def get(self, partner_id): produces: - application/json parameters: - - in: header - name: Authorization - description: Base64 encoded session token - required: true - type: string - default: Token sessionTokenHere== - in: query name: fromDate type: string @@ -65,14 +55,6 @@ def get(self, partner_id): 500: description: Internal Server Error """ - - request_user = User.get_by_id(token_auth.current_user()) - if request_user.role != UserRole.ADMIN.value: - return { - "Error": "Only admin users can manage partners.", - "SubCode": "OnlyAdminAccess", - }, 403 - mapswipe = MapswipeService() from_date = request.args.get("fromDate") to_date = request.args.get("toDate") @@ -118,7 +100,6 @@ def get(self, partner_id): class GroupPartnerStatisticsAPI(Resource): - @token_auth.login_required def get(self, partner_id): """ Get partner statistics by id and broken down by each contributor. @@ -129,12 +110,6 @@ def get(self, partner_id): produces: - application/json parameters: - - in: header - name: Authorization - description: Base64 encoded session token - required: true - type: string - default: Token sessionTokenHere== - in: query name: limit description: The number of partner members to fetch @@ -167,13 +142,6 @@ def get(self, partner_id): description: Internal Server Error """ - request_user = User.get_by_id(token_auth.current_user()) - if request_user.role != UserRole.ADMIN.value: - return { - "Error": "Only admin users can manage partners.", - "SubCode": "OnlyAdminAccess", - }, 403 - mapswipe = MapswipeService() partner = PartnerService.get_partner_by_id(partner_id) From 0dae7840ef9b80d9f61dfdab5755840584161295 Mon Sep 17 00:00:00 2001 From: bshankar Date: Thu, 19 Sep 2024 15:19:57 +0530 Subject: [PATCH 052/101] Use permalink instead of id for partner statistics --- backend/__init__.py | 4 ++-- backend/api/partners/statistics.py | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/backend/__init__.py b/backend/__init__.py index 5d927d5ae3..4e032534ae 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -598,12 +598,12 @@ def add_api_endpoints(app): ) api.add_resource( GroupPartnerStatisticsAPI, - format_url("/partners//general-statistics"), + format_url("/partners//general-statistics"), methods=["GET"], ) api.add_resource( FilteredPartnerStatisticsAPI, - format_url("/partners//filtered-statistics"), + format_url("/partners//filtered-statistics"), methods=["GET"], ) api.add_resource( diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index 15b221e9f0..e0f2ae973c 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -20,7 +20,7 @@ def is_valid_group_id(group_id: Optional[str]) -> bool: class FilteredPartnerStatisticsAPI(Resource): - def get(self, partner_id): + def get(self, permalink: str): """ Get partner statistics by id and time range --- @@ -41,10 +41,11 @@ def get(self, partner_id): description: Fetch partner statistics to date as yyyy-mm-dd - name: partner_id in: path - description: The id of the partner + - name: permalink + in: path + description: The permalink of the partner required: true - type: integer - default: 1 + type: string responses: 200: description: Partner found @@ -83,7 +84,7 @@ def get(self, partner_id): to_date=to_date, ) - partner = PartnerService.get_partner_by_id(partner_id) + partner = PartnerService.get_partner_by_permalink(permalink) if not is_valid_group_id(partner.mapswipe_group_id): raise BadRequest( @@ -100,7 +101,7 @@ def get(self, partner_id): class GroupPartnerStatisticsAPI(Resource): - def get(self, partner_id): + def get(self, permalink: str): """ Get partner statistics by id and broken down by each contributor. This API is paginated with limit and offset query parameters. @@ -125,12 +126,11 @@ def get(self, partner_id): description: Download users in this group as CSV type: boolean example: false - - name: partner_id + - name: permalink in: path - description: The id of the partner + description: The permalink of the partner required: true - type: integer - default: 1 + type: string responses: 200: description: Partner found @@ -143,7 +143,7 @@ def get(self, partner_id): """ mapswipe = MapswipeService() - partner = PartnerService.get_partner_by_id(partner_id) + partner = PartnerService.get_partner_by_permalink(permalink) if not is_valid_group_id(partner.mapswipe_group_id): raise BadRequest( From c27ecbc1547ab362c788a8d7665dfa297c585000 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 19 Sep 2024 19:19:44 +0530 Subject: [PATCH 053/101] Add zoom minus svg icon --- frontend/src/components/svgIcons/index.js | 1 + frontend/src/components/svgIcons/zoomMinus.js | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 frontend/src/components/svgIcons/zoomMinus.js diff --git a/frontend/src/components/svgIcons/index.js b/frontend/src/components/svgIcons/index.js index 9809e2ad2f..42ab0ff074 100644 --- a/frontend/src/components/svgIcons/index.js +++ b/frontend/src/components/svgIcons/index.js @@ -70,6 +70,7 @@ export { EnvelopeIcon } from './envelope'; export { LinkedinIcon } from './linkedin'; export { MarkerIcon } from './marker'; export { ZoomPlusIcon } from './zoomPlus'; +export { ZoomMinusIcon } from './zoomMinus'; export { SidebarIcon } from './sidebar'; export { QuestionCircleIcon } from './questionCircle'; export { ChartLineIcon } from './chart'; diff --git a/frontend/src/components/svgIcons/zoomMinus.js b/frontend/src/components/svgIcons/zoomMinus.js new file mode 100644 index 0000000000..86628fd29b --- /dev/null +++ b/frontend/src/components/svgIcons/zoomMinus.js @@ -0,0 +1,21 @@ +import { PureComponent } from 'react'; + +export class ZoomMinusIcon extends PureComponent { + render() { + return ( + + + + + + ); + } +} From 05a3c02c31fc00950a391cb57ee758a0737b444b Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 19 Sep 2024 19:20:33 +0530 Subject: [PATCH 054/101] Update colours and handle warnings --- .../timeSpentContributing.js | 47 +++++++++++-------- .../timeSpentContributingByDay.js | 4 +- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js index 9ca9009438..36937ea3fc 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js @@ -4,7 +4,6 @@ import { Chart } from 'chart.js/auto'; import 'chartjs-adapter-date-fns'; import { CHART_COLOURS } from '../../config'; -import { Dropdown } from '../dropdown'; import messages from './messages'; const MOCK_DATA = [ @@ -30,8 +29,7 @@ export const TimeSpentContributing = () => { const context = chartRef.current.getContext('2d'); // Create gradient for the area const gradient = context.createLinearGradient(0, 0, 0, 450); - gradient.addColorStop(0, CHART_COLOURS.red); - gradient.addColorStop(0.4, CHART_COLOURS.red); + gradient.addColorStop(0, `rgba(215, 63, 63, 0.5)`); gradient.addColorStop(1, 'rgba(215, 63, 63, 0)'); if (!chartInstance.current) { @@ -43,6 +41,8 @@ export const TimeSpentContributing = () => { { label: 'Time Spent', backgroundColor: gradient, + borderColor: CHART_COLOURS.red, + borderWidth: 1.5, data: MOCK_DATA.map((entry) => entry.minutesSpent), fill: true, tension: 0.4, @@ -58,6 +58,7 @@ export const TimeSpentContributing = () => { chartInstance.current.destroy(); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { @@ -65,6 +66,7 @@ export const TimeSpentContributing = () => { chartInstance.current.options = getChartOptions(); chartInstance.current.update(); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [chartDistribution]); const getChartOptions = () => { @@ -129,29 +131,34 @@ export const TimeSpentContributing = () => { }; }; - const dropdownOptions = [ - { - label: , - value: 'day', - }, - { - label: , - value: 'month', - }, - ]; - return (

- setChartDistribution(options[0].value)} - options={dropdownOptions} - value={chartDistribution} - className="ba b--grey-light bg-white mr1 v-mid pv2 ph2" - /> +
+ + +
diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js index 9841908fa4..a8555d3fb8 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js @@ -31,7 +31,9 @@ export const TimeSpentContributingByDay = () => { datasets: [ { label: 'Time Spent', - backgroundColor: CHART_COLOURS.red, + backgroundColor: `rgba(215, 63, 63, 0.4)`, + borderWidth: 2, + borderColor: CHART_COLOURS.red, data: MOCK_DATA.map((entry) => entry.y), }, ], From 92860d3b558e576d80169d72bd1d7b3b832aa944 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 19 Sep 2024 19:22:56 +0530 Subject: [PATCH 055/101] feat: add zoom handler icons, update zoom behaviour, and update styling --- .../contributionsHeatmap.css | 6 ++ .../contributionsHeatmap.js | 80 +++++++++++++++---- 2 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 frontend/src/components/partnerMapswipeStats/contributionsHeatmap.css diff --git a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.css b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.css new file mode 100644 index 0000000000..4c4873401f --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.css @@ -0,0 +1,6 @@ +.partner-mapswipe-heatmap-zoom-text { + visibility: hidden; +} +.partner-mapswipe-heatmap-wrapper:hover .partner-mapswipe-heatmap-zoom-text { + visibility: visible; +} diff --git a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js index 2bbd593b71..44e046e5fc 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js +++ b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js @@ -7,15 +7,15 @@ import 'mapbox-gl/dist/mapbox-gl.css'; import { MAPBOX_TOKEN, MAP_STYLE } from '../../config'; import { CHART_COLOURS } from '../../config'; import { geoJSON } from './geoJsonData'; +import { ZoomPlusIcon, ZoomMinusIcon } from '../svgIcons'; import messages from './messages'; +import './contributionsHeatmap.css'; mapboxgl.accessToken = MAPBOX_TOKEN; export const ContributionsHeatmap = () => { const mapContainer = useRef(null); const map = useRef(null); - const [lng, setLng] = useState(0); - const [lat, setLat] = useState(0); const [zoom, setZoom] = useState(1.25); useEffect(() => { @@ -24,29 +24,29 @@ export const ContributionsHeatmap = () => { map.current = new mapboxgl.Map({ container: mapContainer.current, style: MAP_STYLE, - center: [lng, lat], + center: [0, 0], zoom: zoom, - bearing: 0, - pitch: 0, }); + map.current.scrollZoom.disable(); + const getStyle = (row) => { const styles = [ { - color: CHART_COLOURS.orange, + color: CHART_COLOURS.red, opacity: 0.2, }, { - color: CHART_COLOURS.orange, - opacity: 0.4, + color: CHART_COLOURS.red, + opacity: 0.3, }, { - color: CHART_COLOURS.orange, - opacity: 0.6, + color: CHART_COLOURS.red, + opacity: 0.4, }, { - color: CHART_COLOURS.orange, - opacity: 0.7, + color: CHART_COLOURS.red, + opacity: 0.6, }, { color: CHART_COLOURS.red, @@ -128,20 +128,66 @@ export const ContributionsHeatmap = () => { }); }); - map.current.on('move', () => { - setLng(map.current.getCenter().lng.toFixed(4)); - setLat(map.current.getCenter().lat.toFixed(4)); - setZoom(map.current.getZoom().toFixed(2)); + map.current.on('wheel', (event) => { + if (event.originalEvent.ctrlKey) { + // Check if CTRL key is pressed + event.originalEvent.preventDefault(); // Prevent chrome/firefox default behavior + if (!map.current.scrollZoom._enabled) map.current.scrollZoom.enable(); // Enable zoom only if it's disabled + } else { + if (map.current.scrollZoom._enabled) map.current.scrollZoom.disable(); // Disable zoom only if it's enabled + } }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + map.current.zoomTo(zoom, { duration: 700 }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [zoom]); + + const handleZoom = (isZoomingOut = false) => { + const currentZoom = parseFloat(zoom); + if (!isZoomingOut) { + setZoom(currentZoom + 0.5); + } else { + if (currentZoom.toFixed(2) === '0.75') return; + setZoom(currentZoom - 0.5); + } + }; + + const shouldDisableZoomOut = zoom === '0.75' || zoom === 0.75; + return (

-
+
+
+
+
+ handleZoom(true)} + /> + handleZoom()} + /> +
+

+ Use Ctrl + Scroll to zoom +

+
+
); }; From 8887ad99fc9a971e51127a7819429acd00a54b1d Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 19 Sep 2024 19:23:49 +0530 Subject: [PATCH 056/101] Add mapswipe tab --- frontend/src/views/partnersStats.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/partnersStats.js b/frontend/src/views/partnersStats.js index 5673160345..0d255b9411 100644 --- a/frontend/src/views/partnersStats.js +++ b/frontend/src/views/partnersStats.js @@ -7,6 +7,7 @@ import messages from './messages'; import { NotFound } from './notFound'; import { useFetch } from '../hooks/UseFetch'; import { Leaderboard } from '../components/partners/leaderboard'; +import { PartnersMapswipeStats } from './partnersMapswipeStats'; import { Resources } from '../components/partners/partnersResources'; import { OHSOME_STATS_BASE_URL } from '../config'; import { Button } from '../components/button'; @@ -26,7 +27,10 @@ function getSocialIcons(link) { } } -const tabData = [{ id: 'leaderboard', title: 'Leaderboard' }]; +const tabData = [ + { id: 'leaderboard', title: 'Leaderboard' }, + { id: 'mapswipe', title: 'Map Swipe' }, +]; export const PartnersStats = () => { const { id, tabname } = useParams(); @@ -71,6 +75,8 @@ export const PartnersStats = () => { switch (tabname) { case 'leaderboard': return ; + case 'mapswipe': + return ; default: return <>; } From 43ac9188f646951626aa72d5ac1d3366fe3aeb5e Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 20 Sep 2024 02:14:58 +0530 Subject: [PATCH 057/101] feat: integrate general statistics API, handle loading and empty state --- .../partnerMapswipeStats/messages.js | 12 ++ .../partnerMapswipeStats/overview.js | 151 +++++++++++++----- 2 files changed, 125 insertions(+), 38 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index c8dfdf753d..c18c91c3ef 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -8,14 +8,26 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.totalSwipes', defaultMessage: 'Total Swipes', }, + recentTotalSwipesText: { + id: 'management.partners.stats.mapswipe.totalSwipes.recentText', + defaultMessage: 'swipes in the last 30 days', + }, totalTimeSpent: { id: 'management.partners.stats.mapswipe.totalTimeSpent', defaultMessage: 'Total Time Spent', }, + recentTotalTimeSpentText: { + id: 'management.partners.stats.mapswipe.totalTimeSpent.recentText', + defaultMessage: 'in the last 30 days', + }, totalContributors: { id: 'management.partners.stats.mapswipe.totalContributors', defaultMessage: 'Total Contributors', }, + recentTotalContributorsText: { + id: 'management.partners.stats.mapswipe.totalContributors.recentText', + defaultMessage: 'active contributors in the last 30 days', + }, groupMembers: { id: 'management.partners.stats.mapswipe.groupMembers', defaultMessage: 'Group Members', diff --git a/frontend/src/components/partnerMapswipeStats/overview.js b/frontend/src/components/partnerMapswipeStats/overview.js index 85e7c8d0dd..4308c7ad94 100644 --- a/frontend/src/components/partnerMapswipeStats/overview.js +++ b/frontend/src/components/partnerMapswipeStats/overview.js @@ -1,51 +1,126 @@ -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, FormattedNumber } from 'react-intl'; +import { useParams } from 'react-router-dom'; +import { useQuery } from '@tanstack/react-query'; +import ReactPlaceholder from 'react-placeholder'; +import shortNumber from 'short-number'; +import { intervalToDuration } from 'date-fns'; import messages from './messages'; import { StatsCardWithFooter } from '../statsCard'; import { MappingIcon, SwipeIcon, ClockIcon } from '../svgIcons'; +import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; const iconClass = 'w-100'; const iconStyle = { height: '55px' }; +const OverviewPlaceholder = () => ( +
+ + + +
+); export const Overview = () => { + const { id: partnerPermalink } = useParams(); + + const { isLoading, data, isRefetching } = useQuery({ + queryKey: ['partners-mapswipe-general-statistics', partnerPermalink], + queryFn: async () => { + const response = await fetchLocalJSONAPI( + `partners/${partnerPermalink}/general-statistics/?limit=0&offset=0`, + ); + return response; + }, + }); + + const getShortNumber = (value) => { + const shortNumberValue = shortNumber(value); + + return typeof shortNumberValue === 'number' ? ( + + ) : ( + + + {shortNumberValue.substr(-1)} + + ); + }; + + const formatSecondsToTwoUnits = (seconds) => { + const duration = intervalToDuration({ start: 0, end: seconds * 1000 }); + const units = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds']; + + // Filter units that have a value greater than 0 + const nonZeroUnits = units.filter((unit) => duration[unit] > 0).slice(0, 2); + + return nonZeroUnits + .map((unit) => `${duration[unit]} ${duration[unit] === 1 ? unit.slice(0, -1) : unit}`) + .join(' '); + }; + return ( -
} + ready={!isLoading && !isRefetching} > - } - description={} - value={'1 M'} - delta={ - - 66k swipes in the last 30 days - - } - className="w-100" - /> - } - description={} - value={'1 Mo 2 Days'} - delta={ - - 2 days 15 hours in the last 30 days - - } - className="w-100" - /> - } - description={} - value={'407'} - delta={ - - 33 active contributors in the last 30 days - - } - className="w-100" - /> -
+
+ } + description={} + value={data?.totalcontributions ? getShortNumber(data.totalcontributions) : '-'} + delta={ + data?.totalRecentcontributions ? ( + + {getShortNumber(data.totalRecentcontributions)}{' '} + + + ) : ( + '--' + ) + } + className="w-100" + /> + } + description={} + value={ + data?.totalcontributionTime ? formatSecondsToTwoUnits(data.totalcontributionTime) : '-' + } + delta={ + data?.totalRecentcontributionTime ? ( + + {formatSecondsToTwoUnits(data.totalRecentcontributionTime)}{' '} + + + ) : ( + '--' + ) + } + className="w-100" + /> + } + description={} + value={data?.totalContributors ? getShortNumber(data.totalContributors) : '-'} + delta={ + data?.totalRecentContributors ? ( + + {getShortNumber(data.totalRecentContributors)}{' '} + + + ) : ( + '--' + ) + } + className="w-100" + /> +
+ ); }; From ac1db7c6ce2f85e5e268d10b59b1ff678014dee9 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 20 Sep 2024 02:15:46 +0530 Subject: [PATCH 058/101] feat: add API call to filtered statistics --- frontend/src/views/partnersMapswipeStats.js | 143 ++++++++++++-------- 1 file changed, 84 insertions(+), 59 deletions(-) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 5d375a62df..b0bbd0bdd5 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -1,5 +1,7 @@ import { FormattedMessage } from 'react-intl'; +import { useParams } from 'react-router-dom'; import ReactPlaceholder from 'react-placeholder'; +import { useQuery } from '@tanstack/react-query'; import { InfoIcon } from '../components/svgIcons'; import { Overview } from '../components/partnerMapswipeStats/overview'; @@ -14,20 +16,21 @@ import { SwipesByOrganisation } from '../components/partnerMapswipeStats/swipesB import { StatsCardWithFooter } from '../components/statsCard'; import messages from './messages'; import { BanIcon } from '../components/svgIcons'; +import { fetchLocalJSONAPI } from '../network/genericJSONRequest'; import './partnersMapswipeStats.css'; const PagePlaceholder = () => ( -
- -
+ {/* */} + {/*
-
-
+
*/} +
{ }; export const PartnersMapswipeStats = () => { - const isPageReady = true; - const isError = false; + const { id: partnerPermalink } = useParams(); + const { isLoading, isError, data, isRefetching } = useQuery({ + queryKey: ['partners-mapswipe-filtered-statistics', partnerPermalink], + queryFn: async () => { + const today = new Date(); + const currentYear = today.getFullYear(); + + const formatDate = (date) => { + const offset = date.getTimezoneOffset(); + const adjustedDate = new Date(date.getTime() - offset * 60 * 1000); + return adjustedDate.toISOString().split('T')[0]; + }; + + const fromDate = formatDate(new Date(currentYear, 0, 1)); + const endDate = formatDate(today); + + const response = await fetchLocalJSONAPI( + `partners/${partnerPermalink}/filtered-statistics/?fromDate=${fromDate}&toDate=${endDate}`, + ); + return response; + }, + }); return ( - } ready={isPageReady}> - {isPageReady && isError ? ( -
-
- -

- -

-
-
- ) : ( -
- - - -
- -
+
+ + -
- + } ready={!isLoading && !isRefetching}> + {!isLoading && isError ? ( +
+
+ +

+ +

+
+ ) : ( + <> +
+ +
-
- -
+
+ +
-
- -
+
+ +
-
- -
+
+ +
-
- } - value="338K" - style={{ width: '48.5%' }} - /> - } - value="11 days 5 hrs" - className="w-100" - style={{ width: '48.5%' }} - /> -
+
+ +
-
- - -
+
+ } + value="338K" + style={{ width: '48.5%' }} + /> + } + value="11 days 5 hrs" + className="w-100" + style={{ width: '48.5%' }} + /> +
-
- -
-
- )} - +
+ + +
+ +
+ +
+ + )} + +
); }; From 97afe9ca35f941fb410411f5f0413ca4e2d64871 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 20 Sep 2024 02:50:52 +0530 Subject: [PATCH 059/101] feat: integrate API data for ContributionsGrid component --- .../partnerMapswipeStats/contributionsGrid.js | 33 ++++++++++++------- .../partnerMapswipeStats/messages.js | 4 +-- frontend/src/views/partnersMapswipeStats.js | 2 +- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/contributionsGrid.js b/frontend/src/components/partnerMapswipeStats/contributionsGrid.js index 9b7f220bba..2525a0bc6a 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsGrid.js +++ b/frontend/src/components/partnerMapswipeStats/contributionsGrid.js @@ -24,17 +24,28 @@ const Legend = () => { ); }; -export const ContributionsGrid = () => { +export const ContributionsGrid = ({ contributionsByDate = [] }) => { + contributionsByDate = contributionsByDate.map((contribution) => ({ + date: contribution.taskDate, + count: contribution.totalcontributions, + })); const intl = useIntl(); - const today = new Date(); - const shiftDate = (date, numDays) => { - const newDate = new Date(date); - newDate.setDate(newDate.getDate() + numDays); - return newDate; + const getDate = (isEndDate = false) => { + const today = new Date(); + const currentYear = today.getFullYear(); + + const formatDate = (date) => { + const offset = date.getTimezoneOffset(); + return new Date(date.getTime() - offset * 60 * 1000); + }; + + return !isEndDate + ? formatDate(new Date(currentYear - 1, 11, 31)) + : formatDate(new Date(currentYear, 11, 31)); }; - const countValues = [].map((r) => r.count); + const countValues = contributionsByDate.map((contribution) => contribution.count); const maxValue = Math.max(...countValues); const getHeatmapClass = (value) => { @@ -65,9 +76,9 @@ export const ContributionsGrid = () => {
{ if (!value) return 'fill-tan'; return getHeatmapClass(value); @@ -78,7 +89,7 @@ export const ContributionsGrid = () => { if (value.count !== null) { tooltipContent = `${value.count} ${intl.formatMessage( messages.contributionsGridTooltip, - )}`; + )} on ${value.date}`; } return { diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index c18c91c3ef..72775a29d7 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -70,11 +70,11 @@ export default defineMessages({ }, contributionsGridEmpty: { id: 'management.partners.stats.mapswipe.contributions.empty', - defaultMessage: 'No contribution', + defaultMessage: 'No swipes', }, contributionsGridTooltip: { id: 'management.partners.stats.mapswipe.contributions.tooltip', - defaultMessage: 'contribution(s)', + defaultMessage: 'swipes', }, contributionsHeatmap: { id: 'management.partners.stats.mapswipe.contributions.heatmap', diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index b0bbd0bdd5..5a788ac723 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -104,7 +104,7 @@ export const PartnersMapswipeStats = () => { ) : ( <>
- +
From fc571410f654cc144adb5a9969b5d58a54245fdb Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 20 Sep 2024 14:04:58 +0530 Subject: [PATCH 060/101] feat: integrate API data with heatmap component, handle hex res change, add proptypes --- .../partnerMapswipeStats/contributionsGrid.js | 10 + .../contributionsHeatmap.js | 71 +- .../partnerMapswipeStats/geoJsonData.js | 954 ------------------ frontend/src/views/partnersMapswipeStats.js | 2 +- 4 files changed, 75 insertions(+), 962 deletions(-) delete mode 100644 frontend/src/components/partnerMapswipeStats/geoJsonData.js diff --git a/frontend/src/components/partnerMapswipeStats/contributionsGrid.js b/frontend/src/components/partnerMapswipeStats/contributionsGrid.js index 2525a0bc6a..2b8f4e4a9d 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsGrid.js +++ b/frontend/src/components/partnerMapswipeStats/contributionsGrid.js @@ -1,6 +1,7 @@ import CalendarHeatmap from 'react-calendar-heatmap'; import { Tooltip } from 'react-tooltip'; import { FormattedMessage, useIntl } from 'react-intl'; +import PropTypes from 'prop-types'; import messages from './messages'; @@ -105,3 +106,12 @@ export const ContributionsGrid = ({ contributionsByDate = [] }) => {
); }; + +ContributionsGrid.propTypes = { + contributionsByDate: PropTypes.arrayOf( + PropTypes.shape({ + taskDate: PropTypes.string, + totalcontributions: PropTypes.number, + }), + ), +}; diff --git a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js index 44e046e5fc..31b1c3245d 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js +++ b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js @@ -2,18 +2,18 @@ import React, { useState, useEffect, useRef } from 'react'; import mapboxgl from 'mapbox-gl'; import { latLngToCell, cellToBoundary } from 'h3-js'; import { FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; import 'mapbox-gl/dist/mapbox-gl.css'; import { MAPBOX_TOKEN, MAP_STYLE } from '../../config'; import { CHART_COLOURS } from '../../config'; -import { geoJSON } from './geoJsonData'; import { ZoomPlusIcon, ZoomMinusIcon } from '../svgIcons'; import messages from './messages'; import './contributionsHeatmap.css'; mapboxgl.accessToken = MAPBOX_TOKEN; -export const ContributionsHeatmap = () => { +export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => { const mapContainer = useRef(null); const map = useRef(null); const [zoom, setZoom] = useState(1.25); @@ -73,16 +73,16 @@ export const ContributionsHeatmap = () => { return styles[4]; }; - map.current.on('load', () => { - const hexagonsArrayCopy = geoJSON.map((data) => { - const h3Index = latLngToCell(data.geojson.coordinates[1], data.geojson.coordinates[0], 1); + const getHexagonFeatures = (res = 1) => { + const hexagonsArray = contributionsByGeo.map((data) => { + const h3Index = latLngToCell(data.geojson.coordinates[1], data.geojson.coordinates[0], res); return { hexindex7: h3Index, totalContributionCount: data.totalContribution, }; }); - const features = hexagonsArrayCopy.map((row) => { + const features = hexagonsArray.map((row) => { const style = getStyle(row); return { type: 'Feature', @@ -98,11 +98,42 @@ export const ContributionsHeatmap = () => { }; }); + return features; + }; + + const zoomToH3ResMapping = { + 1: 1, + 2: 2, + 3: 2, + 4: 3, + 5: 3, + 6: 4, + 7: 4, + 8: 5, + 9: 6, + 10: 6, + 11: 7, + 12: 7, + 13: 8, + 14: 8, + 15: 9, + 16: 10, + 17: 10, + 18: 11, + 19: 12, + 20: 13, + 21: 13, + 22: 14, + 23: 15, + 24: 15, + }; + + map.current.on('load', () => { map.current.addSource('hexbin', { type: 'geojson', data: { type: 'FeatureCollection', - features: features, + features: getHexagonFeatures(), }, }); @@ -137,6 +168,20 @@ export const ContributionsHeatmap = () => { if (map.current.scrollZoom._enabled) map.current.scrollZoom.disable(); // Disable zoom only if it's enabled } }); + + map.current.on('zoomend', () => { + const currentZoom = map.current.getZoom(); + console.log(currentZoom, 'ZOOM'); + const h3ResBasedOnZoom = + currentZoom >= 1 + ? zoomToH3ResMapping[parseInt(currentZoom)] ?? Math.floor((currentZoom - 2) * 0.7) + : 1; + + map.current.getSource('hexbin').setData({ + type: 'FeatureCollection', + features: getHexagonFeatures(h3ResBasedOnZoom), + }); + }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -191,3 +236,15 @@ export const ContributionsHeatmap = () => {
); }; + +ContributionsHeatmap.propTypes = { + contributionsByGeo: PropTypes.arrayOf( + PropTypes.shape({ + totalcontributions: PropTypes.number, + geojson: PropTypes.shape({ + type: PropTypes.string, + coordinates: PropTypes.array, + }), + }), + ), +}; diff --git a/frontend/src/components/partnerMapswipeStats/geoJsonData.js b/frontend/src/components/partnerMapswipeStats/geoJsonData.js deleted file mode 100644 index 7e9f102e79..0000000000 --- a/frontend/src/components/partnerMapswipeStats/geoJsonData.js +++ /dev/null @@ -1,954 +0,0 @@ -export const geoJSON = [ - { - geojson: { - type: 'Point', - coordinates: [-85.29066571046852, 15.599525977892466], - }, - totalContribution: 958, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [17.83145975288232, 8.330671974117966], - }, - totalContribution: 5311, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-111.49991715114375, 32.90960177086646], - }, - totalContribution: 256, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [37.55277057498438, -15.428707695616959], - }, - totalContribution: 54, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-72.64747093368038, -1.361696978303415], - }, - totalContribution: 167, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-86.95286078124859, 12.834213274005663], - }, - totalContribution: 1710, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-87.0792935843515, 12.511719153924345], - }, - totalContribution: 758, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-84.77268253283326, 11.546148902370424], - }, - totalContribution: 136, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-78.55328208504923, -5.007382183145697], - }, - totalContribution: 41, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-79.59815175170993, -4.091432430493313], - }, - totalContribution: 1114, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [31.195582690216785, -16.276487330903482], - }, - totalContribution: 120, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-78.0547666676697, -2.825082939722951], - }, - totalContribution: 212, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-71.89545587098073, 1.298110173544335], - }, - totalContribution: 51, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [31.0755952578275, -16.621924168016108], - }, - totalContribution: 78, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [17.068156756986493, 9.501727907988265], - }, - totalContribution: 570, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-113.0291745899052, 35.429619930966155], - }, - totalContribution: 61199, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-85.45517170072486, 13.999002771878763], - }, - totalContribution: 3, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-88.29635175, 18.5011917], - }, - totalContribution: 259, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-73.10396834864834, 2.539712405290437], - }, - totalContribution: 117, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-114.56995240824085, 35.40558885777537], - }, - totalContribution: 242, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-74.77129309897693, -8.361615584031053], - }, - totalContribution: 48, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [21.692822242845754, 32.623341974086216], - }, - totalContribution: 33, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [31.693173872400514, -19.26958190745124], - }, - totalContribution: 109, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-114.56995242452524, 35.40558885232169], - }, - totalContribution: 5571, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-72.66913820889279, 1.188377298969117], - }, - totalContribution: 46, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [20.373907647044145, 32.00262018240991], - }, - totalContribution: 206, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-61.68237405622985, -3.810160941839017], - }, - totalContribution: 38, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [61.866132251827956, 34.546275583949544], - }, - totalContribution: 1156, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-71.17855963901002, -0.791600935042186], - }, - totalContribution: 146, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-76.71958133476728, 0.565003176010218], - }, - totalContribution: 49, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [105.16145765, -3.9464773], - }, - totalContribution: 555, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-10.65602055, 8.5784377], - }, - totalContribution: 27935, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [144.6266440884106, -5.548789486185929], - }, - totalContribution: 48, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [142.75920024697723, -3.870628843450433], - }, - totalContribution: 403, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [17.585299305735855, 8.174124394871342], - }, - totalContribution: 2015, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-76.2028327, -13.72738145], - }, - totalContribution: 37, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-73.45838763797263, 3.212288633510766], - }, - totalContribution: 56, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-84.27666033170857, 13.972490901048117], - }, - totalContribution: 17053, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [30.627553545784952, 8.588993868708355], - }, - totalContribution: 377, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-79.13114579286535, -5.11558990014555], - }, - totalContribution: 334, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-87.54121653976195, 12.907307117412893], - }, - totalContribution: 2, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [105.18227995, -3.9473116], - }, - totalContribution: 2960, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-72.52089048670294, 1.926544828731387], - }, - totalContribution: 978, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-85.5371335786593, 13.576605227349209], - }, - totalContribution: 109, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-80.22439239999999, -3.48025665], - }, - totalContribution: 74, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-78.2610077889491, -3.214116548452895], - }, - totalContribution: 1, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [90.00115255, 23.60059035], - }, - totalContribution: 666, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-91.7208309784723, 18.672273479275766], - }, - totalContribution: 96, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [22.666039189078656, 32.503308146559135], - }, - totalContribution: 13754, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-84.93675766914332, 13.902270671817577], - }, - totalContribution: 297, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-68.41429488863824, -10.658810636419465], - }, - totalContribution: 132, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [32.562545708705656, -20.24707721355555], - }, - totalContribution: 142, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [92.95309353117739, 22.9696207456173], - }, - totalContribution: 42, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-88.53803291049154, 19.554317362356283], - }, - totalContribution: 558, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [17.345159766864796, 8.091350806289423], - }, - totalContribution: 1917, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-85.1198964514573, 13.980393189284156], - }, - totalContribution: 1172, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-75.61141649329109, 1.343481984982563], - }, - totalContribution: 118, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [45.18387664443346, -24.739395546597734], - }, - totalContribution: 43, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [37.85053622258006, 1.091773186351989], - }, - totalContribution: 457, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [109.35353605000002, -7.57011055], - }, - totalContribution: 37, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [35.48272144960324, -14.415911510882315], - }, - totalContribution: 88, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [89.43787645, 23.59917345], - }, - totalContribution: 2906, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [85.14000895, 28.002809], - }, - totalContribution: 148, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-112.24826370962738, 32.909856628282334], - }, - totalContribution: 21, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-84.87894222594483, 14.11428636095636], - }, - totalContribution: 3666, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [22.81168331557454, 14.019015919841559], - }, - totalContribution: 2774, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [68.7095207013628, 36.26334834780437], - }, - totalContribution: 51, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [33.7946453, -13.97105845], - }, - totalContribution: 74, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [89.34596785, 23.7691698], - }, - totalContribution: 1406, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [32.097337697983995, -19.672736743975427], - }, - totalContribution: 102, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-113.79979149730327, 35.42005980032755], - }, - totalContribution: 12497, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [143.1167797438696, -4.656960795757978], - }, - totalContribution: 60, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-79.06916177979166, -3.034005965493468], - }, - totalContribution: 231, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [89.241669, 23.66517035], - }, - totalContribution: 76518, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [31.1615097276057, 0.777047095590563], - }, - totalContribution: 95, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-77.47483008962396, -2.671800082849541], - }, - totalContribution: 918, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [22.625851548038135, 12.668992345921207], - }, - totalContribution: 110, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-85.12586422532074, 14.627692488182635], - }, - totalContribution: 9442, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-67.77638302730661, 2.318842316135067], - }, - totalContribution: 104, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-84.91410579371339, 14.353417306945618], - }, - totalContribution: 3400, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-87.20881001860016, 12.75327604966244], - }, - totalContribution: 3530, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [37.77650094469254, -15.869041578395171], - }, - totalContribution: 142, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [27.225786837706476, -7.74059249702745], - }, - totalContribution: 43, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-83.52420997372839, 14.245248145874273], - }, - totalContribution: 157, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-100.82986828280633, 19.335069136307755], - }, - totalContribution: 34, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [17.855939639879555, 8.78452825808433], - }, - totalContribution: 864, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [82.37118483115022, 29.61829835532351], - }, - totalContribution: 456, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [142.69498962645142, -4.279295885667389], - }, - totalContribution: 283, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-76.08583221934292, 1.188235729693674], - }, - totalContribution: 115, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-78.03111022033067, -1.030023448569005], - }, - totalContribution: 1451, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [105.18473205, -3.9566782], - }, - totalContribution: 111, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [143.58635692996836, -4.368596348999677], - }, - totalContribution: 170, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-84.22885167703019, 13.533698812518912], - }, - totalContribution: 10996, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [93.22530083821907, 23.447192435994232], - }, - totalContribution: 53, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [32.755421668802896, -17.93530847459356], - }, - totalContribution: 199, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [81.75186187109553, 29.19518691339494], - }, - totalContribution: 2567, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [21.0308920603301, 32.49925302210381], - }, - totalContribution: 830, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [93.1666453156887, 23.6544015985086], - }, - totalContribution: 77, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-112.25566386169581, 34.80325861626855], - }, - totalContribution: 378, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-77.34633979411231, -3.761121621878996], - }, - totalContribution: 41, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [22.73889139426033, 14.534553645345042], - }, - totalContribution: 8003, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [35.03770801825091, -15.771260184591872], - }, - totalContribution: 1364, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-79.19674088255486, -3.256832333960287], - }, - totalContribution: 151, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [120.38377675, 16.83994845], - }, - totalContribution: 16, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-111.49691306212117, 33.54079445034419], - }, - totalContribution: 308, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [18.074765184412374, 8.257902650895652], - }, - totalContribution: 4038, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [35.06200485, -15.783931], - }, - totalContribution: 74, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-91.35947451373393, 18.3314215788839], - }, - totalContribution: 10344, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-114.54959912741137, 34.77524490124089], - }, - totalContribution: 317, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [17.585299305735855, 8.174124394871342], - }, - totalContribution: 2430, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-74.94386755939892, -9.567978301900402], - }, - totalContribution: 263, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-111.48731874510736, 35.43398171764529], - }, - totalContribution: 18925, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-74.81170044558343, -8.920617444439028], - }, - totalContribution: 158, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-73.38392646574127, 2.966663019116976], - }, - totalContribution: 226, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [39.138121600783734, -15.124347352480978], - }, - totalContribution: 378, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [22.888767367107956, 13.445056045099104], - }, - totalContribution: 201, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-91.3592784, 15.7464449], - }, - totalContribution: 484, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [-73.701759296725, 2.967536670595839], - }, - totalContribution: 58, - __typename: 'MapContributionStatsType', - }, - { - geojson: { - type: 'Point', - coordinates: [89.6161154, 23.5335064], - }, - totalContribution: 37, - __typename: 'MapContributionStatsType', - }, -]; diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 5a788ac723..1b0ca45338 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -108,7 +108,7 @@ export const PartnersMapswipeStats = () => {
- +
From d2f7d65111512f333a22be769bfd2fd239fe1580 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 20 Sep 2024 14:17:01 +0530 Subject: [PATCH 061/101] [WIP] Integrate API data into group members table --- .../partnerMapswipeStats/groupMembers.js | 92 +++++++------------ .../groupMembersPlaceholder.js | 19 ++++ 2 files changed, 52 insertions(+), 59 deletions(-) create mode 100644 frontend/src/components/partnerMapswipeStats/groupMembersPlaceholder.js diff --git a/frontend/src/components/partnerMapswipeStats/groupMembers.js b/frontend/src/components/partnerMapswipeStats/groupMembers.js index f17d017579..77c7ad18ff 100644 --- a/frontend/src/components/partnerMapswipeStats/groupMembers.js +++ b/frontend/src/components/partnerMapswipeStats/groupMembers.js @@ -1,59 +1,20 @@ import { FormattedMessage } from 'react-intl'; import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table'; +import { useParams } from 'react-router-dom'; +import { useQuery } from '@tanstack/react-query'; +import { useState } from 'react'; +import ReactPlaceholder from 'react-placeholder'; +import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; import { BanIcon, CircleExclamationIcon } from '../svgIcons'; import { PaginatorLine } from '../paginator'; import messages from './messages'; +import { GroupMembersPlaceholder } from './groupMembersPlaceholder'; import './groupMembers.css'; -const MOCK_DATA = [ - { - user: 'map4life', - totalSwipes: 12023, - projectContributed: 'American Red Cross', - timeSpent: '2 Months', - }, - { - user: 'PinkSky234', - totalSwipes: 11984, - projectContributed: 'American Red Cross', - timeSpent: '1 Month', - }, - { - user: 'swiper55', - totalSwipes: 3540, - projectContributed: 'HOT', - timeSpent: '3 Weeks', - }, - { - user: 'edelweiss93', - totalSwipes: 1234, - projectContributed: 'American Red Cross', - timeSpent: '1 Week', - }, - { - user: 'mappy45', - totalSwipes: 842, - projectContributed: 'HOT', - timeSpent: '2 Weeks', - }, - { - user: 'sup3rMap', - totalSwipes: 325, - projectContributed: 'American Red Cross', - timeSpent: '5 Days', - }, - { - user: '4weeksmapping', - totalSwipes: 74, - projectContributed: 'HOT', - timeSpent: '1 Day', - }, -]; - const COLUMNS = [ { - accessorKey: 'user', + accessorKey: 'username', header: () => ( @@ -61,7 +22,7 @@ const COLUMNS = [ ), }, { - accessorKey: 'totalSwipes', + accessorKey: 'totalcontributions', header: () => ( @@ -69,7 +30,7 @@ const COLUMNS = [ ), }, { - accessorKey: 'projectContributed', + accessorKey: 'totalMappingProjects', header: () => ( @@ -77,7 +38,7 @@ const COLUMNS = [ ), }, { - accessorKey: 'timeSpent', + accessorKey: 'totalcontributionTime', header: () => ( @@ -87,19 +48,33 @@ const COLUMNS = [ ]; export const GroupMembers = () => { + const { id: partnerPermalink } = useParams(); + + const ROWS = 10; + const [pageNumber, setPageNumber] = useState(0) + + const { isLoading, isError, data, isRefetching } = useQuery({ + queryKey: ['partners-mapswipe-general-statistics', partnerPermalink, pageNumber], + queryFn: async () => { + const response = await fetchLocalJSONAPI( + `partners/${partnerPermalink}/general-statistics/?limit=${ROWS}&offset=${pageNumber * ROWS}`, + ); + return response; + }, + }); + const table = useReactTable({ columns: COLUMNS, - data: MOCK_DATA, + data: data?.members ?? [], getCoreRowModel: getCoreRowModel(), columnResizeMode: 'onChange', columnResizeDirection: 'ltr', }); const isEmpty = false; - const isError = false; return ( -
+ } ready={!isLoading && !isRefetching}>

@@ -126,9 +101,8 @@ export const GroupMembers = () => { onDoubleClick: () => header.column.resetSize(), onMouseDown: header.getResizeHandler(), onTouchStart: header.getResizeHandler(), - className: `partner-mapswipe-stats-column-resizer ${ - table.options.columnResizeDirection === 'ltr' ? 'right-0' : '' - } ${header.column.getIsResizing() ? 'isResizing' : ''}`, + className: `partner-mapswipe-stats-column-resizer ${table.options.columnResizeDirection === 'ltr' ? 'right-0' : '' + } ${header.column.getIsResizing() ? 'isResizing' : ''}`, }} /> )} @@ -181,13 +155,13 @@ export const GroupMembers = () => {
{}} - lastPage={1} + activePage={pageNumber + 1} + setPageFn={(n) => setPageNumber(parseInt(n) - 1)} + lastPage={Math.max(Math.ceil((data?.membersCount ?? 0) / ROWS), 1)} className="flex items-center justify-center pa4" />
-
+
); }; diff --git a/frontend/src/components/partnerMapswipeStats/groupMembersPlaceholder.js b/frontend/src/components/partnerMapswipeStats/groupMembersPlaceholder.js new file mode 100644 index 0000000000..df6b4dbe37 --- /dev/null +++ b/frontend/src/components/partnerMapswipeStats/groupMembersPlaceholder.js @@ -0,0 +1,19 @@ +import { FormattedMessage } from 'react-intl'; +import ReactPlaceholder from 'react-placeholder'; +import { TextRow } from 'react-placeholder/lib/placeholders'; + +import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; +import { BanIcon, CircleExclamationIcon } from '../svgIcons'; +import { PaginatorLine } from '../paginator'; +import messages from './messages'; + +export const GroupMembersPlaceholder = () => { + return ( +
+

+ +

+ Loading ... +
+ ) +} From 9ce197b4368ece775f92eca65c57286ec5f29385 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 20 Sep 2024 14:38:04 +0530 Subject: [PATCH 062/101] feat: Integrate API into area swiped pie chart --- .../partnerMapswipeStats/swipesByProjectType.js | 8 ++++---- frontend/src/views/partnersMapswipeStats.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js index d2879ff197..77baece32c 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js @@ -5,7 +5,7 @@ import Chart from 'chart.js/auto'; import { CHART_COLOURS } from '../../config'; import messages from './messages'; -export const SwipesByProjectType = () => { +export const SwipesByProjectType = ({ areaSwipedByProjectType = [] }) => { const chartRef = useRef(null); const chartInstance = useRef(null); @@ -21,14 +21,14 @@ export const SwipesByProjectType = () => { chartInstance.current = new Chart(context, { type: 'doughnut', data: { - labels: ['Find', 'Validate'], + labels: ['Find', 'Compare', 'Validate'], datasets: [ { - data: [75, 25], + data: areaSwipedByProjectType.map(c => c.totalArea), backgroundColor: [ CHART_COLOURS.orange, // Orange for Find - CHART_COLOURS.green, // Green for Validate CHART_COLOURS.blue, // Blue for Compare + CHART_COLOURS.green, // Green for Validate ], borderColor: '#fff', borderWidth: 2, diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 1b0ca45338..ebd5468b86 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -138,7 +138,7 @@ export const PartnersMapswipeStats = () => {
- +
From 13c417ae750dc73c605314fff66e8501c40fa137 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 20 Sep 2024 14:46:54 +0530 Subject: [PATCH 063/101] feat: Integrate API into swipes by organization pie chart --- .../partnerMapswipeStats/swipesByOrganisation.js | 12 +++--------- frontend/src/views/partnersMapswipeStats.js | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js index 83bf275244..a285857439 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js @@ -5,7 +5,7 @@ import Chart from 'chart.js/auto'; import { CHART_COLOURS } from '../../config'; import messages from './messages'; -export const SwipesByOrganisation = () => { +export const SwipesByOrganisation = ( { contributionsByOrganization = [] } ) => { const chartRef = useRef(null); const chartInstance = useRef(null); @@ -21,16 +21,10 @@ export const SwipesByOrganisation = () => { chartInstance.current = new Chart(context, { type: 'doughnut', data: { - labels: [ - 'American Red Cross', - 'Arizona State University', - 'HOT', - 'Médecins Sans Frontières', - 'Others', - ], + labels: contributionsByOrganization.map(c => c.organizationName), datasets: [ { - data: [35, 25, 20, 15, 5], + data: contributionsByOrganization.map(c => c.totalcontributions), backgroundColor: [ CHART_COLOURS.red, // Orange for American Red Cross CHART_COLOURS.orange, // Yellow for Arizona State University diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index ebd5468b86..49ff006cad 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -139,7 +139,7 @@ export const PartnersMapswipeStats = () => {
- +
From 9e3131b2e59fe3ed35cd2984457c0a26b2e8762e Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 20 Sep 2024 15:01:17 +0530 Subject: [PATCH 064/101] Export formatSecondsToTwoUnits and getShortNumber functions --- .../partnerMapswipeStats/overview.js | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/overview.js b/frontend/src/components/partnerMapswipeStats/overview.js index 4308c7ad94..fed989f964 100644 --- a/frontend/src/components/partnerMapswipeStats/overview.js +++ b/frontend/src/components/partnerMapswipeStats/overview.js @@ -23,6 +23,32 @@ const OverviewPlaceholder = () => (
); + +export const formatSecondsToTwoUnits = (seconds) => { + const duration = intervalToDuration({ start: 0, end: seconds * 1000 }); + const units = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds']; + + // Filter units that have a value greater than 0 + const nonZeroUnits = units.filter((unit) => duration[unit] > 0).slice(0, 2); + + return nonZeroUnits + .map((unit) => `${duration[unit]} ${duration[unit] === 1 ? unit.slice(0, -1) : unit}`) + .join(' '); +}; + +export const getShortNumber = (value) => { + const shortNumberValue = shortNumber(value); + + return typeof shortNumberValue === 'number' ? ( + + ) : ( + + + {shortNumberValue.substr(-1)} + + ); +}; + export const Overview = () => { const { id: partnerPermalink } = useParams(); @@ -36,31 +62,6 @@ export const Overview = () => { }, }); - const getShortNumber = (value) => { - const shortNumberValue = shortNumber(value); - - return typeof shortNumberValue === 'number' ? ( - - ) : ( - - - {shortNumberValue.substr(-1)} - - ); - }; - - const formatSecondsToTwoUnits = (seconds) => { - const duration = intervalToDuration({ start: 0, end: seconds * 1000 }); - const units = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds']; - - // Filter units that have a value greater than 0 - const nonZeroUnits = units.filter((unit) => duration[unit] > 0).slice(0, 2); - - return nonZeroUnits - .map((unit) => `${duration[unit]} ${duration[unit] === 1 ? unit.slice(0, -1) : unit}`) - .join(' '); - }; - return ( } From af5489d4d563ee79f1e06d16c0ab556ce7c67bfd Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 20 Sep 2024 18:05:39 +0530 Subject: [PATCH 065/101] Add empty set mathematical icon --- frontend/src/components/svgIcons/emptySet.js | 25 ++++++++++++++++++++ frontend/src/components/svgIcons/index.js | 1 + 2 files changed, 26 insertions(+) create mode 100644 frontend/src/components/svgIcons/emptySet.js diff --git a/frontend/src/components/svgIcons/emptySet.js b/frontend/src/components/svgIcons/emptySet.js new file mode 100644 index 0000000000..4ff8f5d2e8 --- /dev/null +++ b/frontend/src/components/svgIcons/emptySet.js @@ -0,0 +1,25 @@ +import { PureComponent } from 'react'; + +export class EmptySetIcon extends PureComponent { + render() { + return ( + + + + + + + + ); + } +} diff --git a/frontend/src/components/svgIcons/index.js b/frontend/src/components/svgIcons/index.js index 42ab0ff074..58ba46eab7 100644 --- a/frontend/src/components/svgIcons/index.js +++ b/frontend/src/components/svgIcons/index.js @@ -87,3 +87,4 @@ export { CircleMinusIcon } from './circleMinus'; export { CircleExclamationIcon } from './circleExclamation'; export { TableListIcon } from './tableList'; export { SwipeIcon } from './swipe'; +export { EmptySetIcon } from './emptySet'; From da5635e86249d7d05da604c3e11c842a7328ed00 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 20 Sep 2024 18:07:16 +0530 Subject: [PATCH 066/101] feat: integrate API data for time spent contributing and refactor code --- .../timeSpentContributing.js | 159 ++++++++++-------- frontend/src/views/partnersMapswipeStats.js | 2 +- 2 files changed, 86 insertions(+), 75 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js index 36937ea3fc..6e4591e9ed 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js @@ -2,53 +2,47 @@ import { useState, useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import { Chart } from 'chart.js/auto'; import 'chartjs-adapter-date-fns'; +import PropTypes from 'prop-types'; +import { parse, format } from 'date-fns'; import { CHART_COLOURS } from '../../config'; +import { formatSecondsToTwoUnits } from './overview'; +import { EmptySetIcon } from '../svgIcons'; import messages from './messages'; -const MOCK_DATA = [ - { date: '2024-01-02', minutesSpent: 10 }, - { date: '2024-01-29', minutesSpent: 40 }, - { date: '2024-02-26', minutesSpent: 120 }, - { date: '2024-03-26', minutesSpent: 180 }, - { date: '2024-04-24', minutesSpent: 230 }, - { date: '2024-05-23', minutesSpent: 350 }, - { date: '2024-06-21', minutesSpent: 90 }, - { date: '2024-07-18', minutesSpent: 60 }, - { date: '2024-09-06', minutesSpent: 45 }, -]; - -export const TimeSpentContributing = () => { +export const TimeSpentContributing = ({ contributionTimeByDate = [] }) => { const [chartDistribution, setChartDistribution] = useState('day'); // "day" or "month" const chartRef = useRef(null); const chartInstance = useRef(null); + const aggregateTimeSpentContributingByMonth = (data) => { + const aggregatedData = {}; + + data.forEach((item) => { + const date = parse(item.date, 'yyyy-MM-dd', new Date()); + const monthKey = format(date, 'yyyy-MM'); + + if (!aggregatedData[monthKey]) { + aggregatedData[monthKey] = { + date: format(date, 'MMM yyyy'), + totalcontributionTime: 0, + }; + } + + aggregatedData[monthKey].totalcontributionTime += item.totalcontributionTime; + }); + + return Object.values(aggregatedData).sort((a, b) => new Date(a.date) - new Date(b.date)); + }; + useEffect(() => { if (!chartRef.current) return; const context = chartRef.current.getContext('2d'); - // Create gradient for the area - const gradient = context.createLinearGradient(0, 0, 0, 450); - gradient.addColorStop(0, `rgba(215, 63, 63, 0.5)`); - gradient.addColorStop(1, 'rgba(215, 63, 63, 0)'); - if (!chartInstance.current) { chartInstance.current = new Chart(context, { type: 'line', - data: { - labels: MOCK_DATA.map((entry) => entry.date), - datasets: [ - { - label: 'Time Spent', - backgroundColor: gradient, - borderColor: CHART_COLOURS.red, - borderWidth: 1.5, - data: MOCK_DATA.map((entry) => entry.minutesSpent), - fill: true, - tension: 0.4, - }, - ], - }, + data: getChartDataConfig(), options: getChartOptions(), }); } @@ -63,49 +57,64 @@ export const TimeSpentContributing = () => { useEffect(() => { if (chartInstance.current) { + chartInstance.current.data = getChartDataConfig(); chartInstance.current.options = getChartOptions(); chartInstance.current.update(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [chartDistribution]); - const getChartOptions = () => { - const xAxisTime = + const getChartDataConfig = () => { + const context = chartRef.current.getContext('2d'); + // Create gradient for the area + const gradient = context.createLinearGradient(0, 0, 0, 450); + gradient.addColorStop(0, `rgba(215, 63, 63, 0.5)`); + gradient.addColorStop(1, 'rgba(215, 63, 63, 0)'); + + const data = chartDistribution === 'day' - ? { - unit: 'day', - tooltipFormat: 'MMM d, yyyy', - } - : { - unit: 'month', - tooltipFormat: 'MMM, yyyy', - }; + ? contributionTimeByDate + : aggregateTimeSpentContributingByMonth(contributionTimeByDate); + return { + labels: data.map((entry) => entry.date), + datasets: [ + { + label: 'Time Spent', + backgroundColor: gradient, + borderColor: CHART_COLOURS.red, + borderWidth: 1.5, + data: data.map((entry) => entry.totalcontributionTime), + fill: true, + tension: 0.4, + }, + ], + }; + }; + + const getChartOptions = () => { return { responsive: true, maintainAspectRatio: false, legend: { display: false }, scales: { - x: { - type: 'time', - time: xAxisTime, - }, + x: + chartDistribution === 'day' + ? { + type: 'time', + time: { + unit: 'day', + tooltipFormat: 'MMM d, yyyy', + }, + } + : {}, y: { beginAtZero: true, ticks: { callback: function (value) { - const days = Math.floor(value / (24 * 60)); - const hours = Math.floor((value % (24 * 60)) / 60); - const minutes = value % 60; - - let label = ''; - if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; - if (hours > 0 || days > 0) label += `${hours} hr${hours !== 1 ? 's' : ''}`; - if (minutes > 0 && days === 0) label += ` ${minutes} min${minutes !== 1 ? 's' : ''}`; - - return label.trim(); + return formatSecondsToTwoUnits(value); }, - stepSize: 60, + stepSize: 1000, }, }, }, @@ -114,16 +123,7 @@ export const TimeSpentContributing = () => { callbacks: { label: function (context) { const value = context.parsed.y; - const days = Math.floor(value / (24 * 60)); - const hours = Math.floor((value % (24 * 60)) / 60); - const minutes = value % 60; - - let label = 'Time Spent: '; - if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; - if (hours > 0 || days > 0) label += `${hours} hour${hours !== 1 ? 's' : ''} `; - if (minutes > 0) label += `${minutes} minute${minutes !== 1 ? 's' : ''}`; - - return label.trim(); + return formatSecondsToTwoUnits(value); }, }, }, @@ -140,9 +140,7 @@ export const TimeSpentContributing = () => {
-
+
+ {contributionTimeByDate.length === 0 && ( +
+ +

No data found

+
+ )}
); }; + +TimeSpentContributing.propTypes = { + contributionTimeByDate: PropTypes.arrayOf( + PropTypes.shape({ + totalcontributionTime: PropTypes.number, + date: PropTypes.string, + }), + ), +}; diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 49ff006cad..52ece66358 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -112,7 +112,7 @@ export const PartnersMapswipeStats = () => {
- +
From cede94a8395ce04195a06929481d5c5405b33f8c Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 20 Sep 2024 18:29:34 +0530 Subject: [PATCH 067/101] feat: integrate API data with timeSpentContributingByDay bar chart --- .../timeSpentContributing.js | 2 +- .../timeSpentContributingByDay.js | 88 +++++++++++-------- frontend/src/views/partnersMapswipeStats.js | 2 +- 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js index 6e4591e9ed..7029b15355 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js @@ -114,7 +114,7 @@ export const TimeSpentContributing = ({ contributionTimeByDate = [] }) => { callback: function (value) { return formatSecondsToTwoUnits(value); }, - stepSize: 1000, + stepSize: 36 * 60, }, }, }, diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js index a8555d3fb8..62fc00c332 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js @@ -1,21 +1,15 @@ import { useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import { Chart } from 'chart.js/auto'; +import PropTypes from 'prop-types'; +import { parse, getDay } from 'date-fns'; import { CHART_COLOURS } from '../../config'; +import { formatSecondsToTwoUnits } from './overview'; +import { EmptySetIcon } from '../svgIcons'; import messages from './messages'; -const MOCK_DATA = [ - { x: 'Sun', y: 15 * 60 + 16 }, // 15 hours 16 minutes - { x: 'Mon', y: 30 * 60 }, // 1 day 6 hours - { x: 'Tue', y: 45 * 60 }, // 1 day 21 hours - { x: 'Wed', y: 61 * 60 }, // 2 days 13 hours - { x: 'Thu', y: 61 * 60 }, // 2 days 13 hours - { x: 'Fri', y: 45 * 60 }, // 1 day 21 hours - { x: 'Sat', y: 15 * 60 + 16 }, // 15 hours 16 minutes -]; - -export const TimeSpentContributingByDay = () => { +export const TimeSpentContributingByDay = ({ contributionTimeByDate = [] }) => { const chartRef = useRef(null); const chartInstance = useRef(null); @@ -24,6 +18,8 @@ export const TimeSpentContributingByDay = () => { chartInstance.current.destroy(); } + const chartData = aggregateContributionTimeByWeekday(); + chartInstance.current = new Chart(chartRef.current.getContext('2d'), { type: 'bar', data: { @@ -34,7 +30,7 @@ export const TimeSpentContributingByDay = () => { backgroundColor: `rgba(215, 63, 63, 0.4)`, borderWidth: 2, borderColor: CHART_COLOURS.red, - data: MOCK_DATA.map((entry) => entry.y), + data: chartData.map((entry) => entry.totalcontributionTime), }, ], }, @@ -47,19 +43,9 @@ export const TimeSpentContributingByDay = () => { beginAtZero: true, ticks: { callback: function (value) { - const days = Math.floor(value / (24 * 60)); - const hours = Math.floor((value % (24 * 60)) / 60); - const minutes = value % 60; - - let label = ''; - if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; - if (hours > 0 || days > 0) label += `${hours} hr${hours !== 1 ? 's' : ''}`; - if (minutes > 0 && days === 0) - label += ` ${minutes} min${minutes !== 1 ? 's' : ''}`; - - return label.trim(); + return formatSecondsToTwoUnits(value); }, - stepSize: 24 * 60, // One day + stepSize: 48 * 60, }, }, }, @@ -68,16 +54,7 @@ export const TimeSpentContributingByDay = () => { callbacks: { label: function (context) { const value = context.parsed.y; - const days = Math.floor(value / (24 * 60)); - const hours = Math.floor((value % (24 * 60)) / 60); - const minutes = value % 60; - - let label = 'Time Spent: '; - if (days > 0) label += `${days} day${days > 1 ? 's' : ''} `; - if (hours > 0 || days > 0) label += `${hours} hour${hours !== 1 ? 's' : ''} `; - if (minutes > 0) label += `${minutes} minute${minutes !== 1 ? 's' : ''}`; - - return label.trim(); + return formatSecondsToTwoUnits(value); }, }, }, @@ -90,17 +67,58 @@ export const TimeSpentContributingByDay = () => { chartInstance.current.destroy(); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const aggregateContributionTimeByWeekday = () => { + const aggregatedData = {}; + const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + + // Initialize aggregatedData with all weekdays + weekdays.forEach((day, index) => { + aggregatedData[index] = { + weekday: day, + totalcontributionTime: 0, + }; + }); + + contributionTimeByDate.forEach((item) => { + const date = parse(item.date, 'yyyy-MM-dd', new Date()); + const weekdayIndex = getDay(date); + + aggregatedData[weekdayIndex].totalcontributionTime += item.totalcontributionTime; + }); + + // Convert to array and sort by weekday index + return Object.entries(aggregatedData) + .sort(([a], [b]) => Number(a) - Number(b)) + .map(([_, value]) => value); + }; + return (

-
+
+ {contributionTimeByDate.length === 0 && ( +
+ +

No data found

+
+ )}
); }; + +TimeSpentContributingByDay.propTypes = { + contributionTimeByDate: PropTypes.arrayOf( + PropTypes.shape({ + totalcontributionTime: PropTypes.number, + date: PropTypes.string, + }), + ), +}; diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 52ece66358..47cb3892aa 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -116,7 +116,7 @@ export const PartnersMapswipeStats = () => {
- +
From d30eaaf4fbb9198a5744b06d9a79dd9b5390e0e8 Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 20 Sep 2024 19:06:53 +0530 Subject: [PATCH 068/101] fix: Remove redundant query param --- frontend/src/components/partnerMapswipeStats/overview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/partnerMapswipeStats/overview.js b/frontend/src/components/partnerMapswipeStats/overview.js index fed989f964..907a4c3b7b 100644 --- a/frontend/src/components/partnerMapswipeStats/overview.js +++ b/frontend/src/components/partnerMapswipeStats/overview.js @@ -56,7 +56,7 @@ export const Overview = () => { queryKey: ['partners-mapswipe-general-statistics', partnerPermalink], queryFn: async () => { const response = await fetchLocalJSONAPI( - `partners/${partnerPermalink}/general-statistics/?limit=0&offset=0`, + `partners/${partnerPermalink}/general-statistics/?limit=0`, ); return response; }, From f3e8e2a5bdf6f9a2fa7ef9df590dfb8e59826f9a Mon Sep 17 00:00:00 2001 From: bshankar Date: Fri, 20 Sep 2024 19:14:22 +0530 Subject: [PATCH 069/101] feat: Integrate API data into filtered statistics cards --- .../partnerMapswipeStats/projectTypeAreaStats.js | 13 +++++++------ frontend/src/views/partnersMapswipeStats.js | 10 +++++++--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js index 01e1010e0d..4569ebd85d 100644 --- a/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js +++ b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js @@ -3,11 +3,12 @@ import { FormattedMessage } from 'react-intl'; import messages from './messages'; import { StatsCardWithFooter } from '../statsCard'; import { MappingIcon, SwipeIcon, ClockIcon } from '../svgIcons'; +import { getShortNumber } from './overview'; const iconClass = 'w-100'; const iconStyle = { height: '55px' }; -export const ProjectTypeAreaStats = () => { +export const ProjectTypeAreaStats = ({ projectTypeAreaStats = [], areaSwipedByProjectType = [] }) => { return (
{ } description={} - value={'213K'} + value={getShortNumber(projectTypeAreaStats[0].totalcontributions)} delta={
- 25K Sq. KM. + {Math.round(areaSwipedByProjectType[0].totalArea)} Sq. KM. @@ -30,7 +31,7 @@ export const ProjectTypeAreaStats = () => { } description={} - value={'114K'} + value={getShortNumber(projectTypeAreaStats[2].totalcontributions)} delta={
@@ -41,10 +42,10 @@ export const ProjectTypeAreaStats = () => { } description={} - value={'11K'} + value={getShortNumber(projectTypeAreaStats[1].totalcontributions)} delta={
- 230 Sq. KM. + {Math.round(areaSwipedByProjectType[1].totalArea)} Sq. KM.
} diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 47cb3892aa..6f9e84bd46 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -14,6 +14,7 @@ import { ProjectTypeAreaStats } from '../components/partnerMapswipeStats/project import { SwipesByProjectType } from '../components/partnerMapswipeStats/swipesByProjectType'; import { SwipesByOrganisation } from '../components/partnerMapswipeStats/swipesByOrganisation'; import { StatsCardWithFooter } from '../components/statsCard'; +import { getShortNumber, formatSecondsToTwoUnits } from '../components/partnerMapswipeStats/overview'; import messages from './messages'; import { BanIcon } from '../components/svgIcons'; import { fetchLocalJSONAPI } from '../network/genericJSONRequest'; @@ -120,18 +121,21 @@ export const PartnersMapswipeStats = () => {
- +
} - value="338K" + value={getShortNumber(data?.contributionsByProjectType.map(c => c.totalcontributions).reduce((acc, t) => acc + t))} style={{ width: '48.5%' }} /> } - value="11 days 5 hrs" + value={formatSecondsToTwoUnits(data?.contributionTimeByDate.map(c => c.totalcontributionTime).reduce((acc, t) => acc + t))} className="w-100" style={{ width: '48.5%' }} /> From 6b5b519770977e1c1d980a35b76659b3c7744574 Mon Sep 17 00:00:00 2001 From: bshankar Date: Sun, 22 Sep 2024 08:34:49 +0530 Subject: [PATCH 070/101] fix: Pass getShortNumber when data not yet available --- frontend/src/views/partnersMapswipeStats.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 6f9e84bd46..895e21c7df 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -14,7 +14,7 @@ import { ProjectTypeAreaStats } from '../components/partnerMapswipeStats/project import { SwipesByProjectType } from '../components/partnerMapswipeStats/swipesByProjectType'; import { SwipesByOrganisation } from '../components/partnerMapswipeStats/swipesByOrganisation'; import { StatsCardWithFooter } from '../components/statsCard'; -import { getShortNumber, formatSecondsToTwoUnits } from '../components/partnerMapswipeStats/overview'; +import { getShortNumber, formatSecondsToTwoUnits } from '../components/partnerMapswipeStats/overview'; import messages from './messages'; import { BanIcon } from '../components/svgIcons'; import { fetchLocalJSONAPI } from '../network/genericJSONRequest'; @@ -130,12 +130,12 @@ export const PartnersMapswipeStats = () => {
} - value={getShortNumber(data?.contributionsByProjectType.map(c => c.totalcontributions).reduce((acc, t) => acc + t))} + value={getShortNumber(data?.contributionsByProjectType.map(c => c.totalcontributions).reduce((acc, t) => acc + t) ?? 0)} style={{ width: '48.5%' }} /> } - value={formatSecondsToTwoUnits(data?.contributionTimeByDate.map(c => c.totalcontributionTime).reduce((acc, t) => acc + t))} + value={formatSecondsToTwoUnits(data?.contributionTimeByDate.map(c => c.totalcontributionTime).reduce((acc, t) => acc + t) ?? 0)} className="w-100" style={{ width: '48.5%' }} /> From 9fd130e17299eea039c51e8680933b27a6d0f985 Mon Sep 17 00:00:00 2001 From: bshankar Date: Sun, 22 Sep 2024 08:44:34 +0530 Subject: [PATCH 071/101] fix: Hide empty data in area swiped by project type doughnut --- .../components/partnerMapswipeStats/swipesByProjectType.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js index 77baece32c..b189633525 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js @@ -21,10 +21,10 @@ export const SwipesByProjectType = ({ areaSwipedByProjectType = [] }) => { chartInstance.current = new Chart(context, { type: 'doughnut', data: { - labels: ['Find', 'Compare', 'Validate'], + labels: ['Find', 'Compare', 'Validate'].filter((_, i) => areaSwipedByProjectType[i].totalArea > 0), datasets: [ { - data: areaSwipedByProjectType.map(c => c.totalArea), + data: areaSwipedByProjectType.filter((_, i) => areaSwipedByProjectType[i].totalArea > 0).map(c => c.totalArea), backgroundColor: [ CHART_COLOURS.orange, // Orange for Find CHART_COLOURS.blue, // Blue for Compare From c6c305583778bff69341a5fd1efda33fa243e756 Mon Sep 17 00:00:00 2001 From: bshankar Date: Sun, 22 Sep 2024 09:05:01 +0530 Subject: [PATCH 072/101] feat: swipes by org doughnut: group low swipers into others --- .../swipesByOrganisation.js | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js index a285857439..e83150e880 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js @@ -5,9 +5,23 @@ import Chart from 'chart.js/auto'; import { CHART_COLOURS } from '../../config'; import messages from './messages'; -export const SwipesByOrganisation = ( { contributionsByOrganization = [] } ) => { +function withGroupedLowContributors(contributionsByOrganization, keepTop = 4) { + if (contributionsByOrganization.length <= keepTop) { + return contributionsByOrganization; + } + + contributionsByOrganization.sort((a, b) => b.totalcontributions - a.totalcontributions) + const topContributors = contributionsByOrganization.slice(0, keepTop) + const others = contributionsByOrganization.slice(keepTop) + .reduce((acc, c) => ({ ...acc, totalcontributions: acc.totalcontributions + c.totalcontributions }), + { organizationName: 'Others', totalcontributions: 0 }) + topContributors.push(others) + return topContributors +} +export const SwipesByOrganisation = ({ contributionsByOrganization = [] }) => { const chartRef = useRef(null); const chartInstance = useRef(null); + contributionsByOrganization = withGroupedLowContributors(contributionsByOrganization) useEffect(() => { if (chartInstance.current) { @@ -26,11 +40,11 @@ export const SwipesByOrganisation = ( { contributionsByOrganization = [] } ) => { data: contributionsByOrganization.map(c => c.totalcontributions), backgroundColor: [ - CHART_COLOURS.red, // Orange for American Red Cross - CHART_COLOURS.orange, // Yellow for Arizona State University - CHART_COLOURS.green, // Green for HOT - CHART_COLOURS.blue, // Blue for Médecins Sans Frontières - CHART_COLOURS.gray, // Gray for Others + CHART_COLOURS.red, + CHART_COLOURS.orange, + CHART_COLOURS.green, + CHART_COLOURS.blue, + CHART_COLOURS.gray, ], borderColor: '#fff', borderWidth: 2, From 3a0af08db49474efdf9e7fe9eaef7005934a1419 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Sun, 22 Sep 2024 13:26:03 +0530 Subject: [PATCH 073/101] fix: handle pagination and loading placeholder for group members table --- .../partnerMapswipeStats/groupMembers.js | 98 ++++++++++++------- .../groupMembersPlaceholder.js | 19 ---- 2 files changed, 64 insertions(+), 53 deletions(-) delete mode 100644 frontend/src/components/partnerMapswipeStats/groupMembersPlaceholder.js diff --git a/frontend/src/components/partnerMapswipeStats/groupMembers.js b/frontend/src/components/partnerMapswipeStats/groupMembers.js index 77c7ad18ff..58db4739ae 100644 --- a/frontend/src/components/partnerMapswipeStats/groupMembers.js +++ b/frontend/src/components/partnerMapswipeStats/groupMembers.js @@ -1,15 +1,14 @@ +import { useEffect, useState } from 'react'; import { FormattedMessage } from 'react-intl'; import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table'; import { useParams } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; -import { useState } from 'react'; import ReactPlaceholder from 'react-placeholder'; import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; import { BanIcon, CircleExclamationIcon } from '../svgIcons'; import { PaginatorLine } from '../paginator'; import messages from './messages'; -import { GroupMembersPlaceholder } from './groupMembersPlaceholder'; import './groupMembers.css'; const COLUMNS = [ @@ -20,6 +19,7 @@ const COLUMNS = [ ), + cell: ({ row }) => {row.original.username}, }, { accessorKey: 'totalcontributions', @@ -47,22 +47,46 @@ const COLUMNS = [ }, ]; +const GroupMembersPlaceholder = () => ( + <> + {new Array(9).fill( + + {new Array(4).fill( + + + , + )} + , + )} + +); + export const GroupMembers = () => { + const [pageNumber, setPageNumber] = useState(0); const { id: partnerPermalink } = useParams(); - const ROWS = 10; - const [pageNumber, setPageNumber] = useState(0) + const rows = 10; - const { isLoading, isError, data, isRefetching } = useQuery({ - queryKey: ['partners-mapswipe-general-statistics', partnerPermalink, pageNumber], + const { isLoading, isError, data, isRefetching, refetch } = useQuery({ + queryKey: ['partners-mapswipe-statistics-group-members', partnerPermalink], queryFn: async () => { const response = await fetchLocalJSONAPI( - `partners/${partnerPermalink}/general-statistics/?limit=${ROWS}&offset=${pageNumber * ROWS}`, + `partners/${partnerPermalink}/general-statistics/?limit=${rows}&offset=${ + pageNumber * rows + }`, ); return response; }, }); + useEffect(() => { + refetch(); + }, [pageNumber]); + const table = useReactTable({ columns: COLUMNS, data: data?.members ?? [], @@ -71,10 +95,10 @@ export const GroupMembers = () => { columnResizeDirection: 'ltr', }); - const isEmpty = false; + const isEmpty = !isLoading && !isRefetching && !isError && data?.members.length === 0; return ( - } ready={!isLoading && !isRefetching}> +

@@ -95,14 +119,15 @@ export const GroupMembers = () => { ? null : flexRender(header.column.columnDef.header, header.getContext())} - {header.id !== 'timeSpent' && ( + {header.id !== 'totalcontributionTime' && (
header.column.resetSize(), onMouseDown: header.getResizeHandler(), onTouchStart: header.getResizeHandler(), - className: `partner-mapswipe-stats-column-resizer ${table.options.columnResizeDirection === 'ltr' ? 'right-0' : '' - } ${header.column.getIsResizing() ? 'isResizing' : ''}`, + className: `partner-mapswipe-stats-column-resizer ${ + table.options.columnResizeDirection === 'ltr' ? 'right-0' : '' + } ${header.column.getIsResizing() ? 'isResizing' : ''}`, }} /> )} @@ -112,25 +137,30 @@ export const GroupMembers = () => { ))} - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ))} + } + ready={!isLoading && !isRefetching} + > + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + @@ -156,12 +186,12 @@ export const GroupMembers = () => {
setPageNumber(parseInt(n) - 1)} - lastPage={Math.max(Math.ceil((data?.membersCount ?? 0) / ROWS), 1)} + setPageFn={(newPageNumber) => setPageNumber(newPageNumber - 1)} + lastPage={Math.max(Math.ceil((data?.membersCount ?? 0) / rows), 1)} className="flex items-center justify-center pa4" />
- +
); }; diff --git a/frontend/src/components/partnerMapswipeStats/groupMembersPlaceholder.js b/frontend/src/components/partnerMapswipeStats/groupMembersPlaceholder.js deleted file mode 100644 index df6b4dbe37..0000000000 --- a/frontend/src/components/partnerMapswipeStats/groupMembersPlaceholder.js +++ /dev/null @@ -1,19 +0,0 @@ -import { FormattedMessage } from 'react-intl'; -import ReactPlaceholder from 'react-placeholder'; -import { TextRow } from 'react-placeholder/lib/placeholders'; - -import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; -import { BanIcon, CircleExclamationIcon } from '../svgIcons'; -import { PaginatorLine } from '../paginator'; -import messages from './messages'; - -export const GroupMembersPlaceholder = () => { - return ( -
-

- -

- Loading ... -
- ) -} From a812356bbde7af5e69981337a20c107a21d24251 Mon Sep 17 00:00:00 2001 From: bshankar Date: Sun, 22 Sep 2024 14:59:12 +0530 Subject: [PATCH 074/101] fix: Handle empty states in the response --- .../partnerMapswipeStats/projectTypeAreaStats.js | 10 +++++----- .../partnerMapswipeStats/swipesByProjectType.js | 2 +- frontend/src/views/partnersMapswipeStats.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js index 4569ebd85d..e87843c0db 100644 --- a/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js +++ b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js @@ -17,10 +17,10 @@ export const ProjectTypeAreaStats = ({ projectTypeAreaStats = [], areaSwipedByPr } description={} - value={getShortNumber(projectTypeAreaStats[0].totalcontributions)} + value={getShortNumber(projectTypeAreaStats[0]?.totalcontributions ?? 0)} delta={
- {Math.round(areaSwipedByProjectType[0].totalArea)} Sq. KM. + {Math.round(areaSwipedByProjectType[0]?.totalArea ?? 0)} Sq. KM. @@ -31,7 +31,7 @@ export const ProjectTypeAreaStats = ({ projectTypeAreaStats = [], areaSwipedByPr } description={} - value={getShortNumber(projectTypeAreaStats[2].totalcontributions)} + value={getShortNumber(projectTypeAreaStats[2]?.totalcontributions ?? 0)} delta={
@@ -42,10 +42,10 @@ export const ProjectTypeAreaStats = ({ projectTypeAreaStats = [], areaSwipedByPr } description={} - value={getShortNumber(projectTypeAreaStats[1].totalcontributions)} + value={getShortNumber(projectTypeAreaStats[1]?.totalcontributions ?? 0)} delta={
- {Math.round(areaSwipedByProjectType[1].totalArea)} Sq. KM. + {Math.round(areaSwipedByProjectType[1]?.totalArea ?? 0)} Sq. KM.
} diff --git a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js index b189633525..bf48e25478 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js @@ -21,7 +21,7 @@ export const SwipesByProjectType = ({ areaSwipedByProjectType = [] }) => { chartInstance.current = new Chart(context, { type: 'doughnut', data: { - labels: ['Find', 'Compare', 'Validate'].filter((_, i) => areaSwipedByProjectType[i].totalArea > 0), + labels: ['Find', 'Compare', 'Validate'].filter((_, i) => areaSwipedByProjectType[i]?.totalArea ?? 0 > 0), datasets: [ { data: areaSwipedByProjectType.filter((_, i) => areaSwipedByProjectType[i].totalArea > 0).map(c => c.totalArea), diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 895e21c7df..b119fcce90 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -130,12 +130,12 @@ export const PartnersMapswipeStats = () => {
} - value={getShortNumber(data?.contributionsByProjectType.map(c => c.totalcontributions).reduce((acc, t) => acc + t) ?? 0)} + value={getShortNumber(data?.contributionsByProjectType.map(c => c.totalcontributions).reduce((acc, t) => acc + t, 0) ?? 0)} style={{ width: '48.5%' }} /> } - value={formatSecondsToTwoUnits(data?.contributionTimeByDate.map(c => c.totalcontributionTime).reduce((acc, t) => acc + t) ?? 0)} + value={formatSecondsToTwoUnits(data?.contributionTimeByDate.map(c => c.totalcontributionTime).reduce((acc, t) => acc + t, 0) ?? 0)} className="w-100" style={{ width: '48.5%' }} /> From ce97d0607e9ee3d58554442c9104e29344ac3983 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Sun, 22 Sep 2024 16:07:28 +0530 Subject: [PATCH 075/101] feat: implement download members as CSV + add caching to table --- .../partnerMapswipeStats/groupMembers.js | 36 ++++++++++++------- .../partnerMapswipeStats/messages.js | 4 +++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/groupMembers.js b/frontend/src/components/partnerMapswipeStats/groupMembers.js index 58db4739ae..f33b8f898f 100644 --- a/frontend/src/components/partnerMapswipeStats/groupMembers.js +++ b/frontend/src/components/partnerMapswipeStats/groupMembers.js @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { FormattedMessage } from 'react-intl'; import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table'; import { useParams } from 'react-router-dom'; @@ -6,8 +6,9 @@ import { useQuery } from '@tanstack/react-query'; import ReactPlaceholder from 'react-placeholder'; import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; -import { BanIcon, CircleExclamationIcon } from '../svgIcons'; +import { BanIcon, CircleExclamationIcon, DownloadIcon } from '../svgIcons'; import { PaginatorLine } from '../paginator'; +import { API_URL } from '../../config'; import messages from './messages'; import './groupMembers.css'; @@ -71,8 +72,8 @@ export const GroupMembers = () => { const rows = 10; - const { isLoading, isError, data, isRefetching, refetch } = useQuery({ - queryKey: ['partners-mapswipe-statistics-group-members', partnerPermalink], + const { isLoading, isError, data, isFetching, isPreviousData } = useQuery({ + queryKey: ['partners-mapswipe-statistics-group-members', partnerPermalink, pageNumber], queryFn: async () => { const response = await fetchLocalJSONAPI( `partners/${partnerPermalink}/general-statistics/?limit=${rows}&offset=${ @@ -81,12 +82,10 @@ export const GroupMembers = () => { ); return response; }, + keepPreviousData: true, + staleTime: 5 * 60 * 1000, }); - useEffect(() => { - refetch(); - }, [pageNumber]); - const table = useReactTable({ columns: COLUMNS, data: data?.members ?? [], @@ -95,13 +94,24 @@ export const GroupMembers = () => { columnResizeDirection: 'ltr', }); - const isEmpty = !isLoading && !isRefetching && !isError && data?.members.length === 0; + const isEmpty = !isLoading && !isFetching && !isError && data?.members.length === 0; return (
-

- -

+
+

+ +

+ + + + +
+
@@ -139,7 +149,7 @@ export const GroupMembers = () => { } - ready={!isLoading && !isRefetching} + ready={!isLoading && !isFetching} > {table.getRowModel().rows.map((row) => ( diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index 72775a29d7..add45ce2d9 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -128,4 +128,8 @@ export default defineMessages({ id: 'management.partners.stats.mapswipe.area.compare', defaultMessage: 'Compare', }, + downloadMembersAsCSV: { + id: 'management.partners.stats.mapswipe.groupMembers.download', + defaultMessage: 'Download CSV', + }, }); From 8eb282d4329d9e77fed9fa224a20bf44268e0b9b Mon Sep 17 00:00:00 2001 From: bshankar Date: Sun, 22 Sep 2024 19:09:34 +0530 Subject: [PATCH 076/101] fix: partner statistics APIs: detect invalid group id --- backend/services/mapswipe_service.py | 113 +++++++++++++++------------ 1 file changed, 65 insertions(+), 48 deletions(-) diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 68aa4b87cc..22b4366154 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -1,4 +1,5 @@ import json +from backend.exceptions import Conflict from backend.models.dtos.partner_stats_dto import ( GroupedPartnerStatsDTO, FilteredPartnerStatsDTO, @@ -78,53 +79,56 @@ def __build_query_filtered_user_group_stats( operationName = "FilteredUserGroupStats" query = """ query FilteredUserGroupStats($pk: ID!, $fromDate: DateTime!, $toDate: DateTime!) { + userGroup(pk: $pk) { + id + } userGroupStats(userGroupId: $pk) { id - filteredStats(dateRange: {fromDate: $fromDate, toDate: $toDate}) { - userStats { - totalMappingProjects - totalSwipeTime - totalSwipes - username - userId - __typename - } - contributionByGeo { - geojson - totalContribution - __typename - } - areaSwipedByProjectType { - totalArea - projectTypeDisplay - projectType - __typename - } - swipeByDate { - taskDate - totalSwipes - __typename - } - swipeTimeByDate { - date - totalSwipeTime - __typename - } - swipeByProjectType { - projectType - projectTypeDisplay - totalSwipes - __typename - } - swipeByOrganizationName { - organizationName - totalSwipes - __typename - } - __typename - } - __typename - } + filteredStats(dateRange: {fromDate: $fromDate, toDate: $toDate}) { + userStats { + totalMappingProjects + totalSwipeTime + totalSwipes + username + userId + __typename + } + contributionByGeo { + geojson + totalContribution + __typename + } + areaSwipedByProjectType { + totalArea + projectTypeDisplay + projectType + __typename + } + swipeByDate { + taskDate + totalSwipes + __typename + } + swipeTimeByDate { + date + totalSwipeTime + __typename + } + swipeByProjectType { + projectType + projectTypeDisplay + totalSwipes + __typename + } + swipeByOrganizationName { + organizationName + totalSwipes + __typename + } + __typename + } + __typename + } } """ variables = {"fromDate": from_date, "toDate": to_date, "pk": group_id} @@ -138,6 +142,13 @@ def setup_group_dto( group_dto.id = partner_id group_dto.provider = "mapswipe" group_dto.id_inside_provider = group_id + + if group_stats["userGroup"] is None: + raise Conflict( + "INVALID_MAPSWIPE_GROUP_ID", + "The mapswipe group ID linked to this partner is invalid. Please contact an admin.", + ) + group_dto.name_inside_provider = group_stats["userGroup"]["name"] group_dto.description_inside_provider = group_stats["userGroup"]["description"] @@ -190,9 +201,15 @@ def setup_filtered_dto( filtered_stats_dto.from_date = from_date filtered_stats_dto.to_date = to_date - filtered_stats = json.loads(resp_body)["data"]["userGroupStats"][ - "filteredStats" - ] + filtered_stats = json.loads(resp_body)["data"] + + if filtered_stats["userGroup"] is None: + raise Conflict( + "INVALID_MAPSWIPE_GROUP_ID", + "The mapswipe group ID linked to this partner is invalid. Please contact an admin.", + ) + + filtered_stats = filtered_stats["userGroupStats"]["filteredStats"] stats_by_user = [] for user_stats in filtered_stats["userStats"]: From acd86af032c0c713d9a957cfd9edf86234d9a475 Mon Sep 17 00:00:00 2001 From: bshankar Date: Sun, 22 Sep 2024 19:18:55 +0530 Subject: [PATCH 077/101] fix: Backend sonarcube suggestions --- backend/api/partners/statistics.py | 6 +++--- backend/services/mapswipe_service.py | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/api/partners/statistics.py b/backend/api/partners/statistics.py index e0f2ae973c..6d661fec99 100644 --- a/backend/api/partners/statistics.py +++ b/backend/api/partners/statistics.py @@ -153,17 +153,17 @@ def get(self, permalink: str): limit = int(request.args.get("limit", 10)) offset = int(request.args.get("offset", 0)) - downloadAsCSV = bool(request.args.get("downloadAsCSV", "false") == "true") + download_as_csv = bool(request.args.get("downloadAsCSV", "false") == "true") group_dto = mapswipe.fetch_grouped_partner_stats( partner.id, partner.mapswipe_group_id, limit, offset, - downloadAsCSV, + download_as_csv, ) - if downloadAsCSV: + if download_as_csv: return send_file( io.BytesIO(group_dto.to_csv().encode()), mimetype="text/csv", diff --git a/backend/services/mapswipe_service.py b/backend/services/mapswipe_service.py index 22b4366154..b41552c3df 100644 --- a/backend/services/mapswipe_service.py +++ b/backend/services/mapswipe_service.py @@ -26,7 +26,7 @@ class MapswipeService: def __build_query_user_group_stats(group_id: str, limit: int, offset: int): """A private method to build a graphQl query for fetching a user group's stats from Mapswipe.""" - operationName = "UserGroupStats" + operation_name = "UserGroupStats" query = """ query UserGroupStats($pk: ID!, $limit: Int!, $offset: Int!) { userGroup(pk: $pk) { @@ -69,14 +69,14 @@ def __build_query_user_group_stats(group_id: str, limit: int, offset: int): } """ variables = {"limit": limit, "offset": offset, "pk": group_id} - return {"operationName": operationName, "query": query, "variables": variables} + return {"operationName": operation_name, "query": query, "variables": variables} def __build_query_filtered_user_group_stats( self, group_id: str, from_date: str, to_date: str ): """A private method to build a graphQl query to fetch a mapswipe group's stats within a timerange.""" - operationName = "FilteredUserGroupStats" + operation_name = "FilteredUserGroupStats" query = """ query FilteredUserGroupStats($pk: ID!, $fromDate: DateTime!, $toDate: DateTime!) { userGroup(pk: $pk) { @@ -132,7 +132,7 @@ def __build_query_filtered_user_group_stats( } """ variables = {"fromDate": from_date, "toDate": to_date, "pk": group_id} - return {"operationName": operationName, "query": query, "variables": variables} + return {"operationName": operation_name, "query": query, "variables": variables} def setup_group_dto( self, partner_id: str, group_id: str, resp_body: str @@ -297,11 +297,11 @@ def fetch_grouped_partner_stats( group_id: str, limit: int, offset: int, - downloadAsCSV: bool, + download_as_csv: bool, ) -> GroupedPartnerStatsDTO: """Service to fetch user group statistics by each member with pagination""" - if downloadAsCSV: + if download_as_csv: limit = 1_000_000 offset = 0 From aceed74ffe2dc7620debe0f86d5421df40fc4ac7 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Sun, 22 Sep 2024 20:20:18 +0530 Subject: [PATCH 078/101] fix: update the way data is extracted from API --- .../partnerMapswipeStats/messages.js | 2 +- .../projectTypeAreaStats.js | 72 ++++++++++++++++--- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index add45ce2d9..2c0ed85681 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -122,7 +122,7 @@ export default defineMessages({ }, sceneComparedByProjectType: { id: 'management.partners.stats.mapswipe.sceneCompared', - defaultMessage: 'Area Swipes', + defaultMessage: 'Scene Compared', }, compare: { id: 'management.partners.stats.mapswipe.area.compare', diff --git a/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js index e87843c0db..a9e6a284d0 100644 --- a/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js +++ b/frontend/src/components/partnerMapswipeStats/projectTypeAreaStats.js @@ -1,4 +1,5 @@ import { FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; import messages from './messages'; import { StatsCardWithFooter } from '../statsCard'; @@ -8,7 +9,42 @@ import { getShortNumber } from './overview'; const iconClass = 'w-100'; const iconStyle = { height: '55px' }; -export const ProjectTypeAreaStats = ({ projectTypeAreaStats = [], areaSwipedByProjectType = [] }) => { +export const ProjectTypeAreaStats = ({ + projectTypeAreaStats = [], + areaSwipedByProjectType = [], +}) => { + const data = { + find: { + totalcontributions: 0, + totalArea: 0, + }, + validate: { + totalcontributions: 0, + }, + compare: { + totalcontributions: 0, + totalArea: 0, + }, + }; + + projectTypeAreaStats.forEach((stat) => { + if (['build_area', 'buildarea'].includes(stat.projectType.toLowerCase())) { + data.find.totalcontributions = getShortNumber(stat.totalcontributions || 0); + } else if (['foot_print', 'footprint'].includes(stat.projectType.toLowerCase())) { + data.validate.totalcontributions = getShortNumber(stat.totalcontributions || 0); + } else if (['change_detection', 'changedetection'].includes(stat.projectType.toLowerCase())) { + data.compare.totalcontributions = getShortNumber(stat.totalcontributions || 0); + } + }); + + areaSwipedByProjectType.forEach((stat) => { + if (['build_area', 'buildarea'].includes(stat.projectType.toLowerCase())) { + data.find.totalArea = getShortNumber(stat.totalArea || 0); + } else if (['change_detection', 'changedetection'].includes(stat.projectType.toLowerCase())) { + data.compare.totalArea = getShortNumber(stat.totalArea || 0); + } + }); + return (
} description={} - value={getShortNumber(projectTypeAreaStats[0]?.totalcontributions ?? 0)} + value={data.find.totalcontributions} delta={
- {Math.round(areaSwipedByProjectType[0]?.totalArea ?? 0)} Sq. KM. + {data.find.totalArea} Sq. KM. @@ -31,10 +67,12 @@ export const ProjectTypeAreaStats = ({ projectTypeAreaStats = [], areaSwipedByPr } description={} - value={getShortNumber(projectTypeAreaStats[2]?.totalcontributions ?? 0)} + value={data.validate.totalcontributions} delta={
- + + +
} className="w-100" @@ -42,11 +80,13 @@ export const ProjectTypeAreaStats = ({ projectTypeAreaStats = [], areaSwipedByPr } description={} - value={getShortNumber(projectTypeAreaStats[1]?.totalcontributions ?? 0)} + value={data.compare.totalcontributions} delta={
- {Math.round(areaSwipedByProjectType[1]?.totalArea ?? 0)} Sq. KM. - + {data.compare.totalArea} Sq. KM. + + +
} className="w-100" @@ -54,3 +94,19 @@ export const ProjectTypeAreaStats = ({ projectTypeAreaStats = [], areaSwipedByPr
); }; + +ProjectTypeAreaStats.propTypes = { + projectTypeAreaStats: PropTypes.arrayOf( + PropTypes.shape({ + projectType: PropTypes.string, + projectTypeDisplay: PropTypes.string, + totalcontributions: PropTypes.numberstring, + }), + ), + areaSwipedByProjectType: PropTypes.arrayOf( + PropTypes.shape({ + organizationName: PropTypes.string, + totalcontributions: PropTypes.number, + }), + ), +}; From a4f685251dc1ecc143b41aa5603f567ebf9a5895 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Sun, 22 Sep 2024 20:23:04 +0530 Subject: [PATCH 079/101] refactor: remove comments, add functions to get data, clean imports --- frontend/src/views/partnersMapswipeStats.js | 47 ++++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index b119fcce90..7c429dcbbe 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -3,8 +3,12 @@ import { useParams } from 'react-router-dom'; import ReactPlaceholder from 'react-placeholder'; import { useQuery } from '@tanstack/react-query'; -import { InfoIcon } from '../components/svgIcons'; -import { Overview } from '../components/partnerMapswipeStats/overview'; +import { InfoIcon, BanIcon } from '../components/svgIcons'; +import { + Overview, + getShortNumber, + formatSecondsToTwoUnits, +} from '../components/partnerMapswipeStats/overview'; import { GroupMembers } from '../components/partnerMapswipeStats/groupMembers'; import { ContributionsGrid } from '../components/partnerMapswipeStats/contributionsGrid'; import { ContributionsHeatmap } from '../components/partnerMapswipeStats/contributionsHeatmap'; @@ -14,23 +18,12 @@ import { ProjectTypeAreaStats } from '../components/partnerMapswipeStats/project import { SwipesByProjectType } from '../components/partnerMapswipeStats/swipesByProjectType'; import { SwipesByOrganisation } from '../components/partnerMapswipeStats/swipesByOrganisation'; import { StatsCardWithFooter } from '../components/statsCard'; -import { getShortNumber, formatSecondsToTwoUnits } from '../components/partnerMapswipeStats/overview'; import messages from './messages'; -import { BanIcon } from '../components/svgIcons'; import { fetchLocalJSONAPI } from '../network/genericJSONRequest'; import './partnersMapswipeStats.css'; const PagePlaceholder = () => (
- {/* */} - {/*
- - - -
*/}
{ }, }); + const getSwipes = () => { + if (!data) return -; + if (data.contributionsByProjectType?.length === 0) return 0; + return getShortNumber( + data.contributionsByProjectType + .map((item) => item.totalcontributions) + .reduce((total, value) => total + value, 0), + ); + }; + + const getTimeSpentContributing = () => { + if (!data) return '-'; + if (data.contributionTimeByDate?.length === 0) return '0'; + return formatSecondsToTwoUnits( + data.contributionTimeByDate + .map((item) => item.totalcontributionTime) + .reduce((total, value) => total + value, 0), + ); + }; + return (
@@ -130,12 +143,12 @@ export const PartnersMapswipeStats = () => {
} - value={getShortNumber(data?.contributionsByProjectType.map(c => c.totalcontributions).reduce((acc, t) => acc + t, 0) ?? 0)} + value={getSwipes()} style={{ width: '48.5%' }} /> } - value={formatSecondsToTwoUnits(data?.contributionTimeByDate.map(c => c.totalcontributionTime).reduce((acc, t) => acc + t, 0) ?? 0)} + value={getTimeSpentContributing()} className="w-100" style={{ width: '48.5%' }} /> @@ -143,7 +156,9 @@ export const PartnersMapswipeStats = () => {
- +
From 95a0e3b2703b7e7b0e48953d0179752557bb4549 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Sun, 22 Sep 2024 20:55:08 +0530 Subject: [PATCH 080/101] fix: update the way data is being extracted from API --- .../swipesByProjectType.js | 60 +++++++++++++++++-- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js index bf48e25478..ab207f7a08 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js @@ -1,11 +1,13 @@ import { useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import Chart from 'chart.js/auto'; +import PropTypes from 'prop-types'; import { CHART_COLOURS } from '../../config'; +import { EmptySetIcon } from '../svgIcons'; import messages from './messages'; -export const SwipesByProjectType = ({ areaSwipedByProjectType = [] }) => { +export const SwipesByProjectType = ({ contributionsByProjectType = [] }) => { const chartRef = useRef(null); const chartInstance = useRef(null); @@ -16,19 +18,51 @@ export const SwipesByProjectType = ({ areaSwipedByProjectType = [] }) => { if (!chartRef.current) return; + const labelData = []; + const data = { + find: { + totalcontributions: 0, + }, + validate: { + totalcontributions: 0, + }, + compare: { + totalcontributions: 0, + }, + }; + + contributionsByProjectType.forEach((stat) => { + if (['build_area', 'buildarea'].includes(stat.projectType.toLowerCase())) { + data.find.totalcontributions = stat.totalcontributions || 0; + if (data.find.totalcontributions > 0) labelData.push('Find'); + } else if (['change_detection', 'changedetection'].includes(stat.projectType.toLowerCase())) { + data.compare.totalcontributions = stat.totalcontributions || 0; + if (data.compare.totalcontributions > 0) labelData.push('Compare'); + } else if (['foot_print', 'footprint'].includes(stat.projectType.toLowerCase())) { + data.validate.totalcontributions = stat.totalcontributions || 0; + if (data.validate.totalcontributions > 0) labelData.push('Validate'); + } + }); + + const chartData = [ + data.find.totalcontributions, + data.validate.totalcontributions, + data.compare.totalcontributions, + ]; + const context = chartRef.current.getContext('2d'); chartInstance.current = new Chart(context, { type: 'doughnut', data: { - labels: ['Find', 'Compare', 'Validate'].filter((_, i) => areaSwipedByProjectType[i]?.totalArea ?? 0 > 0), + labels: labelData, datasets: [ { - data: areaSwipedByProjectType.filter((_, i) => areaSwipedByProjectType[i].totalArea > 0).map(c => c.totalArea), + data: chartData, backgroundColor: [ CHART_COLOURS.orange, // Orange for Find - CHART_COLOURS.blue, // Blue for Compare CHART_COLOURS.green, // Green for Validate + CHART_COLOURS.blue, // Blue for Compare ], borderColor: '#fff', borderWidth: 2, @@ -59,9 +93,25 @@ export const SwipesByProjectType = ({ areaSwipedByProjectType = [] }) => { -
+
+ {contributionsByProjectType.length === 0 && ( +
+ +

No data found

+
+ )}
); }; + +SwipesByProjectType.propTypes = { + contributionsByProjectType: PropTypes.arrayOf( + PropTypes.shape({ + projectType: PropTypes.string, + projectTypeDisplay: PropTypes.string, + totalcontributions: PropTypes.numberstring, + }), + ), +}; From 9586ec8840b8b2171275e2db29aaa4cc60feb378 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Sun, 22 Sep 2024 20:56:01 +0530 Subject: [PATCH 081/101] Add empty state to pie charts, prettify code --- .../swipesByOrganisation.js | 42 ++++++++++++++----- frontend/src/views/partnersMapswipeStats.js | 2 +- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js index e83150e880..ecfc2b5721 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js @@ -1,8 +1,10 @@ import { useEffect, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import Chart from 'chart.js/auto'; +import PropTypes from 'prop-types'; import { CHART_COLOURS } from '../../config'; +import { EmptySetIcon } from '../svgIcons'; import messages from './messages'; function withGroupedLowContributors(contributionsByOrganization, keepTop = 4) { @@ -10,18 +12,21 @@ function withGroupedLowContributors(contributionsByOrganization, keepTop = 4) { return contributionsByOrganization; } - contributionsByOrganization.sort((a, b) => b.totalcontributions - a.totalcontributions) - const topContributors = contributionsByOrganization.slice(0, keepTop) - const others = contributionsByOrganization.slice(keepTop) - .reduce((acc, c) => ({ ...acc, totalcontributions: acc.totalcontributions + c.totalcontributions }), - { organizationName: 'Others', totalcontributions: 0 }) - topContributors.push(others) - return topContributors + contributionsByOrganization.sort((a, b) => b.totalcontributions - a.totalcontributions); + const topContributors = contributionsByOrganization.slice(0, keepTop); + const others = contributionsByOrganization + .slice(keepTop) + .reduce( + (acc, c) => ({ ...acc, totalcontributions: acc.totalcontributions + c.totalcontributions }), + { organizationName: 'Others', totalcontributions: 0 }, + ); + topContributors.push(others); + return topContributors; } export const SwipesByOrganisation = ({ contributionsByOrganization = [] }) => { const chartRef = useRef(null); const chartInstance = useRef(null); - contributionsByOrganization = withGroupedLowContributors(contributionsByOrganization) + contributionsByOrganization = withGroupedLowContributors(contributionsByOrganization); useEffect(() => { if (chartInstance.current) { @@ -35,10 +40,10 @@ export const SwipesByOrganisation = ({ contributionsByOrganization = [] }) => { chartInstance.current = new Chart(context, { type: 'doughnut', data: { - labels: contributionsByOrganization.map(c => c.organizationName), + labels: contributionsByOrganization.map((item) => item.organizationName), datasets: [ { - data: contributionsByOrganization.map(c => c.totalcontributions), + data: contributionsByOrganization.map((item) => item.totalcontributions), backgroundColor: [ CHART_COLOURS.red, CHART_COLOURS.orange, @@ -79,9 +84,24 @@ export const SwipesByOrganisation = ({ contributionsByOrganization = [] }) => { -
+
+ {contributionsByOrganization.length === 0 && ( +
+ +

No data found

+
+ )}
); }; + +SwipesByOrganisation.propTypes = { + contributionsByOrganization: PropTypes.arrayOf( + PropTypes.shape({ + organizationName: PropTypes.string, + totalcontributions: PropTypes.numberstring, + }), + ), +}; diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 7c429dcbbe..40940b8067 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -155,7 +155,7 @@ export const PartnersMapswipeStats = () => {
- + From ad9dc64fe320ca21bb3101557349de4a276d60fc Mon Sep 17 00:00:00 2001 From: bshankar Date: Mon, 23 Sep 2024 07:55:53 +0530 Subject: [PATCH 082/101] Remove redundant mapswipe json response files --- backend/filtered_mapswipe_stats.json | 6303 -------------------------- backend/general_mapswipe_stats.json | 133 - 2 files changed, 6436 deletions(-) delete mode 100644 backend/filtered_mapswipe_stats.json delete mode 100644 backend/general_mapswipe_stats.json diff --git a/backend/filtered_mapswipe_stats.json b/backend/filtered_mapswipe_stats.json deleted file mode 100644 index be70015d0d..0000000000 --- a/backend/filtered_mapswipe_stats.json +++ /dev/null @@ -1,6303 +0,0 @@ -{ - "data": { - "userGroupStats": { - "id": "-NL6WXPOdFyWACqwNU2O", - "filteredStats": { - "userStats": [ - { - "totalMappingProjects": 3, - "totalSwipeTime": 6288, - "totalSwipes": 1380, - "username": "jaiC", - "userId": "05AfE1oB5vY6EbPqEjkdR9o0s4D3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 69, - "totalSwipes": 37, - "username": "jainMedha", - "userId": "08zR2YnWNXh8b5sFd2xEVa2c2t53", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 18, - "totalSwipeTime": 9469, - "totalSwipes": 3455, - "username": "Micah", - "userId": "09G9UqZq9yODe5XRl1ekhchba3x1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 381, - "totalSwipes": 111, - "username": "divvs2007", - "userId": "0f1KIx0VmmRcqxdwOp2jnlZyNWx2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 2607, - "totalSwipes": 1036, - "username": "ashleycruzc", - "userId": "0N1lhg0NJAN6lvAfEBkhzXnXgv53", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 17845, - "totalSwipes": 4514, - "username": "tanvi", - "userId": "0QCwiA4kCzac6rlsedwmu5vPJb53", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 1280, - "totalSwipes": 836, - "username": "janyapatel", - "userId": "0QQCPKsz2Bh3HtmseOBqSFN6qiq2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 662, - "totalSwipes": 168, - "username": "alau01", - "userId": "0ZjtwYDEtccUoj0DVo2hTBRCeAb2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 4463, - "totalSwipes": 1147, - "username": "Caleb-Roe", - "userId": "1I5AnlHnnzcTArbrrRGeuEb5sYj2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 1667, - "totalSwipes": 783, - "username": "sree", - "userId": "1JDWIeLlLUY8LZTO7GbaoNp45kv1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 8, - "totalSwipeTime": 1251, - "totalSwipes": 847, - "username": "hasini reddy alva", - "userId": "1Y8TnMHfeUh0l43lkK9XMUO6qaq1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 360, - "totalSwipes": 74, - "username": "maryc", - "userId": "2jdrfVroUbX9PIIY1vVRa2bT1YB3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 7, - "totalSwipeTime": 3481, - "totalSwipes": 1105, - "username": "elizak", - "userId": "2nNiuV7OdFbAhhSLKm41OY2gbNR2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 3480, - "totalSwipes": 1813, - "username": "tanisha_j", - "userId": "2Orm8Un7j5e2rOq6XvqweuFDSUf2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 165, - "totalSwipes": 74, - "username": "Jeff Alan", - "userId": "2Pu6lkz3J9TYervsBjWNq7nq9Vt2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 18, - "totalSwipeTime": 26290, - "totalSwipes": 6928, - "username": "Tania Perez M.", - "userId": "2T72ImN2Lgh9Q4LfHN1C3TwX9vo2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 752, - "totalSwipes": 428, - "username": "SAF Team DE-Meade-Central MD", - "userId": "2Z2utzhZvDXxrhJYLYfMKXMOMPb2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 686, - "totalSwipes": 148, - "username": "Cookie", - "userId": "2z3CgS5aKoc5RG7ncJ0i9Zl4Tow2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 120, - "totalSwipes": 50, - "username": "NoUsername", - "userId": "3EFAn9TfckdPxknTfL1W3ZlGcc12", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 2872, - "totalSwipes": 666, - "username": "CynthiaAnna", - "userId": "3QKTiV7g2af3MEF8fhpj0Q8V8mA3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1330, - "totalSwipes": 444, - "username": "davilloyd", - "userId": "3QVA7QlH1ONgsmsTiHClGz9Jc0U2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 378, - "totalSwipes": 111, - "username": "sebettencourt", - "userId": "3Ron1P0VSqcmWfpMxMStvDdyQmS2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 186, - "totalSwipes": 37, - "username": "sarah", - "userId": "3UZYMjitTzS4QX8ZO4QW8yfF15u1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 585, - "totalSwipes": 280, - "username": "anikag", - "userId": "3wq3BJQSfvNcHKOB676aIo0ttC63", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 341, - "totalSwipes": 70, - "username": "dianav", - "userId": "4jupcphb7XPvfvYNdRPkBiUtxOd2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 259, - "totalSwipes": 47, - "username": "juliarrowan21", - "userId": "4MRAHVq0iNf9XWKMJ04mQ2PCzgk2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 430, - "totalSwipes": 316, - "username": "aeraapk", - "userId": "4PfJr5Py7VcLj32duxm52f2WXxd2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 16, - "totalSwipeTime": 5225, - "totalSwipes": 1141, - "username": "Jordan", - "userId": "4SL9TuZNCGVLCoiXiexCnKPAwo72", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 18, - "totalSwipeTime": 248149, - "totalSwipes": 62440, - "username": "ram_chitti", - "userId": "4xx7ekDmRWUA7VxyiASn7y9mXhq2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 4537, - "totalSwipes": 1739, - "username": "Jeff Lewis", - "userId": "56tUnqze11UO5TPwjEOUugHJpRV2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 892, - "totalSwipes": 185, - "username": "rafaello55555", - "userId": "57ePPh5M27OHJeoEDJ681bp8Z2D2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 354, - "totalSwipes": 92, - "username": "sarath", - "userId": "58QhlkZNbrhjz87CaD4qTTLp5Ki2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 9, - "totalSwipeTime": 6961, - "totalSwipes": 1561, - "username": "i.fecher", - "userId": "5com0SwjxJb3kRk0zLxJgKA5joH2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 16, - "totalSwipeTime": 14163, - "totalSwipes": 4898, - "username": "sm_21", - "userId": "5GcQFhvkEUZ4gOx6QRqYfDakLrG3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 324, - "totalSwipes": 184, - "username": "siddhik", - "userId": "5GkjMU7ROlfu9Vh6Xw0U2umip6m2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 14, - "totalSwipeTime": 20440, - "totalSwipes": 10046, - "username": "lily", - "userId": "5qB1GynAGtOXvz3cROsEAsLGtn82", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 319, - "totalSwipes": 74, - "username": "dhorn", - "userId": "5sH0UWbwzsYXaEgzArpN8mfKhbC2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 1817, - "totalSwipes": 507, - "username": "peacefulrita", - "userId": "5sJ2b98uXBNCkF3whJoH9oOuDuT2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 46159, - "totalSwipes": 20608, - "username": "Megha081", - "userId": "5vO3OZZ1HWgckkWr45rueF0GknD3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 10, - "totalSwipeTime": 1876, - "totalSwipes": 601, - "username": "AvivaLiu", - "userId": "6dkxMKQ2AtMesNj3SU4RCZbIDPw1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 362, - "totalSwipes": 74, - "username": "eeshanz", - "userId": "6h26Qyw0zzajnPJUy9n0dMnAeP22", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 466, - "totalSwipes": 120, - "username": "AryaJha", - "userId": "6nq5SHkCArZKiUh8h8BiYwl5qP12", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 60, - "totalSwipes": 37, - "username": "matthew.albano1", - "userId": "6XvRfvIC93gQr3d0nMAR0TG1sgO2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 622, - "totalSwipes": 148, - "username": "JSolerPA", - "userId": "722Zcqgo7tQ9fTseAmlDXC99cFV2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "JLee", - "userId": "79JKCvG9zXUNv4LSEelrbUZSCrz1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 2979, - "totalSwipes": 502, - "username": "mare", - "userId": "7akDlemMCFURcKChqs8c3ua3Rr22", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 75, - "totalSwipes": 37, - "username": "zgolds", - "userId": "7OzoUURgjoOlWzvA77mKEPEdtcY2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 466, - "totalSwipes": 111, - "username": "calimapnerd", - "userId": "7xfrcUFjw7WNrsVKUeENexN6cx42", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 4287, - "totalSwipes": 1809, - "username": "sanjana", - "userId": "7ZmwkRAV1YYaqAgA9GrY1BT3nir2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 2866, - "totalSwipes": 555, - "username": "eagle46", - "userId": "81cQDKwGzvW4IGVwIZdvZOHHtqi1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 390, - "totalSwipes": 148, - "username": "sandyrav", - "userId": "8bhk4Fb0MWadotpzO216ye4DUYO2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 209, - "totalSwipes": 37, - "username": "PORKCHOP", - "userId": "8P7XEirWiCa5EwvIuvxkTaJ6Bzo1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 623, - "totalSwipes": 148, - "username": "Meirina Hutabarat", - "userId": "8UOBmJHCCGX45buJgF6LBtl9nXz2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1410, - "totalSwipes": 259, - "username": "jpaduveris", - "userId": "98Y1CEGCofURSQIJhF1HGJHf2mC2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 63, - "totalSwipeTime": 20148, - "totalSwipes": 11011, - "username": "vmvm", - "userId": "9EiZFxEi7TXCaYGo8awsjZ1KdPI3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 784, - "totalSwipes": 447, - "username": "alaynaprice ", - "userId": "9ivwOjO4rUNCRxRLxQpIjqHiiju1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1005, - "totalSwipes": 185, - "username": "kjuvan", - "userId": "a04pYbvwD6fzMyaymZyIBOY7jTQ2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1888, - "totalSwipes": 592, - "username": "JoeGillian", - "userId": "A1JzBh0ZYPPaz0MX7quuVEN62Lw1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 913, - "totalSwipes": 132, - "username": "vmsomisetty", - "userId": "A3DnjcRrTpR8ST6nPFyI8bXPUHT2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 358, - "totalSwipes": 1295, - "username": "akashkumar", - "userId": "A9cjlAQiYvhZnxXezVQ0AsxVRxy1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 188, - "totalSwipes": 77, - "username": "tenia", - "userId": "aiwOkak1gsbPt90bfTbrIV2Ogr83", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 273, - "totalSwipes": 106, - "username": "mrod201000", - "userId": "ajnTnLHGnubB3LJ6lrCzjKaitsg1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 378, - "totalSwipes": 102, - "username": "Michael_T", - "userId": "ak3F5vvqUPbyyiGcyjrmMikU1su1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 125, - "totalSwipes": 79, - "username": "campbell_m", - "userId": "aKeR7PTAfofSG3jwM7E2ha2kWrj1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 17, - "totalSwipeTime": 13529, - "totalSwipes": 5736, - "username": "AsmiR1", - "userId": "aPQISKN6ypd9xC05aGzncCOnhFO2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 1436, - "totalSwipes": 378, - "username": "Adhithya", - "userId": "asnaSyGScKXdjKTbgeaJghd36lR2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 336, - "totalSwipes": 111, - "username": "francesca_otiniano", - "userId": "b9KhIjfRhaVWmyaez8TG1RXXphs2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 320, - "totalSwipes": 74, - "username": "ND04", - "userId": "bcWRCgpotRPbg6AaZcUg7uCntLD2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 7, - "totalSwipeTime": 350, - "totalSwipes": 260, - "username": "neervi", - "userId": "BEh2F32cBXgh1W4B40vBu1gL0ji1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 7, - "totalSwipeTime": 2015, - "totalSwipes": 984, - "username": "lrumple", - "userId": "BKblaH18kMaFuwwYLZyoMxkc0r32", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 134, - "totalSwipes": 16, - "username": "wolfegeo", - "userId": "BkMUEHyMBSYI938sbAUx2SmlUJf2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 278, - "totalSwipes": 188, - "username": "Vidhi Patel", - "userId": "bvuUhuPBXSTS6Kla94eVpVwQTcR2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 784, - "totalSwipes": 103, - "username": "allison.lalik", - "userId": "bVuxnsL7mueANiYNUTUNbfRIRf42", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 13, - "totalSwipeTime": 6517, - "totalSwipes": 3977, - "username": "sai_dirisina", - "userId": "c9EA7diVIxPwQfI4fWvy6Kguo2h1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "Stoic_Historian", - "userId": "cBZpfJzrVkau62jG2RTCxpqFKMo2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 186, - "totalSwipes": 37, - "username": "Widlande08", - "userId": "CClMxFYW8aMAg9hZf5YlrrLrYOk1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 225, - "totalSwipes": 68, - "username": "mattlee", - "userId": "CcVgipm7vGQXjY2ZSExMlKhwiQF3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 451, - "totalSwipes": 74, - "username": "autumnj0nes", - "userId": "CE960vgetUOAse82G2Vr81pEhO23", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 812, - "totalSwipes": 444, - "username": "anijahsayles", - "userId": "cFjV3WL06HbNUmhcyfBnVRPYTG83", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 2194, - "totalSwipes": 541, - "username": "Olivia ann Shibu", - "userId": "chnIwT1gpnggeDvmdv9gHrf0Zam2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 23, - "totalSwipeTime": 20933, - "totalSwipes": 11596, - "username": "sachchit", - "userId": "COCQwgVRREd8wAlhW2iJxU0I4L92", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 14, - "totalSwipeTime": 18448, - "totalSwipes": 8310, - "username": "saraht2007", - "userId": "CqA73FsHuJbLjmvvZG5w3p2QgO42", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 3016, - "totalSwipes": 542, - "username": "FrederikeH", - "userId": "crcnFe6tFFPfj6lhbVEJmQ8MF1U2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 174, - "totalSwipes": 37, - "username": "jpiehl34", - "userId": "d0jwYgZC62eU0WmVQQpMBMDsrO03", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 2214, - "totalSwipes": 1134, - "username": "Victoria", - "userId": "d8MZEpFNpqbmEFBXnxMdRYfuvNY2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 10107, - "totalSwipes": 2812, - "username": "kpolanco1120", - "userId": "DdhHvENYxXf0FusTdeGhju887HF2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 631, - "totalSwipes": 111, - "username": "KGartland", - "userId": "dFeAYxrlzMTDc2jUlJSMOB4fTbS2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 1933, - "totalSwipes": 921, - "username": "JLR NIC TIERNEY", - "userId": "DIu7VALwoTOTvm0aqkIXPvgl9x73", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 523, - "totalSwipes": 148, - "username": "JLR Beth", - "userId": "DNABjetEMZX5ILEa0mYb1pcztxx1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 352, - "totalSwipes": 96, - "username": "sruthi", - "userId": "DOgDzCQkjmSlfMkYwfLuwkYNOtg2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 163, - "totalSwipes": 37, - "username": "camiguzman", - "userId": "Dpd239EWM0R659nP7HzYYu55DB32", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 550, - "totalSwipes": 111, - "username": "mzlita", - "userId": "Dw5npFDv3Ed3GerOJaspyZCgpCA3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 5122, - "totalSwipes": 3700, - "username": "Abu-ammar Ghakhar", - "userId": "e5GMgxFHvXfdQOL7xa5PeeXQ12C2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1132, - "totalSwipes": 518, - "username": "aryav09", - "userId": "EIXbzVCl10cTOuISfhUCfDCqodH2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 5103, - "totalSwipes": 1924, - "username": "cnietsche", - "userId": "eLTIeNq1IDfs0dSgDFmUw6uuc1v1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1535, - "totalSwipes": 481, - "username": "Geo305", - "userId": "Etc2BwBAeuQNmUAKtJAIBc86i6V2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 34, - "totalSwipeTime": 149514, - "totalSwipes": 28596, - "username": "naseeha_masood", - "userId": "eVMRAVOUkrWkk3OCOi5qS8EYNhi2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 14, - "totalSwipeTime": 3843, - "totalSwipes": 1961, - "username": "justahelper", - "userId": "EzLDPAmne8WuLTxNPVb6RMTucWp1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 677, - "totalSwipes": 111, - "username": "lamaybin", - "userId": "f4jUTgeqlTdjNmHfH8J8FuO12xW2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 14, - "totalSwipeTime": 6311, - "totalSwipes": 2681, - "username": "neevneevv", - "userId": "FAg1dm3D4ffi4K5d2FSRE9p4rOe2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 517, - "totalSwipes": 156, - "username": "malysha19", - "userId": "fDxfYip0CEPukVK0IeMrCDpQgeO2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 11, - "totalSwipeTime": 22124, - "totalSwipes": 10148, - "username": "Benjam", - "userId": "fLgqmdP8HNZ6zatJ7eFKYPeEtPq2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 334, - "totalSwipes": 215, - "username": "BLESSING SONG", - "userId": "fYeVvqSYwMYQTcb5N0y03j5br692", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 310, - "totalSwipes": 88, - "username": "Saviana", - "userId": "G9SMvPNTeUf7CV5yjLpvFTEFRYz2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 193, - "totalSwipes": 37, - "username": "Garciaju037", - "userId": "geIABDUK0xdYh32bN4ozIUpCLDA3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1715, - "totalSwipes": 1055, - "username": "iangoley", - "userId": "GGHNqWxj5yaunOG1kcXjwif9WE83", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 17197, - "totalSwipes": 5365, - "username": "merlynaranha", - "userId": "gKUVPz01DDUQlhHeWffW1XZVlxX2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 3135, - "totalSwipes": 1459, - "username": "maneli", - "userId": "GpJ9IegUOvQX9qPtS3xjaKbLBNV2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 462, - "totalSwipes": 58, - "username": "pedrobueno", - "userId": "GPjgVef9DWaXAnUZnmk7ue2EQzj2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "erynkmoney", - "userId": "Gvoanxx3oNOPdBPZnuK1rQsyfrz2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 508, - "totalSwipes": 230, - "username": "Yuvraj", - "userId": "H1tQghMFYee6woH8qY1RUInj3Tm1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 558, - "totalSwipes": 227, - "username": "sharonanton21", - "userId": "hd2qQhwwGpN29OvucOlkJk75cVl2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 167, - "totalSwipes": 37, - "username": "emi.molla", - "userId": "HjUmmdZsPUgjiPdZOYNakqQUkjt2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1595, - "totalSwipes": 190, - "username": "saraa__s", - "userId": "hOoUVVU6vpfIfcO8V0t3YebLuZ03", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 262, - "totalSwipes": 81, - "username": "mgavin", - "userId": "HP4hRk6lFWQuRvFBoZZs2b4DIJ92", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 341, - "totalSwipes": 74, - "username": "mlayne29", - "userId": "hSxZ2jSEyVVJuxAHNtmJdPSswmC2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 108, - "totalSwipes": 37, - "username": "eliandres", - "userId": "huHyBy4WcPW5bBrpC0mp5COFwUx1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 8, - "totalSwipeTime": 1790, - "totalSwipes": 566, - "username": "stephmbwilson", - "userId": "HxUHd2R0pohG2bE1c3dw1CM8jfv2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 764, - "totalSwipes": 148, - "username": "Paulette Brown", - "userId": "HzTdhvKEwQeb4VRCfZgbpzewZL63", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 3267, - "totalSwipes": 703, - "username": "Kevi", - "userId": "i6ItLfEEX8Py5pdhZb3x9IZpCoq1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 5391, - "totalSwipes": 1184, - "username": "sss1447", - "userId": "IFhUqsPlqRNbLXAzc9vQFntXjQQ2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 130, - "totalSwipes": 58, - "username": "Izzy2crazy ", - "userId": "IHaZ6GYprVeeMHZNm09En4xtAcL2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 1227, - "totalSwipes": 527, - "username": "kvellatur", - "userId": "inuzkmShesYsvunH9uERhEn65qr2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 11, - "totalSwipeTime": 3222, - "totalSwipes": 1741, - "username": "BenjaminPoore", - "userId": "IY5F5WcChNOoLaf5mjb6WWzd4E93", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "Kittie", - "userId": "J9xpHpTi2IdrbU3YZF4mAgB0VT12", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 446, - "totalSwipes": 111, - "username": "DGGreen", - "userId": "jF7xnwm1ezaCH0o3brk4ilbuli82", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 434, - "totalSwipes": 169, - "username": "danbjoseph", - "userId": "JftvKNTCnBZzSq0LDoMNTP7lVLq2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 1625, - "totalSwipes": 287, - "username": "VAISHNIKA1711", - "userId": "jHASsntBjjfp7w3YwfHLihW4G792", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 646, - "totalSwipes": 407, - "username": "lyssa", - "userId": "ji06JiDZPdgckAqXS8pPpkZNfet1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 520, - "totalSwipes": 133, - "username": "Jelynz", - "userId": "JjuXTheJCQZ3Ma5z1XZeFCyk6v12", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1709, - "totalSwipes": 481, - "username": "mwcoulter", - "userId": "jMdaGHrVoWOSZAAmgaqQUAX04dS2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 9, - "totalSwipeTime": 8467, - "totalSwipes": 23581, - "username": "Charan", - "userId": "jZVHsz5uTahDDUqseTF9vrsPv1x1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 6, - "totalSwipeTime": 591, - "totalSwipes": 147, - "username": "Katbaloo ", - "userId": "kBa3j56BxJYflKZoyGi4ckDqcb63", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 187, - "totalSwipes": 37, - "username": "helloitsme", - "userId": "KJ3M05ezjuWxO6DvvLIxGaiDSwN2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 86, - "totalSwipes": 48, - "username": "Puja_K", - "userId": "kMAPG0Rgh8NxN4dVSEGCtX9qQpC3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 932, - "totalSwipes": 111, - "username": "llorigold", - "userId": "knPQQGuPvSW4FC1EmU8ORIPBlS73", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 300, - "totalSwipes": 111, - "username": "Tammy Walker", - "userId": "kQgzR3CXBAOl8VdBJrN6DiZfttH2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 1042, - "totalSwipes": 333, - "username": "atalia17", - "userId": "ks2e7UMObiTgfqhfdO6WsBPteWw1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 839, - "totalSwipes": 282, - "username": "", - "userId": "kWaffzrUCjclg3PTuJP8FoRSRev1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 193, - "totalSwipes": 99, - "username": "anushka", - "userId": "l1rCq4U5rEPn7PsOmMaXd9bNoYi2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 276, - "totalSwipes": 148, - "username": "JLRLAURAWOOD", - "userId": "LgR0G6F1XWfbp83T6MxkHJV473M2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 441, - "totalSwipes": 276, - "username": "luke2023", - "userId": "LMhZk387pXSMNaceVO4YmLDO5vk1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1565, - "totalSwipes": 777, - "username": "bhaveshmit", - "userId": "LSw2PHUUjQNPxvRgmUfFeljv8t83", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 93, - "totalSwipes": 45, - "username": "kgiamett", - "userId": "LuwOjIbYIjUecPDtrZntEuEBC6A2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 12591, - "totalSwipes": 3451, - "username": "mcreynolds", - "userId": "LWDO6xpaU1TjkjBEeaMeCen2WSR2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 30859, - "totalSwipes": 14614, - "username": "jacobk", - "userId": "LyKNT8FXQHhFNbhVfYqckoZTsIo2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 5388, - "totalSwipes": 2621, - "username": "Jazmin L", - "userId": "m4ak0ewiJQSjQ7i01CfTHNxVOkF2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 4235, - "totalSwipes": 753, - "username": "Priya D ", - "userId": "Ma1oiVuWy5UhqLZFNcH8nidciI23", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 3395, - "totalSwipes": 1043, - "username": "m.dunya", - "userId": "mFzfQtCMHgd66hr0MvGjHSTBtBE3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 7094, - "totalSwipes": 2988, - "username": "Aniket Mathur", - "userId": "MnoF74VqJOaHYni9Y7TrsimHsIj2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 85, - "totalSwipes": 37, - "username": "Chintan0530", - "userId": "mqMy55HR0OV1aGVRYTB4c5B0Bz63", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 328, - "totalSwipes": 74, - "username": "shanikquat", - "userId": "MqX4LrEM3xX0rP565ddSmO2rDLg2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 479, - "totalSwipes": 57, - "username": "quyenclifton", - "userId": "muGPqe0exSQOnxlVbX3gLol8oTw2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 268, - "totalSwipes": 90, - "username": "jparrino1", - "userId": "MUxz0uTTMCPPCoKfcQvVXewESBF2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 716, - "totalSwipes": 156, - "username": "mediaxina", - "userId": "my1FarXc6jbMtuSDyeblQfDMMbv2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 58, - "totalSwipes": 60, - "username": "KyleLehrfeld", - "userId": "mZKhfN9FQ9NotH3QJD5hdiIxjL63", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 389, - "totalSwipes": 111, - "username": "maj1874", - "userId": "N3x0i8zmwbZJS6CZoeo1WVezVv42", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 3732, - "totalSwipes": 1221, - "username": "Ashley J", - "userId": "N6onlKXBThb6CPElQJsTvLhc9yF3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 594, - "totalSwipes": 148, - "username": "Liza ", - "userId": "NBAfMZ2YMOgFpxH2ANFMd3OhHxU2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 7, - "totalSwipeTime": 7797, - "totalSwipes": 1836, - "username": "T_Wang", - "userId": "Nfib1B3MFqhqjbi3Ljr4AagRPDV2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 356, - "totalSwipes": 111, - "username": "JLR Mark Beamont", - "userId": "nH9r8YGAHvggVcHxLlj0Ca4SYI13", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 6, - "totalSwipeTime": 10152, - "totalSwipes": 2232, - "username": "anvita", - "userId": "NN1HPb2o7wVoEx5bDwgWBdytMwZ2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 536, - "totalSwipes": 96, - "username": "LHeaton", - "userId": "NraKWREgqBdcIfOTKbW9lg3R5dp2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "mina", - "userId": "nRjTrhQFU4Voq0I3ndfESQzWt1p2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 386, - "totalSwipes": 81, - "username": "elgurl", - "userId": "nVvqLkfYnERzRkQAwgJn5sQRI8k1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 412, - "totalSwipes": 164, - "username": "tindel02", - "userId": "Nyg0FmzjIth6gKe8FyT1miaAZKH3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 10, - "totalSwipeTime": 2053, - "totalSwipes": 625, - "username": "Puff", - "userId": "oFI5vcDzK6ZfH72jDkrAKYQPNkD3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 106, - "totalSwipes": 106, - "username": "ss09", - "userId": "oGfQitabVYWJ7cXpAPLPj6bZ2dq1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 7273, - "totalSwipes": 4255, - "username": "Linh Tran", - "userId": "oKuNEXIdmmhjAYziyEQKzuA2F8g1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 6, - "totalSwipeTime": 3548, - "totalSwipes": 906, - "username": "", - "userId": "osm:13675489", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 10, - "totalSwipeTime": 965, - "totalSwipes": 471, - "username": "", - "userId": "osm:16034655", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 29, - "totalSwipeTime": 84450, - "totalSwipes": 23745, - "username": "", - "userId": "osm:16361194", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "", - "userId": "osm:17082842", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 6993, - "totalSwipes": 2635, - "username": "", - "userId": "osm:19988894", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 153, - "totalSwipes": 74, - "username": "", - "userId": "osm:21700068", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 24, - "totalSwipeTime": 10713, - "totalSwipes": 6388, - "username": "", - "userId": "osm:3590711", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 334, - "totalSwipes": 74, - "username": "kalkidan", - "userId": "OYquaTFGOANuwHYQZ2hTCBxORgw1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 202, - "totalSwipes": 37, - "username": "JLR Amber Hickey", - "userId": "P0V8hpfwF9W1D0sTU8wV9gOPqB22", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 3336, - "totalSwipes": 710, - "username": "ihuckstorf", - "userId": "p1VeIV4k2Dfswvz5qOgGfgP9Qea2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1333, - "totalSwipes": 333, - "username": "kayakgirl", - "userId": "pINhNGLfuzUOE7LgDyTuk3B47dk1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 6, - "totalSwipeTime": 953, - "totalSwipes": 280, - "username": "TAsh2", - "userId": "PJtKS6RLJbXlWsKT3vtbXuK4U9r2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 8, - "totalSwipeTime": 23865, - "totalSwipes": 9168, - "username": "sunshine", - "userId": "pkUllUB7SDV97tI1xrT0Jher1jA3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "kaleb", - "userId": "PNIwioFW5mXodvlqS8hwTHQB0U32", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 1038, - "totalSwipes": 449, - "username": "helkam-2307", - "userId": "PORyLDshKCQ6A0wJke9LyggsAxz1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 570, - "totalSwipes": 444, - "username": "Cguerrero", - "userId": "Ptrq0b8LGYVLzH2yxrm2AMdmjrx1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 6, - "totalSwipeTime": 4091, - "totalSwipes": 1314, - "username": "sreebalaji", - "userId": "Qb6LA8r6vJMJiXIHW8vPAsUEET13", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 191, - "totalSwipes": 111, - "username": "vera.b.09", - "userId": "Qkf149DsN8WkaKyIzYCxTJFOEUD2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 522, - "totalSwipes": 150, - "username": "NakshatraN.", - "userId": "QTbLt8SOF4elbKZUHpzTG0bQ6dx2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 17, - "totalSwipeTime": 26769, - "totalSwipes": 6507, - "username": "ShreyaD", - "userId": "RjvXjOXh2PdvH8CpN3NoH27VWpq2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 959, - "totalSwipes": 395, - "username": "vikareel", - "userId": "rryJH5a63pg1e6hV9bN3qvlC8rX2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 2505, - "totalSwipes": 844, - "username": "Simran", - "userId": "RW5Gie2SN4eyxxxKuDDvZ5eI1N63", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 678, - "totalSwipes": 165, - "username": "MattHillsberg", - "userId": "rznLCLRp8ycsINpteJSX7FVePJZ2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1331, - "totalSwipes": 481, - "username": "MontielMA", - "userId": "s1R2jE1CIEWoAG60Gyf1R2QY1VS2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 983, - "totalSwipes": 222, - "username": "rico_yuen", - "userId": "S5LptVCLaCMz11AWbZhjgqJD1bk1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 450, - "totalSwipes": 298, - "username": "MalakB", - "userId": "SaSQM9g5lvh3SGXHxmLrZBYVhuA2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 8720, - "totalSwipes": 4592, - "username": "twood6", - "userId": "sfBcSvyIpXRRttvftwxrXwkqrw22", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 1972, - "totalSwipes": 673, - "username": "kerstin Luteijn", - "userId": "sfBRxfIugWgDAatAU6i39a0npSP2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 5612, - "totalSwipes": 3330, - "username": "jlong", - "userId": "SvoK1vssXCRxPO3DgbG9Fk1C16G2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 235, - "totalSwipes": 106, - "username": "hannahjhaslam", - "userId": "sWNuVEw2e7aKZW0Um51bzpzq1cD2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 2717, - "totalSwipes": 1295, - "username": "sarvagna", - "userId": "SWsHXNFPdDYknROrSEfU81yQXSj1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 2458, - "totalSwipes": 407, - "username": "ladypatrick", - "userId": "t7Bg020ct9NiitO3HOYUxuAIz9q2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 8, - "totalSwipeTime": 3896, - "totalSwipes": 767, - "username": "nchase", - "userId": "t9ro1FN82gTkAyfRMqcFLggWG5B2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 847, - "totalSwipes": 327, - "username": "aditi", - "userId": "tD71Y5BgxqONKoOiOphPqfVvT893", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 27, - "totalSwipes": 22, - "username": "Clevin", - "userId": "thPAcQ6Ez0UUDXNcBeI7TmlmWBv1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 41133, - "totalSwipes": 15927, - "username": "vnyc", - "userId": "TWrJa4vmdvRAW5fDtJ3YzjFViBi2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 27, - "totalSwipes": 22, - "username": "AnyaT", - "userId": "tZ7VXVwCj4MMrMJIwKuXiA5wP8j1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 103, - "totalSwipes": 42, - "username": "giaia.12", - "userId": "U9ZTuMgCpqbFZzHLpxn9eGNozwJ3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 2729, - "totalSwipes": 582, - "username": "hxbiscus", - "userId": "ub9xeBlXN6gozVV2ECQi0ClXwLF3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 8, - "totalSwipeTime": 8610, - "totalSwipes": 1910, - "username": "averymcreynolds1", - "userId": "UCbEXpe7oQVDdVSqkcHqcPw7vB93", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 1413, - "totalSwipes": 532, - "username": "lilly", - "userId": "uDtHTz3reeV7lhIHjAReWetsqFi1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 2327, - "totalSwipes": 444, - "username": "bergquistm10", - "userId": "UfPPaUGqoIeaYH5XuVR2VKoBCcK2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 6, - "totalSwipeTime": 2278, - "totalSwipes": 816, - "username": "pat_a", - "userId": "ujNA1G6HCFQrbj7T3owHY4waX7R2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 647, - "totalSwipes": 77, - "username": "kcrane29", - "userId": "UmtXMBT4rIfr8sGzg0icAcML7wt2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 73, - "totalSwipes": 37, - "username": "tamia", - "userId": "URGRsPnZE8WHbsvJ03m2IejqhB92", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "Jack", - "userId": "URKEiIRRwjck5fyhkla0cJoTao42", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 84, - "totalSwipes": 37, - "username": "kevindpatino", - "userId": "VAWoMvFzUSdrJqjQEpk7nRXcocL2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 719, - "totalSwipes": 104, - "username": "bcook", - "userId": "vbSZj8YWnjPb1lKXMCO2nkcbhzF3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 3952, - "totalSwipes": 691, - "username": "yash", - "userId": "vDooLtNIHZhtJu5XI90qBSR9yKp1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "LChiu14", - "userId": "VfA2C2e26bfm9ZFbd90byY37Yty1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 6, - "totalSwipeTime": 1943, - "totalSwipes": 471, - "username": "lizzymueller", - "userId": "vfaRCDHc8ScYibRWDMrIRsWeOKv1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 485, - "totalSwipes": 184, - "username": "chicken", - "userId": "VioR8A3qccTg8j3iOtbDotd64N13", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 17, - "totalSwipeTime": 11692, - "totalSwipes": 5276, - "username": "madelinesosa", - "userId": "VN5ufyAusDPskgb2zuAjIgrGbZe2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 136, - "totalSwipes": 37, - "username": "vashM17", - "userId": "VU9owscq8lPdSk48XfHYeXgJd5v1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 10, - "totalSwipeTime": 71164, - "totalSwipes": 16241, - "username": "allisonbooth", - "userId": "vUws3v2spThYkXTy5AupZ89QYBq1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 8, - "totalSwipeTime": 3664, - "totalSwipes": 1816, - "username": "rubybellin", - "userId": "w3wy48mTezQC2ZGg3iv4403QFG03", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 10, - "totalSwipeTime": 15434, - "totalSwipes": 5488, - "username": "maddi", - "userId": "W5G3QlDWtSh7wJKB5zx2gkAiKp43", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 4, - "totalSwipeTime": 22629, - "totalSwipes": 7565, - "username": "Anirudh Bhaskar ", - "userId": "W6jHlIpD1lbncTYGVkNQ2dNSnLR2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 377, - "totalSwipes": 162, - "username": "jparrino2", - "userId": "w87yDuUuZQWd3KIa5YH4sQv6ZCg2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 903, - "totalSwipes": 148, - "username": "KGiles", - "userId": "wCkYa2jqHoXHisAzasgAutlBJW42", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 1140, - "totalSwipes": 588, - "username": "Ananya Gokulakrishnan ", - "userId": "wd36liv3uvbNxEqqn9LwDcsY5OF3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 8, - "totalSwipeTime": 7686, - "totalSwipes": 2193, - "username": "Dulce", - "userId": "WJISWqxoqQVqqFNRh96h7Tj1kx03", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 14, - "totalSwipeTime": 10444, - "totalSwipes": 4123, - "username": "Joshua", - "userId": "WjJGpIo1dbRycL85DfgEzrC1GIx1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 2984, - "totalSwipes": 979, - "username": "ashleighkb1", - "userId": "WljutX8WXobSdLQyF47vgQX7C7N2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 1441, - "totalSwipes": 814, - "username": "mariajose_paredes", - "userId": "WqBnG6rYgjgIAANDrGXQON0XzXU2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 655, - "totalSwipes": 222, - "username": "kristian155", - "userId": "Ws96DLcxm1esEoI31ro35miZHru1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 192, - "totalSwipes": 56, - "username": "melin", - "userId": "wSK864BEb4QhG2ZnCk4wfiX2DzP2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 550, - "totalSwipes": 156, - "username": "Samara ", - "userId": "WsMV7uAly1PUQsjfIS8rN2Hrm1g1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 16, - "totalSwipes": 2, - "username": "ARiser90", - "userId": "WxRqWpZoV3UkerMSrnwnJSQy1Tk1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 473, - "totalSwipes": 185, - "username": "LouisDavidGH", - "userId": "x0LVOPt821cUe6YIJnp34aFOYYt2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 1459, - "totalSwipes": 407, - "username": "GabyM", - "userId": "XEjpAWkBIbdU6EtpLqKVt0NNVHI2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 468, - "totalSwipes": 57, - "username": "adidesai001", - "userId": "XpXTboEfF1SN5lu6gFuL2sKA9812", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 1096, - "totalSwipes": 465, - "username": "rohita", - "userId": "xt2VciINzqVYJsQcui7ySeEKJTD2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 5, - "totalSwipeTime": 2499, - "totalSwipes": 978, - "username": "Bella Gupta", - "userId": "xthkJfsVO6XoKTqVaJplDMUDd2j1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 844, - "totalSwipes": 206, - "username": "nurselynds", - "userId": "Y3t4CFNmxuO734dPSbwufg5gUtC2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1538, - "totalSwipes": 370, - "username": "alyssaclement", - "userId": "y8CVrhWml2dZC9WFMaYHrmRqxpv2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 3247, - "totalSwipes": 1110, - "username": "Rama", - "userId": "y9qEwjLW4YUtOvqYEBwD2abcyUk2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 2540, - "totalSwipes": 555, - "username": "enzopaesano", - "userId": "yfHPTMNbLRSjsS6lLOOwc3YiFlA3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 208, - "totalSwipes": 148, - "username": "Nur Harun", - "userId": "YI4azbugi1Qw5hGDplrU1Sh6JIH3", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 1123, - "totalSwipes": 256, - "username": "ogh854", - "userId": "YngZiAw9aSQFu5Tk8b0CGbWdDHN2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 3569, - "totalSwipes": 1243, - "username": "ghazala_safi", - "userId": "yqGqGfFc62UxSpeDDZhvRUmC5GZ2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 479, - "totalSwipes": 57, - "username": "DenverDonna", - "userId": "YVTMOfOldKXEV55u8bYYpHHheub2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "sluggirl729", - "userId": "z5CHvw22pCXXIjn4EHT7Q1oISF03", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 1334, - "totalSwipes": 2664, - "username": "Deborah Jackson ", - "userId": "zChmJkC5qjWTJ6RKzYI977fv3vX2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 7, - "totalSwipeTime": 4840, - "totalSwipes": 1138, - "username": "vebo", - "userId": "ZCXKTgbvvrVGmWFBiXpdIaMKbMs2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 3, - "totalSwipeTime": 1852, - "totalSwipes": 558, - "username": "juliamoss", - "userId": "ZlOwTghsAaXdpDqwWDrlTEXWTeg2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 371, - "totalSwipes": 120, - "username": "Owen_Linder", - "userId": "ZnUS1CRxGmRBBriZUPfJeKLVhi52", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 240, - "totalSwipes": 99, - "username": "lokelanim", - "userId": "ZRU4k1c2AJZvgpJ2DswOijSopdu1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 2, - "totalSwipeTime": 3716, - "totalSwipes": 3885, - "username": "klausullm", - "userId": "ZXbv6GVKx0bE5QVkymOjUmMcybh1", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 454, - "totalSwipes": 54, - "username": "RedCrossSophia", - "userId": "ZxgrRrgvS4atvs1tougXDmOAIKq2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "Bhavi3125", - "userId": "ZY9po8URjHa7iUa3irBDQzlfCOT2", - "__typename": "UserGroupUserStatsType" - }, - { - "totalMappingProjects": 1, - "totalSwipeTime": 226, - "totalSwipes": 37, - "username": "Leca", - "userId": "zyAUPW37LrXILyeSkCi2wkR2lYK2", - "__typename": "UserGroupUserStatsType" - } - ], - "contributionByGeo": [ - { - "geojson": { - "type": "Point", - "coordinates": [ - 92.73140175431497, - 23.499270969630363 - ] - }, - "totalContribution": 397, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -70.84958517762927, - -1.355191715444946 - ] - }, - "totalContribution": 416, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -85.1198964514573, - 13.980393189284156 - ] - }, - "totalContribution": 1446, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 92.92127382802134, - 24.286809287816997 - ] - }, - "totalContribution": 372, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -75.61141649329109, - 1.343481984982563 - ] - }, - "totalContribution": 160, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 143.61353859029757, - -3.952259450082971 - ] - }, - "totalContribution": 10533, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 37.85053622258006, - 1.091773186351989 - ] - }, - "totalContribution": 755, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 26.61876826407039, - 9.423529594125787 - ] - }, - "totalContribution": 460, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -90.2894587857745, - 19.59991189757706 - ] - }, - "totalContribution": 642, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -52.75324318273184, - 47.6591391076756 - ] - }, - "totalContribution": 53, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 38.66164175514844, - -15.399619311380487 - ] - }, - "totalContribution": 1236, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 37.55277057498438, - -15.428707695616959 - ] - }, - "totalContribution": 6929, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -72.31600045, - 18.68403465 - ] - }, - "totalContribution": 8000, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 171.20578760000004, - 7.1366634 - ] - }, - "totalContribution": 1610, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -72.64747093368038, - -1.361696978303415 - ] - }, - "totalContribution": 1919, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -78.8843802763227, - -4.010091566306465 - ] - }, - "totalContribution": 3, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -62.95436691259595, - -4.021184656245459 - ] - }, - "totalContribution": 462, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -89.14110606622654, - 15.970939244815773 - ] - }, - "totalContribution": 410, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 34.95859115, - 1.50696485 - ] - }, - "totalContribution": 37, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 68.7095207013628, - 36.26334834780437 - ] - }, - "totalContribution": 762, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -68.56794167938004, - -10.518198604197824 - ] - }, - "totalContribution": 46, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 144.59421611842083, - -5.797188668500342 - ] - }, - "totalContribution": 199, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -51.317165991121456, - -29.970770738273576 - ] - }, - "totalContribution": 25, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 82.21697136250522, - 29.271747744332938 - ] - }, - "totalContribution": 2457, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -83.55559415389867, - 14.739167328368252 - ] - }, - "totalContribution": 535, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -75.29435521590999, - 1.021080400450334 - ] - }, - "totalContribution": 32, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 89.34596785, - 23.7691698 - ] - }, - "totalContribution": 9990, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -84.77268253283326, - 11.546148902370424 - ] - }, - "totalContribution": 3432, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 32.097337697983995, - -19.672736743975427 - ] - }, - "totalContribution": 5632, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -78.55328208504923, - -5.007382183145697 - ] - }, - "totalContribution": 5493, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 69.49271542106688, - 35.96371220019021 - ] - }, - "totalContribution": 7, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -79.0948214265807, - -5.621316804838495 - ] - }, - "totalContribution": 567, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 143.1167797438696, - -4.656960795757978 - ] - }, - "totalContribution": 3432, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -78.0547666676697, - -2.825082939722951 - ] - }, - "totalContribution": 7499, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -79.06916177979166, - -3.034005965493468 - ] - }, - "totalContribution": 102, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -71.89545587098073, - 1.298110173544335 - ] - }, - "totalContribution": 3664, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -77.47483008962396, - -2.671800082849541 - ] - }, - "totalContribution": 1828, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -81.23746142111578, - 48.509127771876024 - ] - }, - "totalContribution": 300, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -72.50470642563677, - 2.549833320740754 - ] - }, - "totalContribution": 2863, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 34.32541349999999, - -14.3831562 - ] - }, - "totalContribution": 14400, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -67.77638302730661, - 2.318842316135067 - ] - }, - "totalContribution": 918, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -71.95473727023082, - 2.59894179622379 - ] - }, - "totalContribution": 4648, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -61.684001119372546, - 12.113960230297222 - ] - }, - "totalContribution": 1450, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -85.45517170072486, - 13.999002771878763 - ] - }, - "totalContribution": 1499, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 32.721361204259374, - -19.781151286275083 - ] - }, - "totalContribution": 652, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -83.95687139107329, - 14.409415061907099 - ] - }, - "totalContribution": 198, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 69.05683960392936, - 35.51029701534008 - ] - }, - "totalContribution": 206, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -88.29635175, - 18.5011917 - ] - }, - "totalContribution": 4440, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 37.77650094469254, - -15.869041578395171 - ] - }, - "totalContribution": 2509, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -70.39905739931731, - -3.075525011594448 - ] - }, - "totalContribution": 11, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 68.33781411579383, - 35.32722110463547 - ] - }, - "totalContribution": 1372, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -74.18843171205127, - 1.838209088567622 - ] - }, - "totalContribution": 2615, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 143.152434612217, - -5.5812882243965 - ] - }, - "totalContribution": 224, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -83.52420997372839, - 14.245248145874273 - ] - }, - "totalContribution": 2546, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -73.10396834864834, - 2.539712405290437 - ] - }, - "totalContribution": 4794, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -100.82986828280633, - 19.335069136307755 - ] - }, - "totalContribution": 4648, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -102.1680905738908, - 19.56164698137183 - ] - }, - "totalContribution": 743, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -85.90812230086513, - 13.486127873926645 - ] - }, - "totalContribution": 395, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -114.56995240824085, - 35.40558885777537 - ] - }, - "totalContribution": 4602, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -74.77129309897693, - -8.361615584031053 - ] - }, - "totalContribution": 7801, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 82.37118483115022, - 29.61829835532351 - ] - }, - "totalContribution": 964, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 92.78453223277066, - 22.739613534421945 - ] - }, - "totalContribution": 2755, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 31.693173872400514, - -19.26958190745124 - ] - }, - "totalContribution": 6820, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -70.61099417706933, - 1.280501916282569 - ] - }, - "totalContribution": 389, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 142.69498962645142, - -4.279295885667389 - ] - }, - "totalContribution": 1794, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -76.08583221934292, - 1.188235729693674 - ] - }, - "totalContribution": 976, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -72.66913820889279, - 1.188377298969117 - ] - }, - "totalContribution": 1619, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 105.18473205, - -3.9566782 - ] - }, - "totalContribution": 82927, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 92.5258853750819, - 23.232898358393314 - ] - }, - "totalContribution": 1929, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 143.58635692996836, - -4.368596348999677 - ] - }, - "totalContribution": 1747, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 80.72860198207461, - 6.417804846704624 - ] - }, - "totalContribution": 1477, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -61.68237405622985, - -3.810160941839017 - ] - }, - "totalContribution": 370, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 144.66991719031637, - -5.793820739253102 - ] - }, - "totalContribution": 262, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 101.70452494312204, - 4.962856375896156 - ] - }, - "totalContribution": 1396, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -71.17855963901002, - -0.791600935042186 - ] - }, - "totalContribution": 2441, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -76.71958133476728, - 0.565003176010218 - ] - }, - "totalContribution": 3774, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 105.16145765, - -3.9464773 - ] - }, - "totalContribution": 24281, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 144.6266440884106, - -5.548789486185929 - ] - }, - "totalContribution": 53, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 142.75920024697723, - -3.870628843450433 - ] - }, - "totalContribution": 2412, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 92.85326093882071, - 23.268100438516683 - ] - }, - "totalContribution": 1501, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -74.34180167241547, - 2.51128934143724 - ] - }, - "totalContribution": 3997, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 143.82751221044325, - -5.291182706201177 - ] - }, - "totalContribution": 380, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 30.64303568483182, - 9.324525428092427 - ] - }, - "totalContribution": 425, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 93.22530083821907, - 23.447192435994232 - ] - }, - "totalContribution": 1360, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -75.61529423316709, - 1.019424583700923 - ] - }, - "totalContribution": 700, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 102.08069452181265, - 5.67114619510633 - ] - }, - "totalContribution": 182, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 32.755421668802896, - -17.93530847459356 - ] - }, - "totalContribution": 1751, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 144.54650766979148, - -6.040632954067647 - ] - }, - "totalContribution": 2544, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -62.95436691259595, - -4.021184656245459 - ] - }, - "totalContribution": 158, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -73.45838763797263, - 3.212288633510766 - ] - }, - "totalContribution": 309, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 32.352349559096695, - -20.849241579120466 - ] - }, - "totalContribution": 56, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 93.1666453156887, - 23.6544015985086 - ] - }, - "totalContribution": 95, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -61.31402437345745, - 12.814700648940763 - ] - }, - "totalContribution": 387, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 30.627553545784952, - 8.588993868708355 - ] - }, - "totalContribution": 60, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -62.33763406010854, - -3.717180796936141 - ] - }, - "totalContribution": 273, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -88.99009388228792, - 16.967918154079996 - ] - }, - "totalContribution": 171, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -79.13114579286535, - -5.11558990014555 - ] - }, - "totalContribution": 3564, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -85.10877408131385, - 14.361584805486794 - ] - }, - "totalContribution": 6285, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -77.34633979411231, - -3.761121621878996 - ] - }, - "totalContribution": 3005, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -61.68237405622985, - -3.810160941839017 - ] - }, - "totalContribution": 555, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 92.92129956718755, - 23.45036122659758 - ] - }, - "totalContribution": 4378, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 35.03770801825091, - -15.771260184591872 - ] - }, - "totalContribution": 2688, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -85.23541754178115, - 12.251953989338162 - ] - }, - "totalContribution": 2596, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 105.18227995, - -3.9473116 - ] - }, - "totalContribution": 30856, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -72.52089048670294, - 1.926544828731387 - ] - }, - "totalContribution": 6468, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 120.38377675, - 16.83994845 - ] - }, - "totalContribution": 4207, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -78.2610077889491, - -3.214116548452895 - ] - }, - "totalContribution": 922, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 22.003747077124736, - 13.426280745812956 - ] - }, - "totalContribution": 107, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 144.59421611842083, - -5.797188668500342 - ] - }, - "totalContribution": 1101, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -74.820469509669, - 1.201661027388751 - ] - }, - "totalContribution": 2408, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 90.00115255, - 23.60059035 - ] - }, - "totalContribution": 59250, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -68.07932565216437, - 2.618340494921993 - ] - }, - "totalContribution": 1451, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 93.01364059226972, - 23.973280563762692 - ] - }, - "totalContribution": 325, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -68.41429488863824, - -10.658810636419465 - ] - }, - "totalContribution": 1252, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 32.562545708705656, - -20.24707721355555 - ] - }, - "totalContribution": 2681, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -85.72679359927517, - 13.364263828813872 - ] - }, - "totalContribution": 338, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -114.54959912741137, - 34.77524490124089 - ] - }, - "totalContribution": 1503, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 92.95309353117739, - 22.9696207456173 - ] - }, - "totalContribution": 1553, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 92.86176603973098, - 23.736902703039004 - ] - }, - "totalContribution": 965, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -62.95436691259595, - -4.021184656245459 - ] - }, - "totalContribution": 135, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 123.09181583505686, - 10.44638206428209 - ] - }, - "totalContribution": 1414, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -74.94386755939892, - -9.567978301900402 - ] - }, - "totalContribution": 2248, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -75.29126082683655, - 1.342510326407644 - ] - }, - "totalContribution": 115, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 0.676875419618685, - 7.209097585678581 - ] - }, - "totalContribution": 365, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 93.23453293584782, - 23.208784155687574 - ] - }, - "totalContribution": 687, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -78.541114166535, - -3.474992601520388 - ] - }, - "totalContribution": 195, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 93.06846162489438, - 23.14066680769323 - ] - }, - "totalContribution": 154, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -89.06064147641405, - 16.205240264487035 - ] - }, - "totalContribution": 518, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -74.81170044558343, - -8.920617444439028 - ] - }, - "totalContribution": 1165, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 68.54999621353622, - 35.783279392246335 - ] - }, - "totalContribution": 1783, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 4.477416918046016, - 7.729973758854705 - ] - }, - "totalContribution": 80, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -100.8300346140192, - 19.33592913127203 - ] - }, - "totalContribution": 841, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -73.38392646574127, - 2.966663019116976 - ] - }, - "totalContribution": 1831, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 39.138121600783734, - -15.124347352480978 - ] - }, - "totalContribution": 3706, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -78.42156759295283, - -0.443712017669453 - ] - }, - "totalContribution": 38, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 143.70645523271023, - -5.718813652819843 - ] - }, - "totalContribution": 1482, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 30.605391623974732, - 8.968571271460986 - ] - }, - "totalContribution": 268, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -74.81467757761713, - 1.833465408502024 - ] - }, - "totalContribution": 1139, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -61.188799541680694, - 13.250572531442755 - ] - }, - "totalContribution": 836, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -73.66162470802598, - 3.329978677963408 - ] - }, - "totalContribution": 948, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -77.94109306086266, - -2.183614308686258 - ] - }, - "totalContribution": 3044, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - -73.701759296725, - 2.967536670595839 - ] - }, - "totalContribution": 1983, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 143.17302026267424, - -3.554419496601139 - ] - }, - "totalContribution": 2867, - "__typename": "MapContributionStatsType" - }, - { - "geojson": { - "type": "Point", - "coordinates": [ - 92.6016404005226, - 22.98291123896723 - ] - }, - "totalContribution": 2757, - "__typename": "MapContributionStatsType" - } - ], - "areaSwipedByProjectType": [ - { - "totalArea": 42444.46410970643, - "projectTypeDisplay": "Find", - "projectType": "BUILD_AREA", - "__typename": "ProjectTypeAreaStatsType" - }, - { - "totalArea": 13.23154020152575, - "projectTypeDisplay": "Compare", - "projectType": "CHANGE_DETECTION", - "__typename": "ProjectTypeAreaStatsType" - }, - { - "totalArea": 0.0, - "projectTypeDisplay": "Validate", - "projectType": "FOOTPRINT", - "__typename": "ProjectTypeAreaStatsType" - } - ], - "swipeByDate": [ - { - "taskDate": "2024-01-01", - "totalSwipes": 150, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-02", - "totalSwipes": 53, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-03", - "totalSwipes": 177, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-04", - "totalSwipes": 490, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-05", - "totalSwipes": 217, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-06", - "totalSwipes": 117, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-08", - "totalSwipes": 535, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-09", - "totalSwipes": 712, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-10", - "totalSwipes": 437, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-11", - "totalSwipes": 1874, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-12", - "totalSwipes": 89, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-13", - "totalSwipes": 1702, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-14", - "totalSwipes": 2413, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-15", - "totalSwipes": 1317, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-16", - "totalSwipes": 1051, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-17", - "totalSwipes": 4032, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-18", - "totalSwipes": 991, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-19", - "totalSwipes": 1331, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-20", - "totalSwipes": 1845, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-21", - "totalSwipes": 240, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-22", - "totalSwipes": 567, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-23", - "totalSwipes": 236, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-24", - "totalSwipes": 691, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-25", - "totalSwipes": 392, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-26", - "totalSwipes": 912, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-27", - "totalSwipes": 1279, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-28", - "totalSwipes": 1324, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-29", - "totalSwipes": 683, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-30", - "totalSwipes": 917, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-01-31", - "totalSwipes": 596, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-01", - "totalSwipes": 833, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-02", - "totalSwipes": 1202, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-03", - "totalSwipes": 267, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-04", - "totalSwipes": 2934, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-05", - "totalSwipes": 1619, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-06", - "totalSwipes": 164, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-07", - "totalSwipes": 104, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-08", - "totalSwipes": 4893, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-09", - "totalSwipes": 740, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-10", - "totalSwipes": 3039, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-11", - "totalSwipes": 851, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-12", - "totalSwipes": 333, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-13", - "totalSwipes": 791, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-14", - "totalSwipes": 1381, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-15", - "totalSwipes": 355, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-16", - "totalSwipes": 1644, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-17", - "totalSwipes": 6664, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-18", - "totalSwipes": 673, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-19", - "totalSwipes": 538, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-20", - "totalSwipes": 888, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-21", - "totalSwipes": 1471, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-22", - "totalSwipes": 3016, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-23", - "totalSwipes": 148, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-24", - "totalSwipes": 795, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-25", - "totalSwipes": 2476, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-26", - "totalSwipes": 2247, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-27", - "totalSwipes": 839, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-28", - "totalSwipes": 1357, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-02-29", - "totalSwipes": 2109, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-01", - "totalSwipes": 1660, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-02", - "totalSwipes": 100, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-03", - "totalSwipes": 268, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-04", - "totalSwipes": 1131, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-05", - "totalSwipes": 1095, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-06", - "totalSwipes": 164, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-07", - "totalSwipes": 1784, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-08", - "totalSwipes": 962, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-09", - "totalSwipes": 96, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-11", - "totalSwipes": 824, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-12", - "totalSwipes": 464, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-13", - "totalSwipes": 999, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-14", - "totalSwipes": 378, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-15", - "totalSwipes": 823, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-16", - "totalSwipes": 1409, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-17", - "totalSwipes": 1224, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-18", - "totalSwipes": 543, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-19", - "totalSwipes": 443, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-20", - "totalSwipes": 2266, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-21", - "totalSwipes": 1162, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-22", - "totalSwipes": 968, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-23", - "totalSwipes": 581, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-24", - "totalSwipes": 259, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-25", - "totalSwipes": 581, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-26", - "totalSwipes": 1299, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-27", - "totalSwipes": 1363, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-28", - "totalSwipes": 1517, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-29", - "totalSwipes": 1653, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-30", - "totalSwipes": 826, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-03-31", - "totalSwipes": 2431, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-01", - "totalSwipes": 2379, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-02", - "totalSwipes": 3936, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-03", - "totalSwipes": 1483, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-05", - "totalSwipes": 1103, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-06", - "totalSwipes": 1049, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-07", - "totalSwipes": 390, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-08", - "totalSwipes": 600, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-09", - "totalSwipes": 244, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-10", - "totalSwipes": 630, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-11", - "totalSwipes": 270, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-12", - "totalSwipes": 129, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-13", - "totalSwipes": 1284, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-14", - "totalSwipes": 81, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-15", - "totalSwipes": 2248, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-16", - "totalSwipes": 2263, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-17", - "totalSwipes": 2646, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-18", - "totalSwipes": 11581, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-19", - "totalSwipes": 4650, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-20", - "totalSwipes": 1445, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-21", - "totalSwipes": 3370, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-22", - "totalSwipes": 977, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-23", - "totalSwipes": 1698, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-24", - "totalSwipes": 733, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-25", - "totalSwipes": 3778, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-26", - "totalSwipes": 1484, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-27", - "totalSwipes": 992, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-28", - "totalSwipes": 495, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-29", - "totalSwipes": 1134, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-04-30", - "totalSwipes": 988, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-01", - "totalSwipes": 927, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-02", - "totalSwipes": 1212, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-03", - "totalSwipes": 1004, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-04", - "totalSwipes": 833, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-05", - "totalSwipes": 452, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-06", - "totalSwipes": 329, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-07", - "totalSwipes": 1039, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-08", - "totalSwipes": 889, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-09", - "totalSwipes": 1731, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-10", - "totalSwipes": 1150, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-11", - "totalSwipes": 1077, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-12", - "totalSwipes": 1047, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-13", - "totalSwipes": 1431, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-14", - "totalSwipes": 2849, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-15", - "totalSwipes": 148, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-16", - "totalSwipes": 1751, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-17", - "totalSwipes": 129, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-18", - "totalSwipes": 123, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-19", - "totalSwipes": 203, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-20", - "totalSwipes": 295, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-21", - "totalSwipes": 439, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-22", - "totalSwipes": 339, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-23", - "totalSwipes": 1345, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-24", - "totalSwipes": 327, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-25", - "totalSwipes": 604, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-26", - "totalSwipes": 421, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-27", - "totalSwipes": 3735, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-28", - "totalSwipes": 251, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-29", - "totalSwipes": 1286, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-30", - "totalSwipes": 1342, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-05-31", - "totalSwipes": 1194, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-01", - "totalSwipes": 320, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-02", - "totalSwipes": 1384, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-03", - "totalSwipes": 1429, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-04", - "totalSwipes": 835, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-05", - "totalSwipes": 1323, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-06", - "totalSwipes": 1337, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-07", - "totalSwipes": 2549, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-08", - "totalSwipes": 1520, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-09", - "totalSwipes": 502, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-10", - "totalSwipes": 2661, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-11", - "totalSwipes": 4624, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-12", - "totalSwipes": 4448, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-13", - "totalSwipes": 2256, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-14", - "totalSwipes": 4423, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-15", - "totalSwipes": 1487, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-16", - "totalSwipes": 2861, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-17", - "totalSwipes": 2434, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-18", - "totalSwipes": 4536, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-19", - "totalSwipes": 546, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-20", - "totalSwipes": 8978, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-21", - "totalSwipes": 8285, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-22", - "totalSwipes": 3629, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-23", - "totalSwipes": 3291, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-24", - "totalSwipes": 6122, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-25", - "totalSwipes": 2843, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-26", - "totalSwipes": 2070, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-27", - "totalSwipes": 3498, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-28", - "totalSwipes": 5176, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-29", - "totalSwipes": 4510, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-06-30", - "totalSwipes": 7587, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-01", - "totalSwipes": 3460, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-02", - "totalSwipes": 2952, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-03", - "totalSwipes": 2155, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-04", - "totalSwipes": 3203, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-05", - "totalSwipes": 6370, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-06", - "totalSwipes": 5166, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-07", - "totalSwipes": 2195, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-08", - "totalSwipes": 2534, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-09", - "totalSwipes": 2240, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-10", - "totalSwipes": 3782, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-11", - "totalSwipes": 3770, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-12", - "totalSwipes": 7828, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-13", - "totalSwipes": 2914, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-14", - "totalSwipes": 3095, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-15", - "totalSwipes": 930, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-16", - "totalSwipes": 4203, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-17", - "totalSwipes": 2179, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-18", - "totalSwipes": 1572, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-19", - "totalSwipes": 4367, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-20", - "totalSwipes": 5931, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-21", - "totalSwipes": 6307, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-22", - "totalSwipes": 790, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-23", - "totalSwipes": 1600, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-24", - "totalSwipes": 4985, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-25", - "totalSwipes": 1678, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-26", - "totalSwipes": 2750, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-27", - "totalSwipes": 3452, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-28", - "totalSwipes": 1035, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-29", - "totalSwipes": 1327, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-30", - "totalSwipes": 1606, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-07-31", - "totalSwipes": 963, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-01", - "totalSwipes": 2370, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-02", - "totalSwipes": 1724, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-03", - "totalSwipes": 3060, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-04", - "totalSwipes": 344, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-05", - "totalSwipes": 3560, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-06", - "totalSwipes": 4064, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-07", - "totalSwipes": 3505, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-08", - "totalSwipes": 3457, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-09", - "totalSwipes": 3198, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-10", - "totalSwipes": 1355, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-11", - "totalSwipes": 858, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-12", - "totalSwipes": 2877, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-13", - "totalSwipes": 1722, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-14", - "totalSwipes": 333, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-15", - "totalSwipes": 1217, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-16", - "totalSwipes": 3517, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-17", - "totalSwipes": 2471, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-18", - "totalSwipes": 3448, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-19", - "totalSwipes": 2057, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-20", - "totalSwipes": 767, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-21", - "totalSwipes": 1107, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-22", - "totalSwipes": 2119, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-23", - "totalSwipes": 1027, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-24", - "totalSwipes": 1959, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-25", - "totalSwipes": 2311, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-26", - "totalSwipes": 3513, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-27", - "totalSwipes": 3308, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-28", - "totalSwipes": 898, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-29", - "totalSwipes": 740, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-30", - "totalSwipes": 868, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-08-31", - "totalSwipes": 1317, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-01", - "totalSwipes": 1118, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-02", - "totalSwipes": 2675, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-03", - "totalSwipes": 1854, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-04", - "totalSwipes": 1241, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-05", - "totalSwipes": 2058, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-06", - "totalSwipes": 4152, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-07", - "totalSwipes": 4136, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-08", - "totalSwipes": 2227, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-09", - "totalSwipes": 993, - "__typename": "ContributorSwipeStatType" - }, - { - "taskDate": "2024-09-10", - "totalSwipes": 1864, - "__typename": "ContributorSwipeStatType" - } - ], - "swipeTimeByDate": [ - { - "date": "2024-01-01", - "totalSwipeTime": 175, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-02", - "totalSwipeTime": 180, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-03", - "totalSwipeTime": 603, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-04", - "totalSwipeTime": 1230, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-05", - "totalSwipeTime": 953, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-06", - "totalSwipeTime": 273, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-08", - "totalSwipeTime": 797, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-09", - "totalSwipeTime": 1731, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-10", - "totalSwipeTime": 1305, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-11", - "totalSwipeTime": 5174, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-12", - "totalSwipeTime": 545, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-13", - "totalSwipeTime": 4021, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-14", - "totalSwipeTime": 4618, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-15", - "totalSwipeTime": 1908, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-16", - "totalSwipeTime": 2604, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-17", - "totalSwipeTime": 11725, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-18", - "totalSwipeTime": 1719, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-19", - "totalSwipeTime": 5267, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-20", - "totalSwipeTime": 3517, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-21", - "totalSwipeTime": 480, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-22", - "totalSwipeTime": 2051, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-23", - "totalSwipeTime": 422, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-24", - "totalSwipeTime": 1964, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-25", - "totalSwipeTime": 1847, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-26", - "totalSwipeTime": 2799, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-27", - "totalSwipeTime": 3395, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-28", - "totalSwipeTime": 3548, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-29", - "totalSwipeTime": 1653, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-30", - "totalSwipeTime": 4544, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-01-31", - "totalSwipeTime": 1741, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-01", - "totalSwipeTime": 2979, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-02", - "totalSwipeTime": 4675, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-03", - "totalSwipeTime": 568, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-04", - "totalSwipeTime": 1838, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-05", - "totalSwipeTime": 3298, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-06", - "totalSwipeTime": 146, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-07", - "totalSwipeTime": 374, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-08", - "totalSwipeTime": 9621, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-09", - "totalSwipeTime": 218, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-10", - "totalSwipeTime": 1475, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-11", - "totalSwipeTime": 263, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-12", - "totalSwipeTime": 349, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-13", - "totalSwipeTime": 678, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-14", - "totalSwipeTime": 2285, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-15", - "totalSwipeTime": 1899, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-16", - "totalSwipeTime": 3960, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-17", - "totalSwipeTime": 3420, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-18", - "totalSwipeTime": 315, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-19", - "totalSwipeTime": 1535, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-20", - "totalSwipeTime": 1605, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-21", - "totalSwipeTime": 2864, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-22", - "totalSwipeTime": 2317, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-23", - "totalSwipeTime": 488, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-24", - "totalSwipeTime": 1037, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-25", - "totalSwipeTime": 5768, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-26", - "totalSwipeTime": 5507, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-27", - "totalSwipeTime": 2143, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-28", - "totalSwipeTime": 2008, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-02-29", - "totalSwipeTime": 8980, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-01", - "totalSwipeTime": 7311, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-02", - "totalSwipeTime": 131, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-03", - "totalSwipeTime": 813, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-04", - "totalSwipeTime": 1621, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-05", - "totalSwipeTime": 2564, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-06", - "totalSwipeTime": 378, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-07", - "totalSwipeTime": 4012, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-08", - "totalSwipeTime": 655, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-09", - "totalSwipeTime": 254, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-11", - "totalSwipeTime": 1196, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-12", - "totalSwipeTime": 589, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-13", - "totalSwipeTime": 1229, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-14", - "totalSwipeTime": 1867, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-15", - "totalSwipeTime": 3201, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-16", - "totalSwipeTime": 4574, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-17", - "totalSwipeTime": 3264, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-18", - "totalSwipeTime": 1980, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-19", - "totalSwipeTime": 3248, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-20", - "totalSwipeTime": 6519, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-21", - "totalSwipeTime": 2882, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-22", - "totalSwipeTime": 4142, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-23", - "totalSwipeTime": 2329, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-24", - "totalSwipeTime": 548, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-25", - "totalSwipeTime": 1931, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-26", - "totalSwipeTime": 3759, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-27", - "totalSwipeTime": 7180, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-28", - "totalSwipeTime": 4524, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-29", - "totalSwipeTime": 6138, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-30", - "totalSwipeTime": 4782, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-03-31", - "totalSwipeTime": 10235, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-01", - "totalSwipeTime": 6856, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-02", - "totalSwipeTime": 13181, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-03", - "totalSwipeTime": 8477, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-05", - "totalSwipeTime": 3117, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-06", - "totalSwipeTime": 5354, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-07", - "totalSwipeTime": 2685, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-08", - "totalSwipeTime": 2257, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-09", - "totalSwipeTime": 1233, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-10", - "totalSwipeTime": 3565, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-11", - "totalSwipeTime": 1409, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-12", - "totalSwipeTime": 395, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-13", - "totalSwipeTime": 4462, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-14", - "totalSwipeTime": 141, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-15", - "totalSwipeTime": 8057, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-16", - "totalSwipeTime": 6532, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-17", - "totalSwipeTime": 8466, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-18", - "totalSwipeTime": 34980, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-19", - "totalSwipeTime": 13203, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-20", - "totalSwipeTime": 2570, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-21", - "totalSwipeTime": 6570, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-22", - "totalSwipeTime": 2479, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-23", - "totalSwipeTime": 2905, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-24", - "totalSwipeTime": 1829, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-25", - "totalSwipeTime": 7019, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-26", - "totalSwipeTime": 4947, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-27", - "totalSwipeTime": 1968, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-28", - "totalSwipeTime": 1577, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-29", - "totalSwipeTime": 3244, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-04-30", - "totalSwipeTime": 2058, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-01", - "totalSwipeTime": 1885, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-02", - "totalSwipeTime": 3917, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-03", - "totalSwipeTime": 3221, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-04", - "totalSwipeTime": 2782, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-05", - "totalSwipeTime": 2058, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-06", - "totalSwipeTime": 1173, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-07", - "totalSwipeTime": 3351, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-08", - "totalSwipeTime": 1455, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-09", - "totalSwipeTime": 6819, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-10", - "totalSwipeTime": 3070, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-11", - "totalSwipeTime": 2573, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-12", - "totalSwipeTime": 1431, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-13", - "totalSwipeTime": 4996, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-14", - "totalSwipeTime": 7824, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-15", - "totalSwipeTime": 178, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-16", - "totalSwipeTime": 5207, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-17", - "totalSwipeTime": 258, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-18", - "totalSwipeTime": 543, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-19", - "totalSwipeTime": 863, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-20", - "totalSwipeTime": 614, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-21", - "totalSwipeTime": 1583, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-22", - "totalSwipeTime": 1442, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-23", - "totalSwipeTime": 5442, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-24", - "totalSwipeTime": 901, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-25", - "totalSwipeTime": 2669, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-26", - "totalSwipeTime": 1480, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-27", - "totalSwipeTime": 9517, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-28", - "totalSwipeTime": 1034, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-29", - "totalSwipeTime": 3909, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-30", - "totalSwipeTime": 4091, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-05-31", - "totalSwipeTime": 2089, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-01", - "totalSwipeTime": 1184, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-02", - "totalSwipeTime": 3877, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-03", - "totalSwipeTime": 4290, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-04", - "totalSwipeTime": 3118, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-05", - "totalSwipeTime": 4371, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-06", - "totalSwipeTime": 1998, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-07", - "totalSwipeTime": 9040, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-08", - "totalSwipeTime": 4567, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-09", - "totalSwipeTime": 1716, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-10", - "totalSwipeTime": 12063, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-11", - "totalSwipeTime": 19046, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-12", - "totalSwipeTime": 10074, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-13", - "totalSwipeTime": 8658, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-14", - "totalSwipeTime": 14809, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-15", - "totalSwipeTime": 3727, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-16", - "totalSwipeTime": 8686, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-17", - "totalSwipeTime": 6617, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-18", - "totalSwipeTime": 11928, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-19", - "totalSwipeTime": 2108, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-20", - "totalSwipeTime": 30040, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-21", - "totalSwipeTime": 21793, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-22", - "totalSwipeTime": 8964, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-23", - "totalSwipeTime": 9414, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-24", - "totalSwipeTime": 20416, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-25", - "totalSwipeTime": 10238, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-26", - "totalSwipeTime": 8446, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-27", - "totalSwipeTime": 10738, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-28", - "totalSwipeTime": 12460, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-29", - "totalSwipeTime": 15712, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-06-30", - "totalSwipeTime": 18091, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-01", - "totalSwipeTime": 15220, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-02", - "totalSwipeTime": 10968, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-03", - "totalSwipeTime": 5917, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-04", - "totalSwipeTime": 9310, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-05", - "totalSwipeTime": 17967, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-06", - "totalSwipeTime": 14844, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-07", - "totalSwipeTime": 5976, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-08", - "totalSwipeTime": 9517, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-09", - "totalSwipeTime": 5972, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-10", - "totalSwipeTime": 14638, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-11", - "totalSwipeTime": 13358, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-12", - "totalSwipeTime": 20049, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-13", - "totalSwipeTime": 10459, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-14", - "totalSwipeTime": 11672, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-15", - "totalSwipeTime": 5405, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-16", - "totalSwipeTime": 7126, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-17", - "totalSwipeTime": 10973, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-18", - "totalSwipeTime": 7712, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-19", - "totalSwipeTime": 18500, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-20", - "totalSwipeTime": 16097, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-21", - "totalSwipeTime": 24523, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-22", - "totalSwipeTime": 2400, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-23", - "totalSwipeTime": 2593, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-24", - "totalSwipeTime": 13028, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-25", - "totalSwipeTime": 5378, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-26", - "totalSwipeTime": 6092, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-27", - "totalSwipeTime": 17566, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-28", - "totalSwipeTime": 6585, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-29", - "totalSwipeTime": 2190, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-30", - "totalSwipeTime": 5133, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-07-31", - "totalSwipeTime": 4210, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-01", - "totalSwipeTime": 7426, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-02", - "totalSwipeTime": 8271, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-03", - "totalSwipeTime": 12001, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-04", - "totalSwipeTime": 1396, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-05", - "totalSwipeTime": 12396, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-06", - "totalSwipeTime": 14951, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-07", - "totalSwipeTime": 14278, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-08", - "totalSwipeTime": 13270, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-09", - "totalSwipeTime": 11120, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-10", - "totalSwipeTime": 6014, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-11", - "totalSwipeTime": 2031, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-12", - "totalSwipeTime": 9368, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-13", - "totalSwipeTime": 5273, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-14", - "totalSwipeTime": 1859, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-15", - "totalSwipeTime": 3563, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-16", - "totalSwipeTime": 9241, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-17", - "totalSwipeTime": 6071, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-18", - "totalSwipeTime": 14260, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-19", - "totalSwipeTime": 6553, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-20", - "totalSwipeTime": 3339, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-21", - "totalSwipeTime": 3789, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-22", - "totalSwipeTime": 6823, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-23", - "totalSwipeTime": 3343, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-24", - "totalSwipeTime": 7411, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-25", - "totalSwipeTime": 6651, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-26", - "totalSwipeTime": 8180, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-27", - "totalSwipeTime": 5361, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-28", - "totalSwipeTime": 2144, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-29", - "totalSwipeTime": 2146, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-30", - "totalSwipeTime": 2178, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-08-31", - "totalSwipeTime": 5897, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-01", - "totalSwipeTime": 4184, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-02", - "totalSwipeTime": 7407, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-03", - "totalSwipeTime": 5471, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-04", - "totalSwipeTime": 2321, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-05", - "totalSwipeTime": 4905, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-06", - "totalSwipeTime": 8804, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-07", - "totalSwipeTime": 8469, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-08", - "totalSwipeTime": 9397, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-09", - "totalSwipeTime": 2233, - "__typename": "ContributorTimeStatType" - }, - { - "date": "2024-09-10", - "totalSwipeTime": 4204, - "__typename": "ContributorTimeStatType" - } - ], - "swipeByProjectType": [ - { - "projectType": "BUILD_AREA", - "projectTypeDisplay": "Find", - "totalSwipes": 226528, - "__typename": "ProjectTypeSwipeStatsType" - }, - { - "projectType": "CHANGE_DETECTION", - "projectTypeDisplay": "Compare", - "totalSwipes": 642, - "__typename": "ProjectTypeSwipeStatsType" - }, - { - "projectType": "FOOTPRINT", - "projectTypeDisplay": "Validate", - "totalSwipes": 239998, - "__typename": "ProjectTypeSwipeStatsType" - } - ], - "swipeByOrganizationName": [ - { - "organizationName": "Malawi Liverpool Wellcome", - "totalSwipes": 2688, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "American Red Cross", - "totalSwipes": 235791, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "Arizona State University", - "totalSwipes": 6105, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "OpenStreetMap Togo", - "totalSwipes": 365, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "Open Mapping Hub - AP", - "totalSwipes": 10066, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "Canadian Red Cross", - "totalSwipes": 353, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "OpenMappingHub - LAC", - "totalSwipes": 21199, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "OSM Papua New Guinea", - "totalSwipes": 22004, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "M\u00e9decins Sans Fronti\u00e8res", - "totalSwipes": 24707, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "OSM Afghanistan", - "totalSwipes": 4130, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "Ladies In Maps Zimbabwe ", - "totalSwipes": 17592, - "__typename": "OrganizationSwipeStatsType" - }, - { - "organizationName": "HOT", - "totalSwipes": 122168, - "__typename": "OrganizationSwipeStatsType" - } - ], - "__typename": "UserGroupFilteredStats" - }, - "__typename": "UserGroupStats" - } - } -} \ No newline at end of file diff --git a/backend/general_mapswipe_stats.json b/backend/general_mapswipe_stats.json deleted file mode 100644 index a950ee14fc..0000000000 --- a/backend/general_mapswipe_stats.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "data": { - "userGroup": { - "id": "-NL6WXPOdFyWACqwNU2O", - "userGroupId": "-NL6WXPOdFyWACqwNU2O", - "name": "American Red Cross", - "description": "", - "userMemberships": { - "count": 531, - "items": [ - { - "id": "05AfE1oB5vY6EbPqEjkdR9o0s4D3--NL6WXPOdFyWACqwNU2O", - "userId": "05AfE1oB5vY6EbPqEjkdR9o0s4D3", - "username": "jaiC", - "isActive": true, - "totalMappingProjects": 3, - "totalSwipeTime": 6288, - "totalSwipes": 1380, - "__typename": "UserGroupUserMembershipType" - }, - { - "id": "08zR2YnWNXh8b5sFd2xEVa2c2t53--NL6WXPOdFyWACqwNU2O", - "userId": "08zR2YnWNXh8b5sFd2xEVa2c2t53", - "username": "jainMedha", - "isActive": true, - "totalMappingProjects": 3, - "totalSwipeTime": 1580, - "totalSwipes": 518, - "__typename": "UserGroupUserMembershipType" - }, - { - "id": "09G9UqZq9yODe5XRl1ekhchba3x1--NL6WXPOdFyWACqwNU2O", - "userId": "09G9UqZq9yODe5XRl1ekhchba3x1", - "username": "Micah", - "isActive": true, - "totalMappingProjects": 18, - "totalSwipeTime": 9469, - "totalSwipes": 3455, - "__typename": "UserGroupUserMembershipType" - }, - { - "id": "0f1KIx0VmmRcqxdwOp2jnlZyNWx2--NL6WXPOdFyWACqwNU2O", - "userId": "0f1KIx0VmmRcqxdwOp2jnlZyNWx2", - "username": "divvs2007", - "isActive": true, - "totalMappingProjects": 1, - "totalSwipeTime": 381, - "totalSwipes": 111, - "__typename": "UserGroupUserMembershipType" - }, - { - "id": "0fXvDI22YrUuiCOGbQPbln5Bbru2--NL6WXPOdFyWACqwNU2O", - "userId": "0fXvDI22YrUuiCOGbQPbln5Bbru2", - "username": "tannu365", - "isActive": true, - "totalMappingProjects": 1, - "totalSwipeTime": 1611, - "totalSwipes": 407, - "__typename": "UserGroupUserMembershipType" - }, - { - "id": "0k4JHPdKrFNJhRgn7BOZX14MU2v1--NL6WXPOdFyWACqwNU2O", - "userId": "0k4JHPdKrFNJhRgn7BOZX14MU2v1", - "username": "arfah234", - "isActive": true, - "totalMappingProjects": 2, - "totalSwipeTime": 1459, - "totalSwipes": 561, - "__typename": "UserGroupUserMembershipType" - }, - { - "id": "0kSwoyz6jgXhicD9r0FDsmExtk32--NL6WXPOdFyWACqwNU2O", - "userId": "0kSwoyz6jgXhicD9r0FDsmExtk32", - "username": "Gabrielle Sarsok", - "isActive": true, - "totalMappingProjects": 0, - "totalSwipeTime": 0, - "totalSwipes": 0, - "__typename": "UserGroupUserMembershipType" - }, - { - "id": "0N1lhg0NJAN6lvAfEBkhzXnXgv53--NL6WXPOdFyWACqwNU2O", - "userId": "0N1lhg0NJAN6lvAfEBkhzXnXgv53", - "username": "ashleycruzc", - "isActive": true, - "totalMappingProjects": 2, - "totalSwipeTime": 3273, - "totalSwipes": 1406, - "__typename": "UserGroupUserMembershipType" - }, - { - "id": "0QCwiA4kCzac6rlsedwmu5vPJb53--NL6WXPOdFyWACqwNU2O", - "userId": "0QCwiA4kCzac6rlsedwmu5vPJb53", - "username": "tanvi", - "isActive": true, - "totalMappingProjects": 7, - "totalSwipeTime": 28628, - "totalSwipes": 7103, - "__typename": "UserGroupUserMembershipType" - }, - { - "id": "0QQCPKsz2Bh3HtmseOBqSFN6qiq2--NL6WXPOdFyWACqwNU2O", - "userId": "0QQCPKsz2Bh3HtmseOBqSFN6qiq2", - "username": "janyapatel", - "isActive": true, - "totalMappingProjects": 5, - "totalSwipeTime": 1280, - "totalSwipes": 836, - "__typename": "UserGroupUserMembershipType" - } - ], - "__typename": "UserGroupUserMembershipTypeCountList" - }, - "__typename": "UserGroupType" - }, - "userGroupStats": { - "id": "-NL6WXPOdFyWACqwNU2O", - "stats": { - "totalContributors": 432, - "totalSwipes": 1095815, - "totalSwipeTime": 2902427, - "__typename": "UserGroupStatsType" - }, - "statsLatest": { - "totalContributors": 56, - "totalSwipeTime": 178588, - "totalSwipes": 65212, - "__typename": "UserGroupLatestStatsType" - }, - "__typename": "UserGroupStats" - } - } -} \ No newline at end of file From 503952a6281bc00c1809353ad42b6bfd438920a9 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Mon, 23 Sep 2024 10:39:17 +0530 Subject: [PATCH 083/101] refactor: clean imports, add proptypes, and address linting warnings --- .../contributionsHeatmap.js | 10 +++---- .../partnerMapswipeStats/groupMembers.js | 2 +- .../swipesByOrganisation.js | 1 + .../swipesByProjectType.js | 1 + frontend/src/components/statsCard.js | 28 ++++++++++++++----- frontend/src/components/svgIcons/swipe.js | 4 +-- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js index 31b1c3245d..843254838a 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js +++ b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js @@ -5,8 +5,7 @@ import { FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; import 'mapbox-gl/dist/mapbox-gl.css'; -import { MAPBOX_TOKEN, MAP_STYLE } from '../../config'; -import { CHART_COLOURS } from '../../config'; +import { MAPBOX_TOKEN, MAP_STYLE, CHART_COLOURS } from '../../config'; import { ZoomPlusIcon, ZoomMinusIcon } from '../svgIcons'; import messages from './messages'; import './contributionsHeatmap.css'; @@ -164,14 +163,13 @@ export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => { // Check if CTRL key is pressed event.originalEvent.preventDefault(); // Prevent chrome/firefox default behavior if (!map.current.scrollZoom._enabled) map.current.scrollZoom.enable(); // Enable zoom only if it's disabled - } else { - if (map.current.scrollZoom._enabled) map.current.scrollZoom.disable(); // Disable zoom only if it's enabled + } else if (map.current.scrollZoom._enabled) { + map.current.scrollZoom.disable(); // Disable zoom only if it's enabled } }); map.current.on('zoomend', () => { const currentZoom = map.current.getZoom(); - console.log(currentZoom, 'ZOOM'); const h3ResBasedOnZoom = currentZoom >= 1 ? zoomToH3ResMapping[parseInt(currentZoom)] ?? Math.floor((currentZoom - 2) * 0.7) @@ -200,7 +198,7 @@ export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => { } }; - const shouldDisableZoomOut = zoom === '0.75' || zoom === 0.75; + const shouldDisableZoomOut = zoom === 0.75; return (
diff --git a/frontend/src/components/partnerMapswipeStats/groupMembers.js b/frontend/src/components/partnerMapswipeStats/groupMembers.js index f33b8f898f..1894c04fb0 100644 --- a/frontend/src/components/partnerMapswipeStats/groupMembers.js +++ b/frontend/src/components/partnerMapswipeStats/groupMembers.js @@ -72,7 +72,7 @@ export const GroupMembers = () => { const rows = 10; - const { isLoading, isError, data, isFetching, isPreviousData } = useQuery({ + const { isLoading, isError, data, isFetching } = useQuery({ queryKey: ['partners-mapswipe-statistics-group-members', partnerPermalink, pageNumber], queryFn: async () => { const response = await fetchLocalJSONAPI( diff --git a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js index ecfc2b5721..91d60ba050 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js @@ -76,6 +76,7 @@ export const SwipesByOrganisation = ({ contributionsByOrganization = [] }) => { chartInstance.current.destroy(); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js index ab207f7a08..00b4c8a3f2 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js @@ -85,6 +85,7 @@ export const SwipesByProjectType = ({ contributionsByProjectType = [] }) => { chartInstance.current.destroy(); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/frontend/src/components/statsCard.js b/frontend/src/components/statsCard.js index 9eae2c5deb..cba58debd9 100644 --- a/frontend/src/components/statsCard.js +++ b/frontend/src/components/statsCard.js @@ -1,4 +1,6 @@ import { FormattedNumber } from 'react-intl'; +import PropTypes from 'prop-types'; + import shortNumber from 'short-number'; export const StatsCard = ({ icon, description, value, className, invertColors = false }) => { @@ -26,7 +28,7 @@ export const StatsCardWithFooter = ({ className, delta, invertColors = false, - style + style, }) => { return (
(

{value}

@@ -65,18 +77,20 @@ export const StatsCardContent = ({ value, label, className, invertColors = false
); -export const StatsCardWithFooterContent = ({ - value, - label, - className, - invertColors = false, -}: Object) => ( +export const StatsCardWithFooterContent = ({ value, label, className, invertColors = false }) => (

{value}

{label}
); +StatsCardWithFooterContent.propTypes = { + value: PropTypes.node, + label: PropTypes.node, + className: PropTypes.string, + invertColors: PropTypes.bool, +}; + function getFormattedNumber(num) { if (typeof num !== 'number') return '-'; const value = shortNumber(num); diff --git a/frontend/src/components/svgIcons/swipe.js b/frontend/src/components/svgIcons/swipe.js index 147e76f53c..0d313f154f 100644 --- a/frontend/src/components/svgIcons/swipe.js +++ b/frontend/src/components/svgIcons/swipe.js @@ -12,8 +12,8 @@ export class SwipeIcon extends PureComponent { {...this.props} aria-label="Swipe" > - - + + Date: Tue, 24 Sep 2024 16:50:15 +0530 Subject: [PATCH 084/101] Rename swipesByOrganisation to swipesByOrganization --- frontend/src/components/partnerMapswipeStats/messages.js | 4 ++-- .../{swipesByOrganisation.js => swipesByOrganization.js} | 4 ++-- frontend/src/views/partnersMapswipeStats.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename frontend/src/components/partnerMapswipeStats/{swipesByOrganisation.js => swipesByOrganization.js} (96%) diff --git a/frontend/src/components/partnerMapswipeStats/messages.js b/frontend/src/components/partnerMapswipeStats/messages.js index 2c0ed85681..8f5a3d4a98 100644 --- a/frontend/src/components/partnerMapswipeStats/messages.js +++ b/frontend/src/components/partnerMapswipeStats/messages.js @@ -101,8 +101,8 @@ export default defineMessages({ defaultMessage: 'Swipes by Project Type', }, swipesByOrganization: { - id: 'management.partners.stats.mapswipe.swipesByOrganisation', - defaultMessage: 'Swipes by Organisation', + id: 'management.partners.stats.mapswipe.swipesByOrganization', + defaultMessage: 'Swipes by Organization', }, areaSwipesByProjectType: { id: 'management.partners.stats.mapswipe.areaSwipes', diff --git a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js b/frontend/src/components/partnerMapswipeStats/swipesByOrganization.js similarity index 96% rename from frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js rename to frontend/src/components/partnerMapswipeStats/swipesByOrganization.js index 91d60ba050..ae210cbe58 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByOrganisation.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByOrganization.js @@ -23,7 +23,7 @@ function withGroupedLowContributors(contributionsByOrganization, keepTop = 4) { topContributors.push(others); return topContributors; } -export const SwipesByOrganisation = ({ contributionsByOrganization = [] }) => { +export const SwipesByOrganization = ({ contributionsByOrganization = [] }) => { const chartRef = useRef(null); const chartInstance = useRef(null); contributionsByOrganization = withGroupedLowContributors(contributionsByOrganization); @@ -98,7 +98,7 @@ export const SwipesByOrganisation = ({ contributionsByOrganization = [] }) => { ); }; -SwipesByOrganisation.propTypes = { +SwipesByOrganization.propTypes = { contributionsByOrganization: PropTypes.arrayOf( PropTypes.shape({ organizationName: PropTypes.string, diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 40940b8067..cec84d2709 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -16,7 +16,7 @@ import { TimeSpentContributing } from '../components/partnerMapswipeStats/timeSp import { TimeSpentContributingByDay } from '../components/partnerMapswipeStats/timeSpentContributingByDay'; import { ProjectTypeAreaStats } from '../components/partnerMapswipeStats/projectTypeAreaStats'; import { SwipesByProjectType } from '../components/partnerMapswipeStats/swipesByProjectType'; -import { SwipesByOrganisation } from '../components/partnerMapswipeStats/swipesByOrganisation'; +import { SwipesByOrganization } from '../components/partnerMapswipeStats/swipesByOrganization'; import { StatsCardWithFooter } from '../components/statsCard'; import messages from './messages'; import { fetchLocalJSONAPI } from '../network/genericJSONRequest'; @@ -156,7 +156,7 @@ export const PartnersMapswipeStats = () => {
-
From 5f241b1aa32bc0163391721ecf4d59633146ec16 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Tue, 24 Sep 2024 17:24:49 +0530 Subject: [PATCH 085/101] feat: replace manual zoom controls with native navigation controls --- .../contributionsHeatmap.js | 40 ++----------------- 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js index 843254838a..63ef04e86b 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js +++ b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useEffect, useRef } from 'react'; import mapboxgl from 'mapbox-gl'; import { latLngToCell, cellToBoundary } from 'h3-js'; import { FormattedMessage } from 'react-intl'; @@ -6,7 +6,6 @@ import PropTypes from 'prop-types'; import 'mapbox-gl/dist/mapbox-gl.css'; import { MAPBOX_TOKEN, MAP_STYLE, CHART_COLOURS } from '../../config'; -import { ZoomPlusIcon, ZoomMinusIcon } from '../svgIcons'; import messages from './messages'; import './contributionsHeatmap.css'; @@ -15,7 +14,6 @@ mapboxgl.accessToken = MAPBOX_TOKEN; export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => { const mapContainer = useRef(null); const map = useRef(null); - const [zoom, setZoom] = useState(1.25); useEffect(() => { if (map.current) return; // initialize map only once @@ -24,10 +22,11 @@ export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => { container: mapContainer.current, style: MAP_STYLE, center: [0, 0], - zoom: zoom, + zoom: 1.25, }); map.current.scrollZoom.disable(); + map.current.addControl(new mapboxgl.NavigationControl()); const getStyle = (row) => { const styles = [ @@ -183,23 +182,6 @@ export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { - map.current.zoomTo(zoom, { duration: 700 }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [zoom]); - - const handleZoom = (isZoomingOut = false) => { - const currentZoom = parseFloat(zoom); - if (!isZoomingOut) { - setZoom(currentZoom + 0.5); - } else { - if (currentZoom.toFixed(2) === '0.75') return; - setZoom(currentZoom - 0.5); - } - }; - - const shouldDisableZoomOut = zoom === 0.75; - return (

@@ -208,21 +190,7 @@ export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => {
-
-
- handleZoom(true)} - /> - handleZoom()} - /> -
+

Date: Tue, 24 Sep 2024 17:54:17 +0530 Subject: [PATCH 086/101] Add formatting to time on table, reduce size of doughnut chart --- .../src/components/partnerMapswipeStats/groupMembers.js | 8 ++++++++ .../partnerMapswipeStats/swipesByOrganization.js | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/partnerMapswipeStats/groupMembers.js b/frontend/src/components/partnerMapswipeStats/groupMembers.js index 1894c04fb0..5e3d47d129 100644 --- a/frontend/src/components/partnerMapswipeStats/groupMembers.js +++ b/frontend/src/components/partnerMapswipeStats/groupMembers.js @@ -7,6 +7,7 @@ import ReactPlaceholder from 'react-placeholder'; import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; import { BanIcon, CircleExclamationIcon, DownloadIcon } from '../svgIcons'; +import { formatSecondsToTwoUnits } from './overview'; import { PaginatorLine } from '../paginator'; import { API_URL } from '../../config'; import messages from './messages'; @@ -45,6 +46,13 @@ const COLUMNS = [ ), + cell: ({ row }) => ( + + {row.original.totalcontributionTime + ? formatSecondsToTwoUnits(row.original.totalcontributionTime) + : '0'} + + ), }, ]; diff --git a/frontend/src/components/partnerMapswipeStats/swipesByOrganization.js b/frontend/src/components/partnerMapswipeStats/swipesByOrganization.js index ae210cbe58..9209740dc3 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByOrganization.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByOrganization.js @@ -85,7 +85,7 @@ export const SwipesByOrganization = ({ contributionsByOrganization = [] }) => {

-
+
{contributionsByOrganization.length === 0 && (
From 475b66a74b4d4c39bdae80b8c8e4cb8c82663c66 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Tue, 24 Sep 2024 17:55:35 +0530 Subject: [PATCH 087/101] fix: ensure the right labels and colors are used when partial data is present --- .../swipesByProjectType.js | 57 ++++++++----------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js index 00b4c8a3f2..399c00f608 100644 --- a/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js +++ b/frontend/src/components/partnerMapswipeStats/swipesByProjectType.js @@ -18,52 +18,45 @@ export const SwipesByProjectType = ({ contributionsByProjectType = [] }) => { if (!chartRef.current) return; - const labelData = []; - const data = { - find: { - totalcontributions: 0, - }, - validate: { - totalcontributions: 0, - }, - compare: { - totalcontributions: 0, - }, - }; + const chartData = [], + labelsData = [], + backgroundColors = []; contributionsByProjectType.forEach((stat) => { if (['build_area', 'buildarea'].includes(stat.projectType.toLowerCase())) { - data.find.totalcontributions = stat.totalcontributions || 0; - if (data.find.totalcontributions > 0) labelData.push('Find'); - } else if (['change_detection', 'changedetection'].includes(stat.projectType.toLowerCase())) { - data.compare.totalcontributions = stat.totalcontributions || 0; - if (data.compare.totalcontributions > 0) labelData.push('Compare'); + const contributionsCount = stat.totalcontributions || 0; + if (contributionsCount > 0) { + chartData.push(contributionsCount); + labelsData.push('Find'); + backgroundColors.push(CHART_COLOURS.orange); + } } else if (['foot_print', 'footprint'].includes(stat.projectType.toLowerCase())) { - data.validate.totalcontributions = stat.totalcontributions || 0; - if (data.validate.totalcontributions > 0) labelData.push('Validate'); + const contributionsCount = stat.totalcontributions || 0; + if (contributionsCount > 0) { + chartData.push(contributionsCount); + labelsData.push('Validate'); + backgroundColors.push(CHART_COLOURS.green); + } + } else if (['change_detection', 'changedetection'].includes(stat.projectType.toLowerCase())) { + const contributionsCount = stat.totalcontributions || 0; + if (contributionsCount > 0) { + chartData.push(contributionsCount); + labelsData.push('Compare'); + backgroundColors.push(CHART_COLOURS.blue); + } } }); - const chartData = [ - data.find.totalcontributions, - data.validate.totalcontributions, - data.compare.totalcontributions, - ]; - const context = chartRef.current.getContext('2d'); chartInstance.current = new Chart(context, { type: 'doughnut', data: { - labels: labelData, + labels: labelsData, datasets: [ { data: chartData, - backgroundColor: [ - CHART_COLOURS.orange, // Orange for Find - CHART_COLOURS.green, // Green for Validate - CHART_COLOURS.blue, // Blue for Compare - ], + backgroundColor: backgroundColors, borderColor: '#fff', borderWidth: 2, }, @@ -94,7 +87,7 @@ export const SwipesByProjectType = ({ contributionsByProjectType = [] }) => { -
+
{contributionsByProjectType.length === 0 && (
From 302d623e1bf69d099deecf881096491b3059086d Mon Sep 17 00:00:00 2001 From: bshankar Date: Tue, 24 Sep 2024 19:27:47 +0530 Subject: [PATCH 088/101] fix: Make CSV output user friendly Drop unnecessary column: id rename column totalcontributionTime -> totalSwipeTimeInSeconds rename column totalcontributions -> totalSwipes --- backend/models/dtos/partner_stats_dto.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/backend/models/dtos/partner_stats_dto.py b/backend/models/dtos/partner_stats_dto.py index 74162b0a7f..59b75430c2 100644 --- a/backend/models/dtos/partner_stats_dto.py +++ b/backend/models/dtos/partner_stats_dto.py @@ -93,6 +93,20 @@ class GroupedPartnerStatsDTO(Model): def to_csv(self): df = pd.json_normalize(self.to_primitive()["members"]) + + df.drop( + columns=["id"], + inplace=True, + axis=1, + ) + df.rename( + columns={ + "totalcontributionTime": "totalSwipeTimeInSeconds", + "totalcontributions": "totalSwipes", + }, + inplace=True, + ) + return df.to_csv(index=False) From 240afd8ba66dd6275c16f1a7906e5d69d646a315 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 27 Sep 2024 13:22:51 +0530 Subject: [PATCH 089/101] refactor: use better standard conventions --- .../partnerMapswipeStats/contributionsGrid.js | 12 ++++++------ .../partnerMapswipeStats/contributionsHeatmap.css | 1 + .../partnerMapswipeStats/contributionsHeatmap.js | 5 +---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/contributionsGrid.js b/frontend/src/components/partnerMapswipeStats/contributionsGrid.js index 2b8f4e4a9d..691231e83e 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsGrid.js +++ b/frontend/src/components/partnerMapswipeStats/contributionsGrid.js @@ -5,8 +5,8 @@ import PropTypes from 'prop-types'; import messages from './messages'; +const LEGEND_INDEXES = [30, 50, 70, 100]; const Legend = () => { - const indexes = [30, 50, 70, 100]; const legendFontStyle = 'ph2 f7 blue-grey ttc'; return ( @@ -15,7 +15,7 @@ const Legend = () => {
- {indexes.map((i) => ( + {LEGEND_INDEXES.map((i) => (
))} @@ -26,7 +26,7 @@ const Legend = () => { }; export const ContributionsGrid = ({ contributionsByDate = [] }) => { - contributionsByDate = contributionsByDate.map((contribution) => ({ + const gridData = contributionsByDate.map((contribution) => ({ date: contribution.taskDate, count: contribution.totalcontributions, })); @@ -46,7 +46,7 @@ export const ContributionsGrid = ({ contributionsByDate = [] }) => { : formatDate(new Date(currentYear, 11, 31)); }; - const countValues = contributionsByDate.map((contribution) => contribution.count); + const countValues = gridData.map((contribution) => contribution.count); const maxValue = Math.max(...countValues); const getHeatmapClass = (value) => { @@ -79,7 +79,7 @@ export const ContributionsGrid = ({ contributionsByDate = [] }) => { { if (!value) return 'fill-tan'; return getHeatmapClass(value); @@ -100,7 +100,7 @@ export const ContributionsGrid = ({ contributionsByDate = [] }) => { }; }} /> - +
diff --git a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.css b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.css index 4c4873401f..2b7ce2b649 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.css +++ b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.css @@ -1,5 +1,6 @@ .partner-mapswipe-heatmap-zoom-text { visibility: hidden; + user-select: none; } .partner-mapswipe-heatmap-wrapper:hover .partner-mapswipe-heatmap-zoom-text { visibility: visible; diff --git a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js index 63ef04e86b..9b82c35b55 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js +++ b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js @@ -191,10 +191,7 @@ export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => {
-

+

Use Ctrl + Scroll to zoom

From de1b114e402c29c819ee6df7be2c76b8dd85fbcd Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Tue, 1 Oct 2024 20:12:29 +0530 Subject: [PATCH 090/101] fix: handle card height, handle line curve better in time spent chart --- frontend/src/components/partnerMapswipeStats/overview.js | 8 ++++---- .../partnerMapswipeStats/timeSpentContributing.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/overview.js b/frontend/src/components/partnerMapswipeStats/overview.js index 907a4c3b7b..182de4ca73 100644 --- a/frontend/src/components/partnerMapswipeStats/overview.js +++ b/frontend/src/components/partnerMapswipeStats/overview.js @@ -68,7 +68,7 @@ export const Overview = () => { ready={!isLoading && !isRefetching} >
{ '--' ) } - className="w-100" + className="w-100 justify-between" /> } @@ -103,7 +103,7 @@ export const Overview = () => { '--' ) } - className="w-100" + className="w-100 justify-between" /> } @@ -119,7 +119,7 @@ export const Overview = () => { '--' ) } - className="w-100" + className="w-100 justify-between" />
diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js index 7029b15355..952c5595cf 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js @@ -86,7 +86,7 @@ export const TimeSpentContributing = ({ contributionTimeByDate = [] }) => { borderWidth: 1.5, data: data.map((entry) => entry.totalcontributionTime), fill: true, - tension: 0.4, + lineTension: chartDistribution === 'day' ? 0.1 : 0.4, }, ], }; From 85d1f28360d391d52bb8386c5333cb35b8ab8b2b Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 3 Oct 2024 17:54:48 +0530 Subject: [PATCH 091/101] Add people icon --- frontend/src/components/svgIcons/index.js | 1 + frontend/src/components/svgIcons/people.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 frontend/src/components/svgIcons/people.js diff --git a/frontend/src/components/svgIcons/index.js b/frontend/src/components/svgIcons/index.js index 58ba46eab7..5e4b2ab9d6 100644 --- a/frontend/src/components/svgIcons/index.js +++ b/frontend/src/components/svgIcons/index.js @@ -88,3 +88,4 @@ export { CircleExclamationIcon } from './circleExclamation'; export { TableListIcon } from './tableList'; export { SwipeIcon } from './swipe'; export { EmptySetIcon } from './emptySet'; +export { PeopleIcon } from './people'; diff --git a/frontend/src/components/svgIcons/people.js b/frontend/src/components/svgIcons/people.js new file mode 100644 index 0000000000..5c227b77ac --- /dev/null +++ b/frontend/src/components/svgIcons/people.js @@ -0,0 +1,14 @@ +import { PureComponent } from 'react'; + +export class PeopleIcon extends PureComponent { + render() { + return ( + + + + ); + } +} From 25755f2f094db08dc9a8d9e5200a51401b8d5e20 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Wed, 9 Oct 2024 18:02:46 +0530 Subject: [PATCH 092/101] Revamp styling and replace StatsCardWithFooter with custom JSX --- .../partnerMapswipeStats/overview.js | 114 +++++++++++------- 1 file changed, 71 insertions(+), 43 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/overview.js b/frontend/src/components/partnerMapswipeStats/overview.js index 182de4ca73..53a004b264 100644 --- a/frontend/src/components/partnerMapswipeStats/overview.js +++ b/frontend/src/components/partnerMapswipeStats/overview.js @@ -6,11 +6,10 @@ import shortNumber from 'short-number'; import { intervalToDuration } from 'date-fns'; import messages from './messages'; -import { StatsCardWithFooter } from '../statsCard'; -import { MappingIcon, SwipeIcon, ClockIcon } from '../svgIcons'; +import { PeopleIcon, SwipeIcon, ClockIcon } from '../svgIcons'; import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; -const iconClass = 'w-100'; +const iconClass = 'w-100 red'; const iconStyle = { height: '55px' }; const OverviewPlaceholder = () => ( @@ -18,9 +17,9 @@ const OverviewPlaceholder = () => ( className="flex justify-between items-center flex-wrap flex-nowrap-ns" style={{ gap: '1.6rem' }} > - - - + + +
); @@ -71,56 +70,85 @@ export const Overview = () => { className="flex justify-between items-stretch flex-wrap flex-nowrap-ns" style={{ gap: '1.6rem' }} > - } - description={} - value={data?.totalcontributions ? getShortNumber(data.totalcontributions) : '-'} - delta={ - data?.totalRecentcontributions ? ( - +
+
+ +
+
+

+ {data?.totalcontributions ? getShortNumber(data.totalcontributions) : '-'} +

+ + + + + {data?.totalRecentcontributions ? ( + {getShortNumber(data.totalRecentcontributions)}{' '} ) : ( - '--' - ) - } - className="w-100 justify-between" - /> - } - description={} - value={ - data?.totalcontributionTime ? formatSecondsToTwoUnits(data.totalcontributionTime) : '-' - } - delta={ - data?.totalRecentcontributionTime ? ( - + '-' + )} +
+
+ +
+
+ +
+
+

+ {data?.totalcontributionTime + ? formatSecondsToTwoUnits(data.totalcontributionTime) + : '-'} +

+ + + + + {data?.totalRecentcontributionTime ? ( + {formatSecondsToTwoUnits(data.totalRecentcontributionTime)}{' '} ) : ( '--' - ) - } - className="w-100 justify-between" - /> - } - description={} - value={data?.totalContributors ? getShortNumber(data.totalContributors) : '-'} - delta={ - data?.totalRecentContributors ? ( - + )} +
+
+ +
+
+ +
+
+

+ {data?.totalContributors ? getShortNumber(data.totalContributors) : '-'} +

+ + + + + {data?.totalRecentContributors ? ( + {getShortNumber(data.totalRecentContributors)}{' '} ) : ( - '--' - ) - } - className="w-100 justify-between" - /> + '-' + )} +
+
); From c0d46cf20d9ee9b3022f9b2be00232cb5691bbbe Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 3 Oct 2024 00:48:02 +0530 Subject: [PATCH 093/101] Revamp styling of swipes and time spent contributing cards --- frontend/src/views/partnersMapswipeStats.js | 26 ++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index cec84d2709..8c901cd462 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -17,7 +17,6 @@ import { TimeSpentContributingByDay } from '../components/partnerMapswipeStats/t import { ProjectTypeAreaStats } from '../components/partnerMapswipeStats/projectTypeAreaStats'; import { SwipesByProjectType } from '../components/partnerMapswipeStats/swipesByProjectType'; import { SwipesByOrganization } from '../components/partnerMapswipeStats/swipesByOrganization'; -import { StatsCardWithFooter } from '../components/statsCard'; import messages from './messages'; import { fetchLocalJSONAPI } from '../network/genericJSONRequest'; import './partnersMapswipeStats.css'; @@ -140,18 +139,19 @@ export const PartnersMapswipeStats = () => { />
-
- } - value={getSwipes()} - style={{ width: '48.5%' }} - /> - } - value={getTimeSpentContributing()} - className="w-100" - style={{ width: '48.5%' }} - /> +
+
+ {getSwipes()} + + + +
+
+ {getTimeSpentContributing()} + + + +
From fdf5fbadb518f3de50359943dc8689eb07e7bac5 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 4 Oct 2024 00:54:56 +0530 Subject: [PATCH 094/101] Add ChecksGrid icon --- frontend/src/components/svgIcons/checksGrid.js | 14 ++++++++++++++ frontend/src/components/svgIcons/index.js | 1 + 2 files changed, 15 insertions(+) create mode 100644 frontend/src/components/svgIcons/checksGrid.js diff --git a/frontend/src/components/svgIcons/checksGrid.js b/frontend/src/components/svgIcons/checksGrid.js new file mode 100644 index 0000000000..13765e4e64 --- /dev/null +++ b/frontend/src/components/svgIcons/checksGrid.js @@ -0,0 +1,14 @@ +import { PureComponent } from 'react'; + +export class ChecksGridIcon extends PureComponent { + render() { + return ( + + + + ); + } +} diff --git a/frontend/src/components/svgIcons/index.js b/frontend/src/components/svgIcons/index.js index 5e4b2ab9d6..ce7fa3bef0 100644 --- a/frontend/src/components/svgIcons/index.js +++ b/frontend/src/components/svgIcons/index.js @@ -89,3 +89,4 @@ export { TableListIcon } from './tableList'; export { SwipeIcon } from './swipe'; export { EmptySetIcon } from './emptySet'; export { PeopleIcon } from './people'; +export { ChecksGridIcon } from './checksGrid'; From 3934ecd60ef32c2a02f91aa35bfa76b778b522b0 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 4 Oct 2024 01:03:56 +0530 Subject: [PATCH 095/101] Add ColumnsGap icon --- frontend/src/components/svgIcons/columnsGap.js | 14 ++++++++++++++ frontend/src/components/svgIcons/index.js | 1 + 2 files changed, 15 insertions(+) create mode 100644 frontend/src/components/svgIcons/columnsGap.js diff --git a/frontend/src/components/svgIcons/columnsGap.js b/frontend/src/components/svgIcons/columnsGap.js new file mode 100644 index 0000000000..c6c50c6b1d --- /dev/null +++ b/frontend/src/components/svgIcons/columnsGap.js @@ -0,0 +1,14 @@ +import { PureComponent } from 'react'; + +export class ColumnsGapIcon extends PureComponent { + render() { + return ( + + + + ); + } +} diff --git a/frontend/src/components/svgIcons/index.js b/frontend/src/components/svgIcons/index.js index ce7fa3bef0..0184b6bf0c 100644 --- a/frontend/src/components/svgIcons/index.js +++ b/frontend/src/components/svgIcons/index.js @@ -90,3 +90,4 @@ export { SwipeIcon } from './swipe'; export { EmptySetIcon } from './emptySet'; export { PeopleIcon } from './people'; export { ChecksGridIcon } from './checksGrid'; +export { ColumnsGapIcon } from './columnsGap'; From 9b1d53aae5188144711c27d7fd8152b11ebcad6d Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 4 Oct 2024 01:07:59 +0530 Subject: [PATCH 096/101] Add Fullscreen icon --- frontend/src/components/svgIcons/fullscreen.js | 14 ++++++++++++++ frontend/src/components/svgIcons/index.js | 1 + 2 files changed, 15 insertions(+) create mode 100644 frontend/src/components/svgIcons/fullscreen.js diff --git a/frontend/src/components/svgIcons/fullscreen.js b/frontend/src/components/svgIcons/fullscreen.js new file mode 100644 index 0000000000..7119bdf953 --- /dev/null +++ b/frontend/src/components/svgIcons/fullscreen.js @@ -0,0 +1,14 @@ +import { PureComponent } from 'react'; + +export class FullscreenIcon extends PureComponent { + render() { + return ( + + + + ); + } +} diff --git a/frontend/src/components/svgIcons/index.js b/frontend/src/components/svgIcons/index.js index 0184b6bf0c..134c41a9ba 100644 --- a/frontend/src/components/svgIcons/index.js +++ b/frontend/src/components/svgIcons/index.js @@ -91,3 +91,4 @@ export { EmptySetIcon } from './emptySet'; export { PeopleIcon } from './people'; export { ChecksGridIcon } from './checksGrid'; export { ColumnsGapIcon } from './columnsGap'; +export { FullscreenIcon } from './fullscreen'; From 6d5e97241995eaec9ba28ab84cc31bbd50842d6c Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Mon, 7 Oct 2024 01:34:28 +0530 Subject: [PATCH 097/101] Revamp styling of projectTypeAreaStats and replace StatsCardWithFooter with custom JSX --- .../partnerMapswipeStats/overview.js | 2 +- .../projectTypeAreaStats.js | 101 ++++++++++-------- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/overview.js b/frontend/src/components/partnerMapswipeStats/overview.js index 53a004b264..7a32bdb898 100644 --- a/frontend/src/components/partnerMapswipeStats/overview.js +++ b/frontend/src/components/partnerMapswipeStats/overview.js @@ -10,7 +10,7 @@ import { PeopleIcon, SwipeIcon, ClockIcon } from '../svgIcons'; import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; const iconClass = 'w-100 red'; -const iconStyle = { height: '55px' }; +const iconStyle = { height: '70px' }; const OverviewPlaceholder = () => (
- } - description={} - value={data.find.totalcontributions} - delta={ -
- {data.find.totalArea} Sq. KM. - - - -
- } - className="w-100" - /> - } - description={} - value={data.validate.totalcontributions} - delta={ -
- - - -
- } - className="w-100" - /> - } - description={} - value={data.compare.totalcontributions} - delta={ -
- {data.compare.totalArea} Sq. KM. - - - -
- } - className="w-100" - /> +
+
+ +
+
+ + + +

{data.find.totalcontributions}

+ + + + + + {data.find.totalArea} km2 + +
+
+ +
+
+ +
+
+ + + +

{data.validate.totalcontributions}

+ + + +
+
+ +
+
+ +
+
+ + + +

{data.compare.totalcontributions}

+ + + + + + {data.compare.totalArea} km2 + +
+
); }; From a903cfeb1e83d68c26933a1ad890fc4f4235016e Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Mon, 7 Oct 2024 02:01:49 +0530 Subject: [PATCH 098/101] Add support for shorter formatting --- .../partnerMapswipeStats/overview.js | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/overview.js b/frontend/src/components/partnerMapswipeStats/overview.js index 7a32bdb898..0eeaf21818 100644 --- a/frontend/src/components/partnerMapswipeStats/overview.js +++ b/frontend/src/components/partnerMapswipeStats/overview.js @@ -23,16 +23,31 @@ const OverviewPlaceholder = () => (
); -export const formatSecondsToTwoUnits = (seconds) => { +export const formatSecondsToTwoUnits = (seconds, shortFormat = false) => { const duration = intervalToDuration({ start: 0, end: seconds * 1000 }); const units = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds']; + const shortUnitsMapping = { + years: 'yrs', + months: 'mos', + weeks: 'wks', + days: 'ds', + hours: 'hrs', + minutes: 'mins', + seconds: 'secs', + }; // Filter units that have a value greater than 0 const nonZeroUnits = units.filter((unit) => duration[unit] > 0).slice(0, 2); - return nonZeroUnits - .map((unit) => `${duration[unit]} ${duration[unit] === 1 ? unit.slice(0, -1) : unit}`) - .join(' '); + const getUnitString = (unit) => { + if (duration[unit] === 1) { + return shortFormat ? shortUnitsMapping[unit].slice(0, -1) : unit.slice(0, -1); + } else { + return shortFormat ? shortUnitsMapping[unit] : unit; + } + }; + + return nonZeroUnits.map((unit) => `${duration[unit]} ${getUnitString(unit)}`).join(' '); }; export const getShortNumber = (value) => { @@ -43,7 +58,8 @@ export const getShortNumber = (value) => { ) : ( - {shortNumberValue.substr(-1)} +   + {shortNumberValue.substr(-1)} ); }; @@ -115,7 +131,7 @@ export const Overview = () => { {data?.totalRecentcontributionTime ? ( - {formatSecondsToTwoUnits(data.totalRecentcontributionTime)}{' '} + {formatSecondsToTwoUnits(data.totalRecentcontributionTime, true)}{' '} ) : ( From f0a7061de84116494077ab1c4b9dc7b071aa7dac Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 10 Oct 2024 02:04:26 +0530 Subject: [PATCH 099/101] Leverage shorter formatting of seconds to time --- .../components/partnerMapswipeStats/timeSpentContributing.js | 4 ++-- .../partnerMapswipeStats/timeSpentContributingByDay.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js index 952c5595cf..99e1b762aa 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributing.js @@ -112,7 +112,7 @@ export const TimeSpentContributing = ({ contributionTimeByDate = [] }) => { beginAtZero: true, ticks: { callback: function (value) { - return formatSecondsToTwoUnits(value); + return formatSecondsToTwoUnits(value, true); }, stepSize: 36 * 60, }, @@ -123,7 +123,7 @@ export const TimeSpentContributing = ({ contributionTimeByDate = [] }) => { callbacks: { label: function (context) { const value = context.parsed.y; - return formatSecondsToTwoUnits(value); + return formatSecondsToTwoUnits(value, true); }, }, }, diff --git a/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js index 62fc00c332..a597b8ab46 100644 --- a/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js +++ b/frontend/src/components/partnerMapswipeStats/timeSpentContributingByDay.js @@ -43,7 +43,7 @@ export const TimeSpentContributingByDay = ({ contributionTimeByDate = [] }) => { beginAtZero: true, ticks: { callback: function (value) { - return formatSecondsToTwoUnits(value); + return formatSecondsToTwoUnits(value, true); }, stepSize: 48 * 60, }, @@ -54,7 +54,7 @@ export const TimeSpentContributingByDay = ({ contributionTimeByDate = [] }) => { callbacks: { label: function (context) { const value = context.parsed.y; - return formatSecondsToTwoUnits(value); + return formatSecondsToTwoUnits(value, true); }, }, }, From a35748474eeabcd824bfc9baf270c24e22f34e86 Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Thu, 10 Oct 2024 02:05:41 +0530 Subject: [PATCH 100/101] Use shorter formatting for time spent contributing --- frontend/src/views/partnersMapswipeStats.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/partnersMapswipeStats.js b/frontend/src/views/partnersMapswipeStats.js index 8c901cd462..c9015d3640 100644 --- a/frontend/src/views/partnersMapswipeStats.js +++ b/frontend/src/views/partnersMapswipeStats.js @@ -96,6 +96,7 @@ export const PartnersMapswipeStats = () => { data.contributionTimeByDate .map((item) => item.totalcontributionTime) .reduce((total, value) => total + value, 0), + true, ); }; @@ -139,7 +140,10 @@ export const PartnersMapswipeStats = () => { />
-
+
{getSwipes()} From 6072acdbf660d2f03eb30d17275710aa9db2262d Mon Sep 17 00:00:00 2001 From: A Vinayak Rugvedi Date: Fri, 11 Oct 2024 00:20:01 +0530 Subject: [PATCH 101/101] Rename tab text from Leaderboard to Tasking Manager --- frontend/src/views/partnersStats.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/views/partnersStats.js b/frontend/src/views/partnersStats.js index 0d255b9411..9da9d83d4b 100644 --- a/frontend/src/views/partnersStats.js +++ b/frontend/src/views/partnersStats.js @@ -28,7 +28,7 @@ function getSocialIcons(link) { } const tabData = [ - { id: 'leaderboard', title: 'Leaderboard' }, + { id: 'leaderboard', title: 'Tasking Manager' }, { id: 'mapswipe', title: 'Map Swipe' }, ];