Skip to content

Commit

Permalink
Add researcher codes (#197)
Browse files Browse the repository at this point in the history
* Add researcher codes

- backend
  - add ResearchGroup model with 6-digit id code for users to use when signing up
  - add checkdigit dependency to add checksum to code
  - add full_data_access flag to users if they should have research access to all data
- admin frontend users tab
  - add reseach_group_id & full_data_access to table
  - add button to add create a research code for a researcher
- frontend
  - add ResearchCodeInput input which checks the entered code checksum is valid
  - add optional code input to user signup, remove role selection
  - make url for signup /signup
  - add optional code e.g. /signup/123451 to allow researchers to send sign-up links to their users with code pre-filled
  - add cdigit dependency to validate codes
- resolves #185

* remove invalid research code from user upon registration to avoid (unlikely) scenario with unintentional sharing of data

* Add test of valid/invalid research codes - requires directly monkeypatching databases used in user.py

* ensure SMTPMock test fixture always starts with last_message = None
  • Loading branch information
lkeegan authored Dec 11, 2024
1 parent 09161ff commit de3f78d
Show file tree
Hide file tree
Showing 22 changed files with 419 additions and 68 deletions.
4 changes: 3 additions & 1 deletion frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ COPY package.json ./

COPY pnpm-lock.yaml ./

COPY pnpm-lock.yaml ./

RUN npm install -g pnpm && pnpm install

COPY . .

RUN echo "VITE_MONDEY_API_URL=${MONDEY_API_URL}" > .env && pnpm run build

FROM nginx:1.27.1
FROM nginx:1.27.3

COPY --from=builder /app/build /usr/share/nginx/html

Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"dependencies": {
"@hey-api/client-fetch": "0.4.3",
"@unovis/ts": "1.5.0-beta.0",
"cdigit": "^4.0.2",
"iso-639-1": "3.1.3",
"svelte-dnd-action": "^0.9.52",
"svelte-i18n": "^4.0.1"
Expand Down
17 changes: 17 additions & 0 deletions frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 67 additions & 1 deletion frontend/src/lib/client/schemas.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,18 @@ export const QuestionTextPublicSchema = {
title: 'QuestionTextPublic'
} as const;

export const ResearchGroupSchema = {
properties: {
id: {
type: 'integer',
title: 'Id'
}
},
type: 'object',
required: ['id'],
title: 'ResearchGroup'
} as const;

export const SubmittedMilestoneImagePublicSchema = {
properties: {
id: {
Expand Down Expand Up @@ -965,6 +977,30 @@ export const UserCreateSchema = {
],
title: 'Is Researcher',
default: false
},
full_data_access: {
anyOf: [
{
type: 'boolean'
},
{
type: 'null'
}
],
title: 'Full Data Access',
default: false
},
research_group_id: {
anyOf: [
{
type: 'integer'
},
{
type: 'null'
}
],
title: 'Research Group Id',
default: 0
}
},
type: 'object',
Expand Down Expand Up @@ -1126,10 +1162,18 @@ export const UserReadSchema = {
is_researcher: {
type: 'boolean',
title: 'Is Researcher'
},
full_data_access: {
type: 'boolean',
title: 'Full Data Access'
},
research_group_id: {
type: 'integer',
title: 'Research Group Id'
}
},
type: 'object',
required: ['id', 'email', 'is_researcher'],
required: ['id', 'email', 'is_researcher', 'full_data_access', 'research_group_id'],
title: 'UserRead'
} as const;

Expand Down Expand Up @@ -1201,6 +1245,28 @@ export const UserUpdateSchema = {
}
],
title: 'Is Researcher'
},
full_data_access: {
anyOf: [
{
type: 'boolean'
},
{
type: 'null'
}
],
title: 'Full Data Access'
},
research_group_id: {
anyOf: [
{
type: 'integer'
},
{
type: 'null'
}
],
title: 'Research Group Id'
}
},
type: 'object',
Expand Down
32 changes: 31 additions & 1 deletion frontend/src/lib/client/services.gen.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts

import { createClient, createConfig, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from '@hey-api/client-fetch';
import type { GetLanguagesError, GetLanguagesResponse, GetMilestonesError, GetMilestonesResponse, GetMilestoneData, GetMilestoneError, GetMilestoneResponse, GetMilestoneGroupsData, GetMilestoneGroupsError, GetMilestoneGroupsResponse, SubmitMilestoneImageData, SubmitMilestoneImageError, SubmitMilestoneImageResponse, GetUserQuestionsError, GetUserQuestionsResponse, GetChildQuestionsError, GetChildQuestionsResponse, CreateLanguageData, CreateLanguageError, CreateLanguageResponse, DeleteLanguageData, DeleteLanguageError, DeleteLanguageResponse, UpdateI18NData, UpdateI18NError, UpdateI18NResponse, GetMilestoneGroupsAdminError, GetMilestoneGroupsAdminResponse, CreateMilestoneGroupAdminError, CreateMilestoneGroupAdminResponse, UpdateMilestoneGroupAdminData, UpdateMilestoneGroupAdminError, UpdateMilestoneGroupAdminResponse, DeleteMilestoneGroupAdminData, DeleteMilestoneGroupAdminError, DeleteMilestoneGroupAdminResponse, OrderMilestoneGroupsAdminData, OrderMilestoneGroupsAdminError, OrderMilestoneGroupsAdminResponse, UploadMilestoneGroupImageData, UploadMilestoneGroupImageError, UploadMilestoneGroupImageResponse, CreateMilestoneData, CreateMilestoneError, CreateMilestoneResponse, UpdateMilestoneData, UpdateMilestoneError, UpdateMilestoneResponse, DeleteMilestoneData, DeleteMilestoneError, DeleteMilestoneResponse, OrderMilestonesAdminData, OrderMilestonesAdminError, OrderMilestonesAdminResponse, UploadMilestoneImageData, UploadMilestoneImageError, UploadMilestoneImageResponse, DeleteMilestoneImageData, DeleteMilestoneImageError, DeleteMilestoneImageResponse, GetSubmittedMilestoneImagesError, GetSubmittedMilestoneImagesResponse, ApproveSubmittedMilestoneImageData, ApproveSubmittedMilestoneImageError, ApproveSubmittedMilestoneImageResponse, DeleteSubmittedMilestoneImageData, DeleteSubmittedMilestoneImageError, DeleteSubmittedMilestoneImageResponse, GetMilestoneAgeScoresData, GetMilestoneAgeScoresError, GetMilestoneAgeScoresResponse, GetUserQuestionsAdminError, GetUserQuestionsAdminResponse, UpdateUserQuestionData, UpdateUserQuestionError, UpdateUserQuestionResponse, CreateUserQuestionError, CreateUserQuestionResponse, DeleteUserQuestionData, DeleteUserQuestionError, DeleteUserQuestionResponse, OrderUserQuestionsAdminData, OrderUserQuestionsAdminError, OrderUserQuestionsAdminResponse, GetChildQuestionsAdminError, GetChildQuestionsAdminResponse, UpdateChildQuestionData, UpdateChildQuestionError, UpdateChildQuestionResponse, CreateChildQuestionError, CreateChildQuestionResponse, DeleteChildQuestionData, DeleteChildQuestionError, DeleteChildQuestionResponse, OrderChildQuestionsAdminData, OrderChildQuestionsAdminError, OrderChildQuestionsAdminResponse, GetUsersError, GetUsersResponse, UsersCurrentUserError, UsersCurrentUserResponse, UsersPatchCurrentUserData, UsersPatchCurrentUserError, UsersPatchCurrentUserResponse, UsersUserData, UsersUserError, UsersUserResponse, UsersPatchUserData, UsersPatchUserError, UsersPatchUserResponse, UsersDeleteUserData, UsersDeleteUserError, UsersDeleteUserResponse, GetChildrenError, GetChildrenResponse, UpdateChildData, UpdateChildError, UpdateChildResponse, CreateChildData, CreateChildError, CreateChildResponse, GetChildData, GetChildError, GetChildResponse, DeleteChildData, DeleteChildError, DeleteChildResponse, GetChildImageData, GetChildImageError, GetChildImageResponse, UploadChildImageData, UploadChildImageError, UploadChildImageResponse, DeleteChildImageData, DeleteChildImageError, DeleteChildImageResponse, GetCurrentMilestoneAnswerSessionData, GetCurrentMilestoneAnswerSessionError, GetCurrentMilestoneAnswerSessionResponse, UpdateMilestoneAnswerData, UpdateMilestoneAnswerError, UpdateMilestoneAnswerResponse, GetCurrentUserAnswersError, GetCurrentUserAnswersResponse, UpdateCurrentUserAnswersData, UpdateCurrentUserAnswersError, UpdateCurrentUserAnswersResponse, GetCurrentChildAnswersData, GetCurrentChildAnswersError, GetCurrentChildAnswersResponse, UpdateCurrentChildAnswersData, UpdateCurrentChildAnswersError, UpdateCurrentChildAnswersResponse, GetExpiredMilestoneAnswerSessionsData, GetExpiredMilestoneAnswerSessionsError, GetExpiredMilestoneAnswerSessionsResponse, GetMilestonegroupsForSessionData, GetMilestonegroupsForSessionError, GetMilestonegroupsForSessionResponse, GetSummaryFeedbackForAnswersessionData, GetSummaryFeedbackForAnswersessionError, GetSummaryFeedbackForAnswersessionResponse, GetDetailedFeedbackForAnswersessionData, GetDetailedFeedbackForAnswersessionError, GetDetailedFeedbackForAnswersessionResponse, AuthCookieLoginData, AuthCookieLoginError, AuthCookieLoginResponse, AuthCookieLogoutError, AuthCookieLogoutResponse, RegisterRegisterData, RegisterRegisterError, RegisterRegisterResponse, ResetForgotPasswordData, ResetForgotPasswordError, ResetForgotPasswordResponse, ResetResetPasswordData, ResetResetPasswordError, ResetResetPasswordResponse, VerifyRequestTokenData, VerifyRequestTokenError, VerifyRequestTokenResponse, VerifyVerifyData, VerifyVerifyError, VerifyVerifyResponse, AuthError, AuthResponse } from './types.gen';
import type { GetLanguagesError, GetLanguagesResponse, GetMilestonesError, GetMilestonesResponse, GetMilestoneData, GetMilestoneError, GetMilestoneResponse, GetMilestoneGroupsData, GetMilestoneGroupsError, GetMilestoneGroupsResponse, SubmitMilestoneImageData, SubmitMilestoneImageError, SubmitMilestoneImageResponse, GetUserQuestionsError, GetUserQuestionsResponse, GetChildQuestionsError, GetChildQuestionsResponse, CreateLanguageData, CreateLanguageError, CreateLanguageResponse, DeleteLanguageData, DeleteLanguageError, DeleteLanguageResponse, UpdateI18NData, UpdateI18NError, UpdateI18NResponse, GetMilestoneGroupsAdminError, GetMilestoneGroupsAdminResponse, CreateMilestoneGroupAdminError, CreateMilestoneGroupAdminResponse, UpdateMilestoneGroupAdminData, UpdateMilestoneGroupAdminError, UpdateMilestoneGroupAdminResponse, DeleteMilestoneGroupAdminData, DeleteMilestoneGroupAdminError, DeleteMilestoneGroupAdminResponse, OrderMilestoneGroupsAdminData, OrderMilestoneGroupsAdminError, OrderMilestoneGroupsAdminResponse, UploadMilestoneGroupImageData, UploadMilestoneGroupImageError, UploadMilestoneGroupImageResponse, CreateMilestoneData, CreateMilestoneError, CreateMilestoneResponse, UpdateMilestoneData, UpdateMilestoneError, UpdateMilestoneResponse, DeleteMilestoneData, DeleteMilestoneError, DeleteMilestoneResponse, OrderMilestonesAdminData, OrderMilestonesAdminError, OrderMilestonesAdminResponse, UploadMilestoneImageData, UploadMilestoneImageError, UploadMilestoneImageResponse, DeleteMilestoneImageData, DeleteMilestoneImageError, DeleteMilestoneImageResponse, GetSubmittedMilestoneImagesError, GetSubmittedMilestoneImagesResponse, ApproveSubmittedMilestoneImageData, ApproveSubmittedMilestoneImageError, ApproveSubmittedMilestoneImageResponse, DeleteSubmittedMilestoneImageData, DeleteSubmittedMilestoneImageError, DeleteSubmittedMilestoneImageResponse, GetMilestoneAgeScoresData, GetMilestoneAgeScoresError, GetMilestoneAgeScoresResponse, GetUserQuestionsAdminError, GetUserQuestionsAdminResponse, UpdateUserQuestionData, UpdateUserQuestionError, UpdateUserQuestionResponse, CreateUserQuestionError, CreateUserQuestionResponse, DeleteUserQuestionData, DeleteUserQuestionError, DeleteUserQuestionResponse, OrderUserQuestionsAdminData, OrderUserQuestionsAdminError, OrderUserQuestionsAdminResponse, GetChildQuestionsAdminError, GetChildQuestionsAdminResponse, UpdateChildQuestionData, UpdateChildQuestionError, UpdateChildQuestionResponse, CreateChildQuestionError, CreateChildQuestionResponse, DeleteChildQuestionData, DeleteChildQuestionError, DeleteChildQuestionResponse, OrderChildQuestionsAdminData, OrderChildQuestionsAdminError, OrderChildQuestionsAdminResponse, GetUsersError, GetUsersResponse, GetResearchGroupsError, GetResearchGroupsResponse, CreateResearchGroupData, CreateResearchGroupError, CreateResearchGroupResponse, DeleteResearchGroupData, DeleteResearchGroupError, DeleteResearchGroupResponse, UsersCurrentUserError, UsersCurrentUserResponse, UsersPatchCurrentUserData, UsersPatchCurrentUserError, UsersPatchCurrentUserResponse, UsersUserData, UsersUserError, UsersUserResponse, UsersPatchUserData, UsersPatchUserError, UsersPatchUserResponse, UsersDeleteUserData, UsersDeleteUserError, UsersDeleteUserResponse, GetChildrenError, GetChildrenResponse, UpdateChildData, UpdateChildError, UpdateChildResponse, CreateChildData, CreateChildError, CreateChildResponse, GetChildData, GetChildError, GetChildResponse, DeleteChildData, DeleteChildError, DeleteChildResponse, GetChildImageData, GetChildImageError, GetChildImageResponse, UploadChildImageData, UploadChildImageError, UploadChildImageResponse, DeleteChildImageData, DeleteChildImageError, DeleteChildImageResponse, GetCurrentMilestoneAnswerSessionData, GetCurrentMilestoneAnswerSessionError, GetCurrentMilestoneAnswerSessionResponse, UpdateMilestoneAnswerData, UpdateMilestoneAnswerError, UpdateMilestoneAnswerResponse, GetCurrentUserAnswersError, GetCurrentUserAnswersResponse, UpdateCurrentUserAnswersData, UpdateCurrentUserAnswersError, UpdateCurrentUserAnswersResponse, GetCurrentChildAnswersData, GetCurrentChildAnswersError, GetCurrentChildAnswersResponse, UpdateCurrentChildAnswersData, UpdateCurrentChildAnswersError, UpdateCurrentChildAnswersResponse, GetExpiredMilestoneAnswerSessionsData, GetExpiredMilestoneAnswerSessionsError, GetExpiredMilestoneAnswerSessionsResponse, GetMilestonegroupsForSessionData, GetMilestonegroupsForSessionError, GetMilestonegroupsForSessionResponse, GetSummaryFeedbackForAnswersessionData, GetSummaryFeedbackForAnswersessionError, GetSummaryFeedbackForAnswersessionResponse, GetDetailedFeedbackForAnswersessionData, GetDetailedFeedbackForAnswersessionError, GetDetailedFeedbackForAnswersessionResponse, AuthCookieLoginData, AuthCookieLoginError, AuthCookieLoginResponse, AuthCookieLogoutError, AuthCookieLogoutResponse, RegisterRegisterData, RegisterRegisterError, RegisterRegisterResponse, ResetForgotPasswordData, ResetForgotPasswordError, ResetForgotPasswordResponse, ResetResetPasswordData, ResetResetPasswordError, ResetResetPasswordResponse, VerifyRequestTokenData, VerifyRequestTokenError, VerifyRequestTokenResponse, VerifyVerifyData, VerifyVerifyError, VerifyVerifyResponse, AuthError, AuthResponse } from './types.gen';

export const client = createClient(createConfig());

Expand Down Expand Up @@ -390,6 +390,36 @@ export const getUsers = <ThrowOnError extends boolean = false>(options?: Options
});
};

/**
* Get Research Groups
*/
export const getResearchGroups = <ThrowOnError extends boolean = false>(options?: Options<unknown, ThrowOnError>) => {
return (options?.client ?? client).get<GetResearchGroupsResponse, GetResearchGroupsError, ThrowOnError>({
...options,
url: '/admin/research-groups/'
});
};

/**
* Create Research Group
*/
export const createResearchGroup = <ThrowOnError extends boolean = false>(options: Options<CreateResearchGroupData, ThrowOnError>) => {
return (options?.client ?? client).post<CreateResearchGroupResponse, CreateResearchGroupError, ThrowOnError>({
...options,
url: '/admin/research-groups/{user_id}'
});
};

/**
* Delete Research Group
*/
export const deleteResearchGroup = <ThrowOnError extends boolean = false>(options: Options<DeleteResearchGroupData, ThrowOnError>) => {
return (options?.client ?? client).delete<DeleteResearchGroupResponse, DeleteResearchGroupError, ThrowOnError>({
...options,
url: '/admin/research-groups/{research_group_id}'
});
};

/**
* Users:Current User
*/
Expand Down
34 changes: 34 additions & 0 deletions frontend/src/lib/client/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ export type QuestionTextPublic = {
options?: string;
};

export type ResearchGroup = {
id: number;
};

export type SubmittedMilestoneImagePublic = {
id: number;
milestone_id: number;
Expand All @@ -240,6 +244,8 @@ export type UserCreate = {
is_superuser?: (boolean | null);
is_verified?: (boolean | null);
is_researcher?: (boolean | null);
full_data_access?: (boolean | null);
research_group_id?: (number | null);
};

export type UserQuestionAdmin = {
Expand Down Expand Up @@ -279,6 +285,8 @@ export type UserRead = {
is_superuser?: boolean;
is_verified?: boolean;
is_researcher: boolean;
full_data_access: boolean;
research_group_id: number;
};

export type UserUpdate = {
Expand All @@ -288,6 +296,8 @@ export type UserUpdate = {
is_superuser?: (boolean | null);
is_verified?: (boolean | null);
is_researcher?: (boolean | null);
full_data_access?: (boolean | null);
research_group_id?: (number | null);
};

export type ValidationError = {
Expand Down Expand Up @@ -584,6 +594,30 @@ export type GetUsersResponse = (Array<UserRead>);

export type GetUsersError = unknown;

export type GetResearchGroupsResponse = (Array<ResearchGroup>);

export type GetResearchGroupsError = unknown;

export type CreateResearchGroupData = {
path: {
user_id: number;
};
};

export type CreateResearchGroupResponse = (ResearchGroup);

export type CreateResearchGroupError = (HTTPValidationError);

export type DeleteResearchGroupData = {
path: {
research_group_id: number;
};
};

export type DeleteResearchGroupResponse = (unknown);

export type DeleteResearchGroupError = (HTTPValidationError);

export type UsersCurrentUserResponse = (UserRead);

export type UsersCurrentUserError = (unknown);
Expand Down
Loading

0 comments on commit de3f78d

Please sign in to comment.