Skip to content

Commit

Permalink
Update to use new leaderboard model
Browse files Browse the repository at this point in the history
  • Loading branch information
alexd-bes committed Nov 2, 2023
1 parent 2a31cdb commit 747f182
Show file tree
Hide file tree
Showing 17 changed files with 121 additions and 28 deletions.
2 changes: 0 additions & 2 deletions packages/central-server/src/apiV2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
getSocialFeed,
getUserRewards,
postChanges,
getLeaderboardList,
} from './meditrakApp';
import { BESAdminCreateHandler } from './CreateHandler';
import { BESAdminDeleteHandler } from './DeleteHandler';
Expand Down Expand Up @@ -165,7 +164,6 @@ apiV2.get('/changes/count', catchAsyncErrors(countChanges));
apiV2.get('/changes/metadata', catchAsyncErrors(changesMetadata));
apiV2.get('/changes', catchAsyncErrors(getChanges));
apiV2.get('/downloadFiles', useRouteHandler(DownloadFiles));
apiV2.get('/leaderboard', catchAsyncErrors(getLeaderboardList));
apiV2.get('/socialFeed', catchAsyncErrors(getSocialFeed));
apiV2.get('/me', useRouteHandler(GETUserForMe));
apiV2.get('/me/rewards', allowAnyone(getUserRewards));
Expand Down

This file was deleted.

1 change: 0 additions & 1 deletion packages/central-server/src/apiV2/meditrakApp/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@ export { getSocialFeed } from './getSocialFeed';
export { getUserRewards } from './getUserRewards';
export { postChanges } from './postChanges';
export * from './countChanges';
export { getLeaderboardList } from './getLeaderboardList';
33 changes: 33 additions & 0 deletions packages/database/src/modelClasses/SurveyResponse.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ import { MaterializedViewLogDatabaseModel } from '../analytics';
import { DatabaseType } from '../DatabaseType';
import { TYPES } from '../types';

const EXCLUDED_USERS = [
"'edmofro@gmail.com'", // Edwin
"'kahlinda.mahoney@gmail.com'", // Kahlinda
"'lparish1980@gmail.com'", // Lewis
"'sus.lake@gmail.com'", // Susie
"'michaelnunan@hotmail.com'", // Michael
"'vanbeekandrew@gmail.com'", // Andrew
"'gerardckelly@gmail.com'", // Gerry K
"'geoffreyfisher@hotmail.com'", // Geoff F
"'josh@sussol.net'", // mSupply API Client
"'unicef.laos.edu@gmail.com'", // Laos Schools Data Collector
"'tamanu-server@tupaia.org'", // Tamanu Server
];
const INTERNAL_EMAIL = ['@beyondessential.com.au', '@bes.au'];

export class SurveyResponseType extends DatabaseType {
static databaseType = TYPES.SURVEY_RESPONSE;
}
Expand All @@ -15,4 +30,22 @@ export class SurveyResponseModel extends MaterializedViewLogDatabaseModel {
get DatabaseTypeClass() {
return SurveyResponseType;
}

async getLeaderboard(rowCount = 10) {
return this.database.executeSql(
` SELECT r.user_id, user_account.first_name, user_account.last_name, r.coconuts, r.pigs
FROM (
SELECT user_id, COUNT(*) as coconuts, FLOOR(COUNT(*) / 100) as pigs
FROM survey_response
GROUP BY user_id
) r
JOIN user_account on user_account.id = r.user_id
WHERE ${INTERNAL_EMAIL.map(email => `email NOT LIKE '%${email}'`).join(' AND ')}
AND email NOT IN (${EXCLUDED_USERS.join(',')})
ORDER BY coconuts DESC
LIMIT ?;
`,
[rowCount],
);
}
}
3 changes: 3 additions & 0 deletions packages/datatrak-web-server/src/app/createApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
ProjectRoute,
SubmitSurveyRoute,
SubmitSurveyRequest,
LeaderboardRequest,
LeaderboardRoute,
} from '../routes';

const {
Expand All @@ -53,6 +55,7 @@ export function createApp() {
.get<SurveyResponsesRequest>('surveyResponses', handleWith(SurveyResponsesRoute))
.get<SurveyRequest>('surveys/:surveyCode', handleWith(SurveyRoute))
.get<ProjectsRequest>('projects', handleWith(ProjectsRoute))
.get<LeaderboardRequest>('leaderboard', handleWith(LeaderboardRoute))
.get<ProjectRequest>('project/:projectCode', handleWith(ProjectRoute))
.use('signup', forwardRequest(WEB_CONFIG_API_URL, { authHandlerProvider }))
// Forward everything else to central server
Expand Down
30 changes: 30 additions & 0 deletions packages/datatrak-web-server/src/models/SurveyResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
import {
SurveyResponseModel as BaseSurveyResponseModel,
SurveyResponseType as BaseSurveyResponseType,
} from '@tupaia/database';
import { Model } from '@tupaia/server-boilerplate';

export type SurveyResponseModelFields = Readonly<{
id: string;
survey_id: string;
user_id: string;
start_time: string;
end_time: string;
metadata: Record<string, any>;
timezone: string | null;
entity_id: string;
data_time: string | null;
outdated: boolean | null;
approval_status: string;
}>;

export interface SurveyResponseModelType
extends SurveyResponseModelFields,
Omit<BaseSurveyResponseType, 'id'> {} // Omit base `id: any` type as we explicity define as a string here

export interface SurveyResponseModel
extends Model<BaseSurveyResponseModel, SurveyResponseModelFields, SurveyResponseModelType> {}
1 change: 1 addition & 0 deletions packages/datatrak-web-server/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@

export { DataTrakSessionModel, DataTrakSessionType } from './DataTrakSession';
export { EntityModel } from './Entity';
export { SurveyResponseModel } from './SurveyResponse';
25 changes: 25 additions & 0 deletions packages/datatrak-web-server/src/routes/LeaderboardRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import { Request } from 'express';
import camelcaseKeys from 'camelcase-keys';
import { Route } from '@tupaia/server-boilerplate';
import { DatatrakWebLeaderboardRequest } from '@tupaia/types';

export type LeaderboardRequest = Request<
DatatrakWebLeaderboardRequest.Params,
DatatrakWebLeaderboardRequest.ResBody,
DatatrakWebLeaderboardRequest.ReqBody,
DatatrakWebLeaderboardRequest.ReqQuery
>;

export class LeaderboardRoute extends Route<LeaderboardRequest> {
public async buildResponse() {
const { models } = this.req;

const leaderboard = await models.surveyResponse.getLeaderboard();
return camelcaseKeys(leaderboard, { deep: true });
}
}
1 change: 1 addition & 0 deletions packages/datatrak-web-server/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export { ProjectsRequest, ProjectsRoute } from './ProjectsRoute';
export { EntitiesRequest, EntitiesRoute } from './EntitiesRoute';
export { ProjectRequest, ProjectRoute } from './ProjectRoute';
export { SubmitSurveyRequest, SubmitSurveyRoute } from './SubmitSurvey/SubmitSurveyRoute';
export { LeaderboardRequest, LeaderboardRoute } from './LeaderboardRoute';
2 changes: 2 additions & 0 deletions packages/datatrak-web-server/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import { ModelRegistry, EntityModel, EntityType as BaseEntityType } from '@tupaia/database';
import { Model } from '@tupaia/server-boilerplate';
import { Entity } from '@tupaia/types';
import { SurveyResponseModel } from './models';

export type EntityType = BaseEntityType & Entity;

export interface DatatrakWebServerModelRegistry extends ModelRegistry {
readonly entity: Model<EntityModel, Entity, EntityType>;
readonly surveyResponse: SurveyResponseModel;
}
7 changes: 5 additions & 2 deletions packages/datatrak-web/src/api/queries/useLeaderboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
import { useQuery } from 'react-query';
import { LeaderboardItem } from '@tupaia/types';
import { DatatrakWebLeaderboardRequest } from '@tupaia/types';
import { get } from '../api';

export const useLeaderboard = () => {
return useQuery(['leaderboard'], (): Promise<LeaderboardItem[]> => get('leaderboard'));
return useQuery(
['leaderboard'],
(): Promise<DatatrakWebLeaderboardRequest.ResBody> => get('leaderboard'),
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const LeaderboardTable = ({ userRewards }: LeaderboardTableProps) => {
if (isLoading) return null;

const userIsInLeaderboard =
user && leaderboard?.some(({ user_id: userId }) => userId === user.id);
user && leaderboard?.some(({ userId }) => userId === user.id);
return (
<TableContainer>
<Table>
Expand All @@ -93,7 +93,7 @@ export const LeaderboardTable = ({ userRewards }: LeaderboardTableProps) => {
</TableHead>
<TableBody>
{leaderboard?.map(
({ user_id: userId, first_name: firstName, last_name: lastName, coconuts }, i) => {
({ userId, firstName, lastName, coconuts }, i) => {
const isActiveUser = user && user.id === userId;
return (
<TableRow key={userId}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

// eslint-disable-next-line import/no-extraneous-dependencies
import MockDate from 'mockdate';

import { constructAccessToken } from '@tupaia/auth';
import {
findOrCreateDummyRecord,
Expand Down Expand Up @@ -60,6 +59,7 @@ describe('socialFeed', () => {
});

const databaseTimezone = await models.database.getTimezone();

const timezoneConvertedFeedItems = FEED_ITEMS.map(feedItem => ({
...feedItem,
creation_date: new Date(feedItem.creation_date).toLocaleString(databaseTimezone), // Adjust for timezone so tests work regardless of db timezone
Expand All @@ -69,6 +69,7 @@ describe('socialFeed', () => {
timezoneConvertedFeedItems,
countryCodeToId,
);

await Promise.all(
feedItemsToInsert.map(async feedItem => {
await findOrCreateDummyRecord(models.feedItem, feedItem, {});
Expand Down Expand Up @@ -113,6 +114,7 @@ describe('socialFeed', () => {
expect(hasMorePages).toBe(false);
expect(pageNumber).toBe(0);
const expectedItems = replaceItemsCountryWithCountryId(FEED_ITEMS, countryCodeToId);

addLeaderboardAsThirdItem(expectedItems, LEADERBOARD_ITEM);
expect(filterItemFields(items)).toEqual(expectedItems);
});
Expand Down
4 changes: 2 additions & 2 deletions packages/meditrak-app-server/src/routes/SocialFeedRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export type SocialFeedRequest = Request<

export class SocialFeedRoute extends Route<SocialFeedRequest> {
private async getLeaderboardFeedItem() {
const { ctx } = this.req;
const leaderboard = await ctx.services.central.fetchResources('/leaderboard');
const { models } = this.req;
const leaderboard = await models.surveyResponse.getLeaderboard();

return {
id: 'leaderboard',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import { LeaderboardItem } from '../../models-extra';

export type Params = Record<string, never>;

export type ResBody = LeaderboardItem[];
export type ReqBody = Record<string, never>;
export type ReqQuery = Record<string, never>;
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * as DatatrakWebProjectsRequest from './ProjectsRequest';
export * as DatatrakWebSurveyRequest from './SurveyRequest';
export * as DatatrakWebSubmitSurveyRequest from './SubmitSurveyRequest';
export * as DatatrakWebSurveyResponsesRequest from './SurveyResponsesRequest';
export * as DatatrakWebLeaderboardRequest from './LeaderboardRequest';
1 change: 1 addition & 0 deletions packages/types/src/types/requests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
DatatrakWebSubmitSurveyRequest,
DatatrakWebSurveyRequest,
DatatrakWebSurveyResponsesRequest,
DatatrakWebLeaderboardRequest,
} from './datatrak-web-server';
export {
TupaiaWebChangePasswordRequest,
Expand Down

0 comments on commit 747f182

Please sign in to comment.