@@ -25,15 +24,10 @@ export function ManifestDetails() {
);
}
- return loading ? (
+ return isLoading ? (
Something went wrong
diff --git a/client/src/features/NewManifest/NewManifest.spec.tsx b/client/src/features/NewManifest/NewManifest.spec.tsx
index db68e7dbe..b74e56df8 100644
--- a/client/src/features/NewManifest/NewManifest.spec.tsx
+++ b/client/src/features/NewManifest/NewManifest.spec.tsx
@@ -1,86 +1,58 @@
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';
import { NewManifest } from 'features/NewManifest/NewManifest';
+import { http, HttpResponse } from 'msw';
+import { setupServer } from 'msw/node';
import React from 'react';
-import { renderWithProviders, screen } from 'test-utils';
-import { createMockRcrainfoPermissions, createMockSite } from 'test-utils/fixtures';
-import { describe, expect, test } from 'vitest';
+import { HaztrakProfileResponse } from 'store/userSlice/user.slice';
+import { cleanup, renderWithProviders, screen } from 'test-utils';
+import { createMockSite } from 'test-utils/fixtures';
+import { userApiMocks } from 'test-utils/mock';
+import { API_BASE_URL } from 'test-utils/mock/htApiMocks';
+import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';
+
+const mySiteId = 'VATESTGEN001';
+const mySiteName = 'My Site';
+const mySite = createMockSite({
+ name: mySiteName,
+ // @ts-ignore
+ handler: { epaSiteId: mySiteId, siteType: 'Tsdf' },
+});
+
+const mockProfile: HaztrakProfileResponse = {
+ user: 'testuser1',
+ sites: [{ site: mySite, eManifest: 'viewer' }],
+ org: {
+ name: 'my org',
+ rcrainfoIntegrated: true,
+ id: '1234',
+ },
+};
+
+const server = setupServer(...userApiMocks);
+server.use(
+ http.get(`${API_BASE_URL}/api/user/profile`, () => {
+ return HttpResponse.json({ ...mockProfile }, { status: 200 });
+ })
+);
+afterEach(() => {
+ cleanup();
+});
+beforeAll(() => server.listen());
+afterAll(() => server.close()); // Disable API mocking after the tests are done.
describe('NewManifest', () => {
- test('renders', () => {
- const mySiteId = 'VATESTGEN001';
- const mySiteName = 'My Site';
- const mySite = createMockSite({
- name: mySiteName,
- // @ts-ignore
- handler: { epaSiteId: mySiteId, siteType: 'Tsdf' },
- });
- renderWithProviders(
, {
- preloadedState: {
- profile: {
- user: 'testuser1',
- sites: { VATESTGEN001: { ...mySite, permissions: { eManifest: 'viewer' } } },
- rcrainfoProfile: {
- user: 'username',
- phoneNumber: '1231231234',
- apiUser: false,
- rcraSites: {
- VATESTGEN001: {
- epaSiteId: mySiteId,
- permissions: createMockRcrainfoPermissions(),
- },
- },
- },
- },
- },
- });
+ test('renders', async () => {
+ renderWithProviders(
);
expect(screen.getByRole('combobox', { name: /site Role/i })).toBeInTheDocument();
});
test('site type is initially disabled', () => {
- const mySiteId = 'VATESTGEN001';
- const mySiteName = 'My Site';
- const mySite = createMockSite({
- name: mySiteName,
- // @ts-ignore
- handler: { epaSiteId: mySiteId, siteType: 'Tsdf' },
- });
- renderWithProviders(
, {
- preloadedState: {
- profile: {
- user: 'testuser1',
- rcrainfoProfile: {
- user: 'username',
- phoneNumber: '1231231234',
- apiUser: false,
- rcraSites: {
- VATESTGEN001: {
- epaSiteId: mySiteId,
- permissions: createMockRcrainfoPermissions(),
- },
- },
- },
- },
- },
- });
+ renderWithProviders(
);
const siteRole = screen.getByRole('combobox', { name: /site Role/i });
expect(siteRole).toBeDisabled();
});
test('site type is not disabled after selecting a site', async () => {
- const mySiteId = 'VATESTGEN001';
- const mySiteName = 'My Site';
- const mySite = createMockSite({
- name: mySiteName,
- // @ts-ignore
- handler: { epaSiteId: mySiteId, siteType: 'Tsdf' },
- });
- renderWithProviders(
, {
- preloadedState: {
- profile: {
- user: 'testuser1',
- sites: { VATESTGEN001: { ...mySite, permissions: { eManifest: 'viewer' } } },
- },
- },
- });
+ renderWithProviders(
);
const siteSelection = screen.getByRole('combobox', { name: /site select/i });
await userEvent.click(siteSelection);
await userEvent.keyboard('{enter}');
diff --git a/client/src/features/NewManifest/NewManifest.tsx b/client/src/features/NewManifest/NewManifest.tsx
index ddfe74583..f283ca3c9 100644
--- a/client/src/features/NewManifest/NewManifest.tsx
+++ b/client/src/features/NewManifest/NewManifest.tsx
@@ -1,14 +1,15 @@
+import { createSelector } from '@reduxjs/toolkit';
import { Manifest, ManifestForm } from 'components/Manifest';
import { RcraSiteType } from 'components/Manifest/manifestSchema';
import { SiteSelect, SiteTypeSelect } from 'components/Manifest/SiteSelect';
import { RcraSite } from 'components/RcraSite';
-import { HtCard } from 'components/UI';
+import { HtCard, HtSpinner } from 'components/UI';
import { useTitle } from 'hooks';
-import React, { useState } from 'react';
+import React, { useMemo, useState } from 'react';
import { Col, Container, Form } from 'react-bootstrap';
import { useForm } from 'react-hook-form';
import { useParams } from 'react-router-dom';
-import { siteByEpaIdSelector, useAppSelector } from 'store';
+import { useGetProfileQuery } from 'store';
/**
* NewManifest component allows a user to create a new electronic manifest.
@@ -21,7 +22,23 @@ export function NewManifest() {
useTitle('New Manifest');
const { control } = useForm();
const { siteId } = useParams();
- const rcraSite = useAppSelector(siteByEpaIdSelector(siteId));
+
+ const selectBySiteId = useMemo(() => {
+ return createSelector(
+ (res) => res.data,
+ (res, siteId) => siteId,
+ (data, siteId) => {
+ return data?.sites[siteId]?.handler ?? undefined;
+ }
+ );
+ }, []);
+
+ const { rcraSite, isLoading } = useGetProfileQuery(undefined, {
+ selectFromResult: (result) => ({
+ ...result,
+ rcraSite: selectBySiteId(result, siteId),
+ }),
+ });
const [manifestingSite, setManifestingSite] = useState
(rcraSite);
const [selectedSiteType, setSelectedSiteType] = useState(
rcraSite?.siteType === 'Generator' ? rcraSite.siteType : undefined
@@ -30,6 +47,8 @@ export function NewManifest() {
rcraSite?.siteType
);
+ if (isLoading && siteId) return ;
+
const handleSiteChange = (site: any) => {
setManifestingSite(site);
setManifestingSiteType(site.siteType);
diff --git a/client/src/features/Profile/Profile.tsx b/client/src/features/Profile/Profile.tsx
index c58c1598f..93539c8f8 100644
--- a/client/src/features/Profile/Profile.tsx
+++ b/client/src/features/Profile/Profile.tsx
@@ -1,18 +1,11 @@
import { UserOrg } from 'components/Org';
import { RcraProfile } from 'components/RcraProfile';
-import { HtCard } from 'components/UI';
+import { HtCard, HtSpinner } from 'components/UI';
import { UserInfoForm } from 'components/User';
import { useTitle } from 'hooks';
-import React, { ReactElement, useEffect } from 'react';
+import React, { ReactElement } from 'react';
import { Col, Container, Row } from 'react-bootstrap';
-import {
- getRcraProfile,
- HaztrakUser,
- selectHaztrakProfile,
- selectUser,
- useAppDispatch,
- useAppSelector,
-} from 'store';
+import { useGetProfileQuery, useGetRcrainfoProfileQuery, useGetUserQuery } from 'store';
/**
* Display user profile, including their Haztrak information, their organization,
@@ -20,19 +13,16 @@ import {
* @constructor
*/
export function Profile(): ReactElement {
- const dispatch = useAppDispatch();
- const profile = useAppSelector(selectHaztrakProfile);
- const user: HaztrakUser | undefined = useAppSelector(selectUser);
+ const { data: profile, isLoading: profileLoading } = useGetProfileQuery();
+ const { data: user, isLoading: userLoading } = useGetUserQuery();
+ const { data: rcrainfoProfile } = useGetRcrainfoProfileQuery('testuser1');
+ const isLoading = profileLoading || userLoading;
useTitle('Profile');
- if (!user) {
- return loading...
;
+ if (isLoading ?? !user ?? !profile) {
+ return ;
}
- useEffect(() => {
- dispatch(getRcraProfile());
- }, [profile.user]);
-
return (
<>
@@ -49,7 +39,7 @@ export function Profile(): ReactElement {
- {profile.org && }
+ {profile && }
@@ -57,7 +47,7 @@ export function Profile(): ReactElement {
- {profile.rcrainfoProfile && }
+ {rcrainfoProfile && }
diff --git a/client/src/features/SiteList/SiteList.spec.tsx b/client/src/features/SiteList/SiteList.spec.tsx
index 54fb6d0db..028237f0d 100644
--- a/client/src/features/SiteList/SiteList.spec.tsx
+++ b/client/src/features/SiteList/SiteList.spec.tsx
@@ -3,7 +3,8 @@ import { setupServer } from 'msw/node';
import React from 'react';
import { renderWithProviders, screen } from 'test-utils';
import { createMockHandler, createMockSite } from 'test-utils/fixtures/mockHandler';
-import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';
+import { htApiMocks, userApiMocks } from 'test-utils/mock';
+import { afterAll, beforeAll, describe, expect, test } from 'vitest';
import { SiteList } from './SiteList';
const mockHandler1 = createMockHandler({ epaSiteId: 'VAT987654321' });
@@ -12,11 +13,7 @@ const mockSites = [
createMockSite({ handler: mockHandler1 }),
createMockSite({ handler: mockHandler2 }),
];
-const server = setupServer(
- http.get(`${import.meta.env.VITE_HT_API_URL}/api/site`, (info) => {
- return HttpResponse.json(mockSites, { status: 200 });
- })
-);
+const server = setupServer(...userApiMocks, ...htApiMocks);
// pre-/post-test hooks
beforeAll(() => server.listen());
@@ -26,11 +23,14 @@ describe('SiteList component', () => {
test('renders', () => {
renderWithProviders(, {});
});
- test('fetches displays all sites a user has access to', async () => {
- // Act
+ test('displays all sites a user has access to', async () => {
+ server.use(
+ http.get(`${import.meta.env.VITE_HT_API_URL}/api/site`, (info) => {
+ return HttpResponse.json(mockSites, { status: 200 });
+ })
+ );
renderWithProviders();
let numIds = await screen.findAllByRole('listitem');
- // Assert
expect(numIds.length).toEqual(mockSites.length);
});
});
diff --git a/client/src/hooks/index.ts b/client/src/hooks/index.ts
index 60c17db15..2a9292a7c 100644
--- a/client/src/hooks/index.ts
+++ b/client/src/hooks/index.ts
@@ -1,6 +1,3 @@
-import { useHtApi } from './useHtAPI/useHtApi';
-import { usePagination } from './usePagination/usePagination';
-import { useProgressTracker } from './useProgressTracker/useProgressTracker';
-import { useTitle } from './useTitle/useTitle';
-
-export { usePagination, useTitle, useHtApi, useProgressTracker };
+export { usePagination } from './usePagination/usePagination';
+export { useProgressTracker } from './useProgressTracker/useProgressTracker';
+export { useTitle } from './useTitle/useTitle';
diff --git a/client/src/hooks/useHtAPI/useHtApi.spec.tsx b/client/src/hooks/useHtAPI/useHtApi.spec.tsx
deleted file mode 100644
index e62832880..000000000
--- a/client/src/hooks/useHtAPI/useHtApi.spec.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import '@testing-library/jest-dom';
-import { cleanup } from '@testing-library/react';
-import { useHtApi } from 'hooks';
-import { http, HttpResponse } from 'msw';
-import { setupServer } from 'msw/node';
-import React from 'react';
-import { render, renderWithProviders, screen } from 'test-utils';
-import { afterAll, afterEach, beforeAll, describe, expect, test, vi } from 'vitest';
-
-interface exampleData {
- foo: string;
- bar: string;
-}
-
-interface exampleProps {
- url: string;
-}
-
-function TestComponent({ url }: exampleProps) {
- const [data, loading, error] = useHtApi(url);
- if (error) {
- return (
- <>
- {error.message}
- >
- );
- } else {
- return <>{loading ? loading
: {data?.foo}
}>;
- }
-}
-
-/**
- * mock Rest API
- */
-const API_BASE_URL = import.meta.env.VITE_HT_API_URL;
-export const testURL = [
- http.get(`${API_BASE_URL}/api/test/url`, (info) => {
- return HttpResponse.json(
- {
- foo: 'foo',
- bar: 'bar',
- },
- { status: 200 }
- );
- }),
- http.get(`${API_BASE_URL}/api/bad/url`, (info) => {
- return HttpResponse.json(
- {
- error: {
- message: 'resource not found',
- },
- },
- { status: 404 }
- );
- }),
-];
-
-const server = setupServer(...testURL);
-
-beforeAll(() => server.listen()); // setup mock http server
-afterEach(() => {
- server.resetHandlers();
- cleanup();
- vi.resetAllMocks();
-});
-afterAll(() => server.close()); // Disable API mocking after the tests are done.
-
-afterEach(() => {
- cleanup();
- vi.resetAllMocks();
-});
-
-describe('useHtAPI', () => {
- test('initially sets loading to true', () => {
- render();
- expect(screen.getByText(/loading/i)).toBeInTheDocument();
- });
- test('sets data to the response body', async () => {
- renderWithProviders();
- expect(await screen.findByText(/foo/i)).toBeInTheDocument();
- });
- test('sets the error for bad requests', async () => {
- renderWithProviders();
- expect(await screen.findByText(/404/i)).toBeInTheDocument();
- });
-});
diff --git a/client/src/hooks/useHtAPI/useHtApi.tsx b/client/src/hooks/useHtAPI/useHtApi.tsx
deleted file mode 100644
index 728fa49e2..000000000
--- a/client/src/hooks/useHtAPI/useHtApi.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { useEffect, useState } from 'react';
-import { htApi } from 'services';
-
-/**
- * Hook for retrieving data from the haztrak http server
- *
- * @description
- * Easy abstraction for GET request from the haztrak http server. Currently,
- * does not support other http methods since those situations tend to require
- * more thought.
- *
- * @param url {string}
- * @return array {[data, loading, error]}
- *
- * @example
- * const [data, loading , error ] = useHtApi(`/api/resource/path/${id}`)
- */
-export function useHtApi(url: string) {
- const [data, setData] = useState(undefined);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState(undefined);
-
- useEffect(() => {
- if (!url) return;
- htApi
- .get(url)
- .then((response) => setData(response.data))
- .then(() => setLoading(false))
- .catch(setError);
- }, [url]);
-
- return [data, loading, error] as const;
-}
diff --git a/client/src/services/APIs/UserApi.ts b/client/src/services/APIs/UserApi.ts
deleted file mode 100644
index 67804a049..000000000
--- a/client/src/services/APIs/UserApi.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { AxiosResponse } from 'axios';
-import { HaztrakSite } from 'components/HaztrakSite';
-import { HaztrakModulePermissions, HaztrakUser, RcrainfoProfile, RcrainfoProfileSite } from 'store';
-import { htApi } from './htApi';
-
-interface HaztrakOrgResponse {
- id: string;
- name: string;
- rcrainfoIntegrated: boolean;
-}
-
-interface HaztrakProfileResponse {
- user: string;
- sites: Array<{
- site: HaztrakSite;
- eManifest: HaztrakModulePermissions;
- }>;
- org?: HaztrakOrgResponse;
-}
-
-interface RcrainfoProfileResponse extends RcrainfoProfile> {}
-
-export const UserApi = {
- /** Fetch the user's Haztrak profile from the Haztrak API*/
- getUserProfile: async (): Promise> => {
- return await htApi.get('/profile');
- },
-
- /** Fetch Haztrak user from server*/
- getUser: async (): Promise> => {
- return await htApi.get('/user');
- },
-
- /** Fetch Haztrak user from server*/
- updateUser: async (data: Partial): Promise> => {
- return await htApi.put('/user', data);
- },
-
- /** Fetch the user's RCRAInfo profile from the Haztrak API*/
- getRcrainfoProfile: async (username: string): Promise> => {
- return await htApi.get(`/rcra/profile/${username}`);
- },
-
- /** Update user's RCRAInfo Profile information such username, api ID & key*/
- updateRcrainfoProfile: async ({
- username,
- data,
- }: {
- username: string;
- data: any;
- }): Promise> => {
- return await htApi.put(`/rcra/profile/${username}`, data);
- },
-
- /** Launch task to pull user's site/module permissions (RCRAInfo profile) from RCRAInfo*/
- syncRcrainfoProfile: async (): Promise> => {
- return await htApi.get(`rcra/profile/sync`);
- },
-};
diff --git a/client/src/services/index.ts b/client/src/services/index.ts
index 27aba813c..131e298ef 100644
--- a/client/src/services/index.ts
+++ b/client/src/services/index.ts
@@ -1,3 +1,2 @@
export { htApi } from 'services/APIs/htApi';
-export { UserApi } from 'services/APIs/UserApi';
export { manifest } from 'services/manifest/manifest';
diff --git a/client/src/store/authSlice/auth.slice.spec.tsx b/client/src/store/authSlice/auth.slice.spec.tsx
new file mode 100644
index 000000000..2ee8c01fe
--- /dev/null
+++ b/client/src/store/authSlice/auth.slice.spec.tsx
@@ -0,0 +1,86 @@
+import '@testing-library/jest-dom';
+import userEvent from '@testing-library/user-event';
+import {
+ removeCredentials,
+ selectAuthenticated,
+ setCredentials,
+ useAppDispatch,
+ useAppSelector,
+} from 'store';
+import { renderWithProviders, screen } from 'test-utils';
+import { afterEach, describe, expect, test, vi } from 'vitest';
+
+const getItemSpy = vi.spyOn(Storage.prototype, 'getItem');
+const setItemSpy = vi.spyOn(Storage.prototype, 'setItem');
+const removeItemSpy = vi.spyOn(Storage.prototype, 'removeItem');
+
+afterEach(() => {
+ getItemSpy.mockClear();
+ setItemSpy.mockClear();
+ removeItemSpy.mockClear();
+});
+
+const TestComponent = () => {
+ const dispatch = useAppDispatch();
+ const isAuthenticated = useAppSelector(selectAuthenticated);
+ return (
+
+
Test
+
{isAuthenticated ? 'authenticated' : 'unauthenticated'}
+
+
+
+
+
+ );
+};
+
+describe('auth slice', () => {
+ test('our localStorage mocks are working', () => {
+ const value = 'test';
+ localStorage.setItem('token', value);
+ expect(setItemSpy).toHaveBeenCalled();
+ const foo = localStorage.getItem('token');
+ expect(setItemSpy).toHaveBeenCalled();
+ expect(foo).toBe(value);
+ });
+ test('initial state is unauthenticated', () => {
+ renderWithProviders();
+ expect(screen.queryByText('unauthenticated')).toBeInTheDocument();
+ expect(screen.queryByText('authenticated')).not.toBeInTheDocument();
+ });
+ test('setCredentials stores the token in local storage', async () => {
+ renderWithProviders();
+ await userEvent.click(screen.getByText('Log in'));
+ expect(setItemSpy).toHaveBeenCalled();
+ });
+ test('selectAuthenticated is true after credentials are set', async () => {
+ renderWithProviders();
+ await userEvent.click(screen.getByText('Log in'));
+ expect(screen.queryByText('authenticated')).toBeInTheDocument();
+ });
+ test('selectAuthenticated is false removeCredentials is dispatched', async () => {
+ renderWithProviders(, { preloadedState: { auth: { token: 'mockToken' } } });
+ localStorage.setItem('token', 'mockToken');
+ await userEvent.click(screen.getByText('Log out'));
+ expect(screen.queryByText('unauthenticated')).toBeInTheDocument();
+ });
+ test('tokens are removed from localStorage on logout', async () => {
+ renderWithProviders(, { preloadedState: { auth: { token: 'mockToken' } } });
+ localStorage.setItem('token', 'mockToken');
+ await userEvent.click(screen.getByText('Log out'));
+ expect(removeItemSpy).toHaveBeenCalled();
+ });
+});
diff --git a/client/src/store/authSlice/auth.slice.ts b/client/src/store/authSlice/auth.slice.ts
index be76a59ef..7dc0ddd9f 100644
--- a/client/src/store/authSlice/auth.slice.ts
+++ b/client/src/store/authSlice/auth.slice.ts
@@ -1,7 +1,4 @@
-import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
-import axios from 'axios';
-import { UserApi } from 'services';
-import { RootState } from 'store/rootStore';
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
export interface HaztrakUser {
username: string;
@@ -15,122 +12,43 @@ export interface HaztrakUser {
/**
* The Redux stored information on the current haztrak user
*/
-export interface UserState {
+export interface AuthSlice {
user?: HaztrakUser;
token?: string;
loading?: boolean;
error?: string;
}
-const initialState: UserState = {
- // Retrieve the user's username and token from local storage. For convenience
+const initialState: AuthSlice = {
user: { username: JSON.parse(localStorage.getItem('user') || 'null') || null, isLoading: false },
token: JSON.parse(localStorage.getItem('token') || 'null') || null,
loading: false,
error: undefined,
};
-
-export const login = createAsyncThunk(
- 'auth/login',
- async ({ username, password }: { username: string; password: string }) => {
- const response = await axios.post(`${import.meta.env.VITE_HT_API_URL}/api/user/login`, {
- username,
- password,
- });
- // return response.data as UserState;
- return {
- user: { username: response.data.user },
- token: response.data.token,
- } as UserState;
- }
-);
-
-/** Fetch a Haztrak User's information and store in global state */
-export const getHaztrakUser = createAsyncThunk('auth/getHaztrakUser', async (arg, thunkAPI) => {
- try {
- const { data } = await UserApi.getUser();
- return data;
- } catch (err) {
- return thunkAPI.rejectWithValue(err);
- }
-});
-
-const authSlice = createSlice({
+export const authSlice = createSlice({
name: 'auth',
initialState,
+ selectors: {
+ selectAuthenticated: (state: AuthSlice): boolean => !!state.token,
+ selectUser: (state: AuthSlice): HaztrakUser | undefined => state.user,
+ selectUserName: (state: AuthSlice): string | undefined => state.user?.username,
+ },
reducers: {
- logout(state: UserState): UserState {
- localStorage.removeItem('user');
- localStorage.removeItem('token');
- return { ...initialState, user: undefined, token: undefined };
- },
- updateUserProfile(state: UserState, action: PayloadAction) {
+ setCredentials(state: AuthSlice, action: PayloadAction<{ token: string }>) {
+ const { token } = action.payload;
+ localStorage.setItem('token', JSON.stringify(token));
return {
...state,
- user: action.payload,
- };
+ token,
+ } as AuthSlice;
+ },
+ removeCredentials(): AuthSlice {
+ localStorage.removeItem('token');
+ return { ...initialState, user: undefined, token: undefined };
},
- },
- extraReducers: (builder) => {
- builder
- .addCase(login.pending, (state) => {
- return {
- ...state,
- error: undefined,
- loading: true,
- };
- })
- .addCase(login.fulfilled, (state, action) => {
- const authResponse = action.payload;
- localStorage.setItem('user', JSON.stringify(authResponse.user?.username));
- localStorage.setItem('token', JSON.stringify(authResponse.token));
- return {
- loading: false,
- error: undefined,
- ...authResponse,
- };
- })
- .addCase(login.rejected, (state, action) => {
- return {
- ...state,
- // @ts-ignore
- error: action.payload.error,
- loading: false,
- };
- })
- .addCase(getHaztrakUser.pending, (state) => {
- return {
- ...state,
- error: undefined,
- loading: true,
- };
- })
- .addCase(getHaztrakUser.rejected, (state, action) => {
- return {
- ...state,
- error: `Error: ${action.payload}`,
- loading: true,
- };
- })
- .addCase(getHaztrakUser.fulfilled, (state, action) => {
- return {
- ...state,
- user: action.payload,
- error: undefined,
- loading: true,
- };
- });
},
});
-/** Get the current user's username from the Redux store*/
-export const selectUserName = (state: RootState): string | undefined => state.auth.user?.username;
-
-/** Select the current user*/
-export const selectUser = (state: RootState): HaztrakUser | undefined => state.auth.user;
-
-/** Select the current User State*/
-export const selectUserState = (state: RootState): UserState => state.auth;
-
export default authSlice.reducer;
-export const { updateUserProfile, logout } = authSlice.actions;
+export const { removeCredentials, setCredentials } = authSlice.actions;
+export const { selectAuthenticated, selectUser, selectUserName } = authSlice.selectors;
diff --git a/client/src/store/haztrakApiSlice.ts b/client/src/store/htApi.slice.ts
similarity index 86%
rename from client/src/store/haztrakApiSlice.ts
rename to client/src/store/htApi.slice.ts
index 3da5f872a..f2a8dac6c 100644
--- a/client/src/store/haztrakApiSlice.ts
+++ b/client/src/store/htApi.slice.ts
@@ -2,11 +2,11 @@ import { BaseQueryFn, createApi } from '@reduxjs/toolkit/query/react';
import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
import { HaztrakSite } from 'components/HaztrakSite';
import { Manifest } from 'components/Manifest';
+import { QuickerSignature } from 'components/Manifest/QuickerSign';
import { Code } from 'components/Manifest/WasteLine/wasteLineSchema';
import { MtnDetails } from 'components/Mtn';
import { RcraSite } from 'components/RcraSite';
import { htApi } from 'services';
-import { QuickerSignature } from 'components/Manifest/QuickerSign';
export interface TaskResponse {
taskId: string;
@@ -19,10 +19,8 @@ export interface HtApiQueryArgs {
params?: AxiosRequestConfig['params'];
}
-export interface HtApiError {
- status?: number;
+export interface HtApiError extends AxiosError {
data?: AxiosResponse['data'];
- code?: string;
statusText?: string;
}
@@ -50,10 +48,10 @@ export const htApiBaseQuery =
return { data: response.data };
} catch (axiosError) {
let err = axiosError as AxiosError;
+ console.log(err);
return {
error: {
- code: err.code,
- status: err.response?.status,
+ ...err,
statusText: err.response?.statusText,
data: err.response?.data || err.message,
} as HtApiError,
@@ -76,6 +74,7 @@ interface RcrainfoSiteSearch {
}
export const haztrakApi = createApi({
+ tagTypes: ['user', 'auth', 'profile', 'rcrainfoProfile', 'site', 'code', 'manifest'],
reducerPath: 'haztrakApi',
baseQuery: htApiBaseQuery({
baseUrl: `${import.meta.env.VITE_HT_API_URL}/api/`,
@@ -101,24 +100,35 @@ export const haztrakApi = createApi({
}),
getFedWasteCodes: build.query, void>({
query: () => ({ url: 'rcra/waste/code/federal', method: 'get' }),
+ providesTags: ['code'],
}),
getStateWasteCodes: build.query, string>({
query: (state) => ({ url: `rcra/waste/code/state/${state}`, method: 'get' }),
+ providesTags: ['code'],
}),
getDotIdNumbers: build.query, string>({
query: (id) => ({ url: 'rcra/waste/dot/id', method: 'get', params: { q: id } }),
+ providesTags: ['code'],
}),
getOrgSites: build.query, string>({
query: (id) => ({ url: `org/${id}/site`, method: 'get' }),
+ providesTags: ['site'],
}),
getUserHaztrakSites: build.query, void>({
query: () => ({ url: 'site', method: 'get' }),
+ providesTags: ['site'],
}),
getUserHaztrakSite: build.query({
query: (epaId) => ({ url: `site/${epaId}`, method: 'get' }),
+ providesTags: ['site'],
}),
getMTN: build.query, string | undefined>({
query: (siteId) => ({ url: siteId ? `rcra/mtn/${siteId}` : 'rcra/mtn', method: 'get' }),
+ providesTags: ['manifest'],
+ }),
+ getManifest: build.query({
+ query: (mtn) => ({ url: `rcra/manifest/${mtn}`, method: 'get' }),
+ providesTags: ['manifest'],
}),
createManifest: build.mutation({
query: (data) => ({
@@ -126,6 +136,7 @@ export const haztrakApi = createApi({
method: 'POST',
data,
}),
+ invalidatesTags: ['manifest'],
}),
updateManifest: build.mutation({
query: ({ mtn, manifest }) => ({
@@ -133,6 +144,7 @@ export const haztrakApi = createApi({
method: 'PUT',
data: manifest,
}),
+ invalidatesTags: ['manifest'],
}),
saveEManifest: build.mutation({
query: (data) => ({
@@ -140,6 +152,7 @@ export const haztrakApi = createApi({
method: 'POST',
data,
}),
+ invalidatesTags: ['manifest'],
}),
syncEManifest: build.mutation({
query: (siteId) => ({
@@ -147,6 +160,7 @@ export const haztrakApi = createApi({
method: 'POST',
data: { siteId: siteId },
}),
+ invalidatesTags: ['manifest'],
}),
signEManifest: build.mutation({
query: (signature) => ({
@@ -154,6 +168,7 @@ export const haztrakApi = createApi({
method: 'POST',
data: signature,
}),
+ invalidatesTags: ['manifest'],
}),
}),
});
diff --git a/client/src/store/index.ts b/client/src/store/index.ts
index b7b5e8251..aab921280 100644
--- a/client/src/store/index.ts
+++ b/client/src/store/index.ts
@@ -1,6 +1,7 @@
-import type { AppDispatch, AppStore, RootState } from './rootStore';
// Haztrak API - RTK Query
-import { haztrakApi } from './haztrakApiSlice';
+import { haztrakApi } from 'store/htApi.slice';
+import { userApi } from 'store/userSlice/user.slice';
+import type { AppDispatch, AppStore, RootState } from './rootStore';
// Root Store
export { rootStore, setupStore, useAppDispatch, useAppSelector } from './rootStore';
@@ -22,30 +23,28 @@ export const {
useSyncEManifestMutation,
useSignEManifestMutation,
useUpdateManifestMutation,
+ useGetManifestQuery,
} = haztrakApi;
+export const {
+ useLoginMutation,
+ useGetUserQuery,
+ useGetProfileQuery,
+ useGetRcrainfoProfileQuery,
+ useUpdateUserMutation,
+ useUpdateRcrainfoProfileMutation,
+ useSyncRcrainfoProfileMutation,
+} = userApi;
+
// Authentication Slice
export {
- getHaztrakUser,
- login,
selectUser,
selectUserName,
- selectUserState,
- updateUserProfile,
-} from './authSlice/auth.slice';
-
-// Profile Slice
-export {
- getHaztrakProfile,
- getRcraProfile,
- selectRcrainfoSites,
- selectRcraProfile,
- siteByEpaIdSelector,
- updateProfile,
- selectHaztrakSites,
- selectHaztrakSiteEpaIds,
- selectHaztrakProfile,
-} from './profileSlice/profile.slice';
+ setCredentials,
+ selectAuthenticated,
+ removeCredentials,
+} from 'store/authSlice/auth.slice';
+export type { HaztrakUser } from 'store/authSlice/auth.slice';
// Notification Slice
export {
@@ -64,9 +63,8 @@ export {
export { addError, selectAllErrors } from './errorSlice/error.slice';
// Types
-export type { HaztrakUser } from './authSlice/auth.slice';
export type { HaztrakError } from './errorSlice/error.slice';
-export type { TaskStatus } from './haztrakApiSlice';
+export type { TaskStatus } from 'store/htApi.slice';
export type { LongRunningTask, HaztrakAlert } from './notificationSlice/notification.slice';
export type {
ProfileSlice,
@@ -75,6 +73,7 @@ export type {
HaztrakSitePermissions,
RcrainfoSitePermissions,
HaztrakModulePermissions,
+ HaztrakProfileOrg,
RcrainfoProfile,
RcrainfoProfileSite,
-} from './profileSlice/profile.slice';
+} from './userSlice/user.slice';
diff --git a/client/src/store/profileSlice/profile.slice.spec.tsx b/client/src/store/profileSlice/profile.slice.spec.tsx
deleted file mode 100644
index bcc217b63..000000000
--- a/client/src/store/profileSlice/profile.slice.spec.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * RcraProfile tests
- */
-import '@testing-library/jest-dom';
-import { screen } from '@testing-library/react';
-import React from 'react';
-import { useAppSelector } from 'store';
-import { renderWithProviders } from 'test-utils';
-import { createMockSite } from 'test-utils/fixtures';
-import { createMockRcrainfoPermissions } from 'test-utils/fixtures/mockHandler';
-import { describe, expect, test } from 'vitest';
-import { selectRcrainfoSites, siteByEpaIdSelector } from './profile.slice';
-
-interface TestComponentProps {
- siteId: string;
-}
-
-function TestComponent({ siteId }: TestComponentProps) {
- const rcraSite = useAppSelector(siteByEpaIdSelector(siteId));
- return (
- <>
- {rcraSite?.epaSiteId}
- >
- );
-}
-
-describe('RcraProfileSlice selectors', () => {
- test('Retrieve RcraProfileSite by EPA ID', () => {
- const mySite = createMockSite();
- renderWithProviders(, {
- preloadedState: {
- profile: {
- user: 'testuser1',
- sites: { VATESTGEN001: { ...mySite, permissions: { eManifest: 'viewer' } } },
- rcrainfoProfile: {
- user: 'username',
- phoneNumber: '1231231234',
- apiUser: false,
- rcraSites: {
- VATESTGEN001: {
- epaSiteId: mySite.handler.epaSiteId,
- permissions: createMockRcrainfoPermissions(),
- },
- },
- },
- },
- },
- });
- expect(screen.getByText(mySite.handler.epaSiteId)).toBeInTheDocument();
- });
- test('retrieve all RcraProfileSites', () => {
- const mySite = createMockSite();
- const TestComp = () => {
- const myRcraSite = useAppSelector(selectRcrainfoSites);
- return (
- <>
- {myRcraSite ? Object.keys(myRcraSite).length : 'not defined'}
- {myRcraSite
- ? myRcraSite.map((site, index) => (
- {site.epaSiteId}
- ))
- : 'not defined'}
- >
- );
- };
- renderWithProviders(, {
- preloadedState: {
- profile: {
- user: 'testuser1',
- rcrainfoProfile: {
- user: 'username',
- phoneNumber: '1231231234',
- apiUser: false,
- rcraSites: {
- VATESTGEN001: {
- epaSiteId: mySite.handler.epaSiteId,
- permissions: createMockRcrainfoPermissions(),
- },
- VATEST00001: {
- epaSiteId: mySite.handler.epaSiteId,
- permissions: createMockRcrainfoPermissions(),
- },
- },
- },
- },
- },
- });
- expect(screen.getByText('2')).toBeInTheDocument();
- expect(screen.queryByText(/Not Defined/i)).not.toBeInTheDocument();
- });
-});
diff --git a/client/src/store/profileSlice/profile.slice.ts b/client/src/store/profileSlice/profile.slice.ts
deleted file mode 100644
index b7594b012..000000000
--- a/client/src/store/profileSlice/profile.slice.ts
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * A user's RcraProfile slice encapsulates our logic related what actions and data a user
- * has access to for each EPA site ID.
- */
-import { createAsyncThunk, createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
-import { HaztrakSite } from 'components/HaztrakSite';
-import { RcraSite } from 'components/RcraSite';
-import { UserApi } from 'services';
-import { RootState } from 'store';
-
-/**The user's RCRAInfo account data stored in the Redux store*/
-export interface ProfileSlice {
- user: string | undefined;
- rcrainfoProfile?: RcrainfoProfile>;
- sites?: Record;
- org?: HaztrakProfileOrg | null;
- loading?: boolean;
- error?: string;
-}
-
-export interface HaztrakProfileOrg {
- id: string;
- name: string;
- rcrainfoIntegrated: boolean;
-}
-
-/** A site a user has access to in RCRAInfo and their module permissions */
-export interface RcrainfoProfileSite {
- epaSiteId: string;
- permissions: RcrainfoSitePermissions;
-}
-
-export interface HaztrakProfileSite extends HaztrakSite {
- permissions: HaztrakSitePermissions;
-}
-
-export type HaztrakModulePermissions = 'viewer' | 'editor' | 'signer';
-
-export interface HaztrakSitePermissions {
- eManifest: HaztrakModulePermissions;
-}
-
-export interface RcrainfoProfileState
- extends RcrainfoProfile> {}
-
-export interface RcrainfoProfile {
- user: string;
- rcraAPIID?: string;
- rcraUsername?: string;
- rcraAPIKey?: string;
- apiUser?: boolean;
- rcraSites?: T;
- phoneNumber?: string;
- isLoading?: boolean;
- error?: string;
-}
-
-export interface RcrainfoSitePermissions {
- siteManagement: boolean;
- annualReport: string;
- biennialReport: string;
- eManifest: string;
- WIETS: string;
- myRCRAid: string;
-}
-
-/**initial, state of a user's RcraProfile.*/
-const initialState: ProfileSlice = {
- user: undefined,
- rcrainfoProfile: undefined,
- sites: undefined,
- loading: false,
- error: undefined,
-};
-
-/**Retrieves a user's profile from the server.*/
-export const getHaztrakProfile = createAsyncThunk('profile/getHaztrakProfile', async () => {
- const { data } = await UserApi.getUserProfile();
- const sites = data.sites.reduce((obj, site) => {
- return {
- ...obj,
- [site.site.handler.epaSiteId]: {
- ...site.site,
- permissions: { eManifest: site.eManifest },
- },
- };
- }, {});
- return {
- user: data.user,
- org: data.org,
- sites: sites,
- } as ProfileSlice;
-});
-
-/**Retrieves a user's RcrainfoProfile, if it exists, from the server.*/
-export const getRcraProfile = createAsyncThunk(
- 'profile/getRcrainfoProfile',
- async (arg, thunkAPI) => {
- const state = thunkAPI.getState() as RootState;
- const username = state.auth.user?.username;
- if (!username) {
- throw new Error('User is not logged in');
- }
- const { data } = await UserApi.getRcrainfoProfile(username);
- const { rcraSites, ...rest } = data;
- return {
- rcrainfoProfile: {
- ...rest,
- rcraSites: rcraSites?.reduce((obj, site) => {
- return {
- ...obj,
- [site.epaSiteId]: { epaSiteId: site.epaSiteId, permissions: site.permissions },
- };
- }, {}),
- },
- } as ProfileSlice;
- }
-);
-
-const profileSlice = createSlice({
- name: 'profile',
- initialState,
- reducers: {
- updateProfile: (state: ProfileSlice, action: PayloadAction) => {
- return {
- ...state,
- ...action.payload,
- };
- },
- },
- extraReducers: (builder) => {
- builder
- .addCase(getHaztrakProfile.pending, (state) => {
- return {
- ...state,
- loading: true,
- error: undefined,
- };
- })
- .addCase(getHaztrakProfile.fulfilled, (state, action) => {
- return {
- ...state,
- ...action.payload,
- loading: false,
- error: undefined,
- };
- })
- .addCase(getHaztrakProfile.rejected, (state) => {
- state.loading = false;
- state.error = 'error';
- return state;
- })
- .addCase(getRcraProfile.pending, (state) => {
- return {
- ...state,
- loading: true,
- error: undefined,
- };
- })
- .addCase(getRcraProfile.fulfilled, (state, action) => {
- return {
- ...state,
- ...action.payload,
- loading: false,
- error: undefined,
- };
- })
- .addCase(getRcraProfile.rejected, (state, action) => {
- state.loading = false;
- // @ts-ignore
- state.error = action.payload.error;
- return state;
- });
- },
-});
-
-/** Retrieve a Haztrak site from the users Profile by the site's EPA ID number */
-export const siteByEpaIdSelector = (
- epaId: string | undefined
-): ((state: RootState) => RcraSite | undefined) =>
- createSelector(
- (state: { profile: ProfileSlice }) => state.profile.sites,
- (sites: Record | undefined) => {
- if (!sites) return undefined;
-
- const siteId = Object.keys(sites).find((key) => sites[key]?.handler.epaSiteId === epaId);
- if (!siteId) return undefined;
-
- const sitePermissions = sites[siteId];
- if (!sitePermissions) return undefined;
-
- return sitePermissions.handler;
- }
- );
-
-/** Get all sites a user has access to their Haztrak Profile*/
-export const selectHaztrakSites = createSelector(
- (state: { profile: ProfileSlice }) => state.profile.sites,
- (sites: Record | undefined) => {
- if (!sites) return undefined;
-
- return Object.values(sites).map((site) => site);
- }
-);
-
-/** Get all sites a user has access to their Haztrak Profile*/
-export const selectHaztrakSiteEpaIds = createSelector(
- (state: { profile: ProfileSlice }) => state.profile.sites,
- (sites: Record | undefined) => {
- if (!sites) return [];
- return Object.values(sites).map((site) => site.handler.epaSiteId);
- }
-);
-
-/** select all RCRAInfo sites a user has access to from their RCRAInfo Profile if they're updated it*/
-export const selectRcrainfoSites = createSelector(
- (state: { profile: ProfileSlice }) => state.profile.rcrainfoProfile?.rcraSites,
- (rcraSites: Record | undefined) => {
- if (!rcraSites) return undefined;
-
- return Object.values(rcraSites).map((site) => site);
- }
-);
-
-/** Retrieve a user's RcraProfile from the Redux store. */
-export const selectRcraProfile = createSelector(
- (state: RootState) => state,
- (state: RootState) => state.profile.rcrainfoProfile
-);
-
-/** Retrieve a user's HaztrakProfile from the Redux store. */
-export const selectHaztrakProfile = createSelector(
- (state: RootState) => state,
- (state: RootState) => state.profile
-);
-
-export default profileSlice.reducer;
-export const { updateProfile } = profileSlice.actions;
diff --git a/client/src/store/rootStore.ts b/client/src/store/rootStore.ts
index e531ba5e5..8a80e7aeb 100644
--- a/client/src/store/rootStore.ts
+++ b/client/src/store/rootStore.ts
@@ -1,14 +1,12 @@
import { combineReducers, configureStore } from '@reduxjs/toolkit';
-import userReducers, { login } from './authSlice/auth.slice';
+import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
+import authReducers from 'store/authSlice/auth.slice';
+import { haztrakApi } from 'store/htApi.slice';
import errorReducers from './errorSlice/error.slice';
-import { haztrakApi } from './haztrakApiSlice';
import notificationReducers from './notificationSlice/notification.slice';
-import profileReducers from './profileSlice/profile.slice';
-import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
const rootReducer = combineReducers({
- auth: userReducers,
- profile: profileReducers,
+ auth: authReducers,
error: errorReducers,
notifications: notificationReducers,
[haztrakApi.reducerPath]: haztrakApi.reducer,
@@ -30,4 +28,4 @@ export const useAppSelector: TypedUseSelectorHook = useSelector;
export type AppDispatch = typeof rootStore.dispatch;
export type RootState = ReturnType;
export type AppStore = ReturnType;
-export { rootStore, login, setupStore };
+export { rootStore, setupStore };
diff --git a/client/src/store/userSlice/user.slice.spec.tsx b/client/src/store/userSlice/user.slice.spec.tsx
new file mode 100644
index 000000000..40fd90705
--- /dev/null
+++ b/client/src/store/userSlice/user.slice.spec.tsx
@@ -0,0 +1,60 @@
+import '@testing-library/jest-dom';
+import { waitFor } from '@testing-library/react';
+import { setupServer } from 'msw/node';
+import { useEffect, useState } from 'react';
+import { useGetUserQuery, useUpdateUserMutation } from 'store';
+import { cleanup, renderWithProviders, screen } from 'test-utils';
+import { userApiMocks } from 'test-utils/mock';
+import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';
+
+const server = setupServer(...userApiMocks);
+afterEach(() => {
+ cleanup();
+});
+beforeAll(() => server.listen());
+afterAll(() => server.close());
+
+const UserQueryComponent = () => {
+ const [fetchCount, setFetchCount] = useState(0);
+ const { data, error, isLoading, isFetching } = useGetUserQuery();
+ const [updateUser, results] = useUpdateUserMutation();
+
+ useEffect(() => {
+ if (isFetching) setFetchCount(fetchCount + 1);
+ }, [isFetching]);
+
+ if (isLoading || isFetching) {
+ return Loading
;
+ }
+ if (error) {
+ return Error
;
+ }
+ if (data) {
+ return (
+
+
Data
+
username: {data.username}
+
email: {data.email}
+
fetchCount: {fetchCount}
+
+
+
+
+ );
+ }
+};
+
+describe('userApi', () => {
+ test('get user query', async () => {
+ renderWithProviders();
+ await waitFor(() => screen.getByText('Data'));
+ expect(screen.queryByText(/username:/i)).toBeInTheDocument();
+ });
+});
diff --git a/client/src/store/userSlice/user.slice.ts b/client/src/store/userSlice/user.slice.ts
new file mode 100644
index 000000000..3d84ebd4a
--- /dev/null
+++ b/client/src/store/userSlice/user.slice.ts
@@ -0,0 +1,171 @@
+import { HaztrakSite } from 'components/HaztrakSite';
+import { HaztrakUser } from 'store/authSlice/auth.slice';
+import { haztrakApi, TaskResponse } from 'store/htApi.slice';
+
+/**The user's RCRAInfo account data stored in the Redux store*/
+export interface ProfileSlice {
+ user: string | undefined;
+ rcrainfoProfile?: RcrainfoProfile>;
+ sites?: Record;
+ org?: HaztrakProfileOrg | null;
+ loading?: boolean;
+ error?: string;
+}
+
+export interface HaztrakProfileOrg {
+ id: string;
+ name: string;
+ rcrainfoIntegrated: boolean;
+}
+
+/** A site a user has access to in RCRAInfo and their module permissions */
+export interface RcrainfoProfileSite {
+ epaSiteId: string;
+ permissions: RcrainfoSitePermissions;
+}
+
+export interface HaztrakProfileSite extends HaztrakSite {
+ permissions: HaztrakSitePermissions;
+}
+
+export type HaztrakModulePermissions = 'viewer' | 'editor' | 'signer';
+
+export interface HaztrakSitePermissions {
+ eManifest: HaztrakModulePermissions;
+}
+
+export interface RcrainfoProfileState
+ extends RcrainfoProfile> {}
+
+export interface RcrainfoProfile {
+ user: string;
+ rcraAPIID?: string;
+ rcraUsername?: string;
+ rcraAPIKey?: string;
+ apiUser?: boolean;
+ rcraSites?: T;
+ phoneNumber?: string;
+ isLoading?: boolean;
+ error?: string;
+}
+
+export interface RcrainfoSitePermissions {
+ siteManagement: boolean;
+ annualReport: string;
+ biennialReport: string;
+ eManifest: string;
+ WIETS: string;
+ myRCRAid: string;
+}
+
+export interface LoginRequest {
+ username: string;
+ password: string;
+}
+
+export interface LoginResponse {
+ key: string;
+}
+
+interface HaztrakOrgResponse {
+ id: string;
+ name: string;
+ rcrainfoIntegrated: boolean;
+}
+
+export interface HaztrakProfileResponse {
+ user: string;
+ sites: Array<{
+ site: HaztrakSite;
+ eManifest: HaztrakModulePermissions;
+ }>;
+ org?: HaztrakOrgResponse;
+}
+
+interface RcrainfoProfileResponse extends RcrainfoProfile> {}
+
+export const userApi = haztrakApi.injectEndpoints({
+ endpoints: (build) => ({
+ // Note: build.query
+ login: build.mutation({
+ query: (data) => ({
+ url: 'login',
+ method: 'POST',
+ data: data,
+ }),
+ invalidatesTags: ['auth'],
+ }),
+ getUser: build.query({
+ query: () => ({
+ url: 'user',
+ method: 'GET',
+ }),
+ providesTags: ['user'],
+ }),
+ updateUser: build.mutation({
+ query: (data) => ({
+ url: 'user',
+ method: 'PUT',
+ data: data,
+ }),
+ invalidatesTags: ['user'],
+ }),
+ getProfile: build.query({
+ query: () => ({
+ url: 'user/profile',
+ method: 'GET',
+ }),
+ providesTags: ['profile'],
+ transformResponse: (response: HaztrakProfileResponse) => {
+ const sites = response.sites.reduce((obj, site) => {
+ return {
+ ...obj,
+ [site.site.handler.epaSiteId]: {
+ ...site.site,
+ permissions: { eManifest: site.eManifest },
+ },
+ };
+ }, {});
+ return {
+ user: response.user,
+ org: response.org,
+ sites: sites,
+ };
+ },
+ }),
+ getRcrainfoProfile: build.query({
+ query: (username) => ({
+ url: `user/rcrainfo-profile/${username}`,
+ method: 'GET',
+ }),
+ providesTags: ['rcrainfoProfile'],
+ transformResponse: (response: RcrainfoProfileResponse) => {
+ const rcraSites = response?.rcraSites;
+ return {
+ ...response,
+ rcraSites: rcraSites?.reduce((obj, site) => {
+ return {
+ ...obj,
+ [site.epaSiteId]: { epaSiteId: site.epaSiteId, permissions: site.permissions },
+ };
+ }, {}),
+ };
+ },
+ }),
+ updateRcrainfoProfile: build.mutation({
+ query: (data) => ({
+ url: `user/rcrainfo-profile/${data.username}`,
+ method: 'PUT',
+ data: data.data,
+ }),
+ invalidatesTags: ['rcrainfoProfile'],
+ }),
+ syncRcrainfoProfile: build.mutation({
+ query: () => ({
+ url: `user/rcrainfo-profile/sync`,
+ method: 'POST',
+ }),
+ invalidatesTags: ['rcrainfoProfile'],
+ }),
+ }),
+});
diff --git a/client/src/test-utils/fixtures/index.ts b/client/src/test-utils/fixtures/index.ts
index 687b20c00..3938bc145 100644
--- a/client/src/test-utils/fixtures/index.ts
+++ b/client/src/test-utils/fixtures/index.ts
@@ -9,3 +9,4 @@ export {
createMockRcrainfoPermissions,
} from './mockHandler';
export { createMockManifest } from './mockManifest';
+export { createMockHaztrakUser } from './mockUser';
diff --git a/client/src/test-utils/fixtures/mockUser.ts b/client/src/test-utils/fixtures/mockUser.ts
new file mode 100644
index 000000000..ad145e19e
--- /dev/null
+++ b/client/src/test-utils/fixtures/mockUser.ts
@@ -0,0 +1,57 @@
+import { HaztrakProfileOrg, HaztrakUser, RcrainfoProfile, RcrainfoProfileSite } from 'store';
+import { HaztrakProfileResponse } from 'store/userSlice/user.slice';
+import { createMockSite } from 'test-utils/fixtures/mockHandler';
+
+export const DEFAULT_HAZTRAK_USER: HaztrakUser = {
+ username: 'testuser1',
+ firstName: 'john',
+ lastName: 'smith',
+ email: 'test@mail.com',
+};
+
+export function createMockHaztrakUser(overWrites?: Partial): HaztrakUser {
+ return {
+ ...DEFAULT_HAZTRAK_USER,
+ ...overWrites,
+ };
+}
+
+interface RcrainfoProfileResponse extends RcrainfoProfile> {}
+
+const DEFAULT_RCRAINFO_PROFILE_RESPONSE: RcrainfoProfileResponse = {
+ user: 'testuser1',
+ rcraAPIID: 'mockRcraAPIID',
+ rcraUsername: undefined,
+ rcraSites: [],
+ phoneNumber: undefined,
+ apiUser: true,
+};
+
+export function createMockRcrainfoProfileResponse(
+ overWrites?: Partial
+): RcrainfoProfileResponse {
+ return {
+ ...DEFAULT_RCRAINFO_PROFILE_RESPONSE,
+ ...overWrites,
+ };
+}
+
+export function createMockOrg(overWrites?: Partial): HaztrakProfileOrg {
+ return {
+ name: 'mockOrg',
+ id: 'mockOrgId',
+ rcrainfoIntegrated: true,
+ ...overWrites,
+ };
+}
+
+export function createMockProfileResponse(
+ overWrites?: Partial
+): HaztrakProfileResponse {
+ return {
+ user: DEFAULT_HAZTRAK_USER.username,
+ org: createMockOrg(),
+ sites: [{ site: createMockSite(), eManifest: 'signer' }],
+ ...overWrites,
+ };
+}
diff --git a/client/src/test-utils/index.tsx b/client/src/test-utils/index.ts
similarity index 100%
rename from client/src/test-utils/index.tsx
rename to client/src/test-utils/index.ts
diff --git a/client/src/test-utils/mock/browser.js b/client/src/test-utils/mock/browser.js
deleted file mode 100644
index b3e2bd2f8..000000000
--- a/client/src/test-utils/mock/browser.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// src/test/browser.js
-
-// This configures a Service Worker with the given request handlers.
-export const worker = setupWorker(...handlers);
diff --git a/client/src/test-utils/mock/handlers.ts b/client/src/test-utils/mock/htApiMocks.ts
similarity index 58%
rename from client/src/test-utils/mock/handlers.ts
rename to client/src/test-utils/mock/htApiMocks.ts
index ae638dc95..a17dcfe9b 100644
--- a/client/src/test-utils/mock/handlers.ts
+++ b/client/src/test-utils/mock/htApiMocks.ts
@@ -4,39 +4,9 @@ import { createMockHandler, createMockManifest, createMockSite } from '../fixtur
export const API_BASE_URL = import.meta.env.VITE_HT_API_URL;
const mockMTN = createMockManifest().manifestTrackingNumber;
const mockEpaId = createMockHandler().epaSiteId;
-const mockUsername = 'testuser1';
const mockSites = [createMockSite(), createMockSite()];
-export const handlers = [
- /** Login endpoint*/
- http.post(`${API_BASE_URL}/api/user/login`, (info) => {
- // Persist user's authentication in the session
- sessionStorage.setItem('token', 'this_is_a_fake_token');
- sessionStorage.setItem('user', mockUsername);
-
- // Mock response from haztrak API
- return HttpResponse.json(
- {
- token: 'fake_token',
- user: mockUsername,
- },
- { status: 200 }
- );
- }),
- /** User RcraProfile data*/
- http.get(`${API_BASE_URL}/api/rcra/profile/${mockUsername}`, (info) => {
- return HttpResponse.json(
- {
- user: mockUsername,
- rcraAPIID: 'mockRcraAPIID',
- rcraUsername: undefined,
- epaSites: [],
- phoneNumber: undefined,
- apiUser: true,
- },
- { status: 200 }
- );
- }),
+export const htApiMocks = [
/** List user sites*/
http.get(`${API_BASE_URL}/api/site`, (info) => {
return HttpResponse.json(mockSites, { status: 200 });
diff --git a/client/src/test-utils/mock/index.ts b/client/src/test-utils/mock/index.ts
new file mode 100644
index 000000000..720f3980a
--- /dev/null
+++ b/client/src/test-utils/mock/index.ts
@@ -0,0 +1,2 @@
+export { userApiMocks } from './userApiMocks';
+export { htApiMocks } from './htApiMocks';
diff --git a/client/src/test-utils/mock/userApiMocks.ts b/client/src/test-utils/mock/userApiMocks.ts
new file mode 100644
index 000000000..eaf852d3e
--- /dev/null
+++ b/client/src/test-utils/mock/userApiMocks.ts
@@ -0,0 +1,48 @@
+import { http, HttpResponse } from 'msw';
+import { HaztrakUser } from 'store/authSlice/auth.slice';
+import { LoginResponse } from 'store/userSlice/user.slice';
+import { createMockHaztrakUser } from 'test-utils/fixtures';
+import {
+ createMockProfileResponse,
+ createMockRcrainfoProfileResponse,
+} from 'test-utils/fixtures/mockUser';
+
+/** mock Rest API*/
+const API_BASE_URL = import.meta.env.VITE_HT_API_URL;
+export const userApiMocks = [
+ /** GET User */
+ http.get(`${API_BASE_URL}/api/user`, () => {
+ return HttpResponse.json({ ...createMockHaztrakUser() }, { status: 200 });
+ }),
+ /** Update User */
+ http.put(`${API_BASE_URL}/api/user`, (info) => {
+ const user: HaztrakUser = { ...createMockHaztrakUser() };
+ return HttpResponse.json({ ...user, ...info.request.body }, { status: 200 });
+ }),
+ /** GET Profile */
+ http.get(`${API_BASE_URL}/api/user/profile`, () => {
+ return HttpResponse.json({ ...createMockProfileResponse() }, { status: 200 });
+ }),
+ /** Login */
+ http.post(`${API_BASE_URL}/api/user/login`, (info) => {
+ const body: LoginResponse = { key: 'mockToken' };
+ return HttpResponse.json(
+ {
+ ...body,
+ },
+ { status: 200 }
+ );
+ }),
+ /** GET RCRAInfo profile */
+ http.get(`${API_BASE_URL}/api/user/rcrainfo-profile/:username`, (info) => {
+ const { username } = info.params;
+ // @ts-ignore
+ const rcrainfoProfile = createMockRcrainfoProfileResponse({ user: username ?? '' });
+ return HttpResponse.json(
+ {
+ ...rcrainfoProfile,
+ },
+ { status: 200 }
+ );
+ }),
+];
diff --git a/server/.gitignore b/server/.gitignore
index c6203673c..57b196ba5 100644
--- a/server/.gitignore
+++ b/server/.gitignore
@@ -3,3 +3,4 @@ test_db
static/
.mypy_cache
test_db-journal
+**/*coverage.json
diff --git a/server/Dockerfile b/server/Dockerfile
index 46a9f7ffa..09762873c 100644
--- a/server/Dockerfile
+++ b/server/Dockerfile
@@ -1,9 +1,10 @@
# Builder
-FROM python:3.12.0-alpine3.18 as builder
+FROM python:3.12.1-alpine3.18 as builder
LABEL maintainer="graham.david@epa.gov"
ENV APP_DIRECTORY=/app/
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
+RUN apk add libffi-dev gcc libc-dev
WORKDIR $APP_DIRECTORY
COPY requirements.txt ./
RUN mkdir $APP_DIRECTORY/static
diff --git a/server/apps/conftest.py b/server/apps/conftest.py
index 92de5d7dc..ff01160b9 100644
--- a/server/apps/conftest.py
+++ b/server/apps/conftest.py
@@ -13,15 +13,15 @@
from faker.providers import BaseProvider
from rest_framework.test import APIClient
-from apps.core.models import HaztrakProfile, HaztrakUser, RcraProfile
-from apps.sites.models import (
+from apps.core.models import HaztrakProfile, HaztrakUser, RcrainfoProfile
+from apps.site.models import (
Address,
Contact,
HaztrakSite,
RcraPhone,
RcraSite,
)
-from apps.sites.models.site_models import HaztrakOrg, SitePermissions
+from apps.site.models.site_models import HaztrakOrg, SitePermissions
from apps.trak.models import ManifestPhone
@@ -80,14 +80,14 @@ def create_user(
@pytest.fixture
def rcra_profile_factory(db, user_factory, faker: Faker):
- """Abstract factory for Haztrak RcraProfile model"""
+ """Abstract factory for Haztrak RcrainfoProfile model"""
def create_profile(
rcra_api_id: Optional[str] = str(faker.uuid4()),
rcra_api_key: Optional[str] = faker.pystr(min_chars=15),
rcra_username: Optional[str] = faker.pystr(min_chars=12),
- ) -> RcraProfile:
- return RcraProfile.objects.create(
+ ) -> RcrainfoProfile:
+ return RcrainfoProfile.objects.create(
rcra_api_id=rcra_api_id,
rcra_api_key=rcra_api_key,
rcra_username=rcra_username,
@@ -98,11 +98,11 @@ def create_profile(
@pytest.fixture
def haztrak_profile_factory(db, user_factory, rcra_profile_factory, haztrak_org_factory):
- """Abstract factory for Haztrak RcraProfile model"""
+ """Abstract factory for Haztrak RcrainfoProfile model"""
def create_profile(
user: Optional[User] = None,
- rcrainfo_profile: Optional[RcraProfile] = rcra_profile_factory(),
+ rcrainfo_profile: Optional[RcrainfoProfile] = rcra_profile_factory(),
org: Optional[HaztrakOrg] = haztrak_org_factory(),
) -> HaztrakProfile:
return HaztrakProfile.objects.create(
@@ -329,7 +329,7 @@ def user_with_org_factory(
def create_fixtures(
user: Optional[User] = None,
org: Optional[HaztrakOrg] = None,
- admin_rcrainfo_profile: Optional[RcraProfile] = None,
+ admin_rcrainfo_profile: Optional[RcrainfoProfile] = None,
is_rcrainfo_enabled: Optional[bool] = True,
):
if is_rcrainfo_enabled:
diff --git a/server/apps/core/admin.py b/server/apps/core/admin.py
index ed604ba09..a9dd05d7b 100644
--- a/server/apps/core/admin.py
+++ b/server/apps/core/admin.py
@@ -3,8 +3,8 @@
from django.urls import reverse
from django.utils.html import format_html, urlencode
-from ..sites.models import RcraSitePermissions, SitePermissions
-from .models import HaztrakProfile, HaztrakUser, RcraProfile
+from ..site.models import RcraSitePermissions, SitePermissions
+from .models import HaztrakProfile, HaztrakUser, RcrainfoProfile
class HiddenListView(admin.ModelAdmin):
@@ -25,7 +25,7 @@ class HaztrakUserAdmin(UserAdmin):
@admin.display(description="Profile")
def related_profile(self, user: HaztrakUser) -> str:
url = (
- reverse("admin:core_haztrakprofile_changelist") + "?" + urlencode({"user": str(user)}) # noqa E501
+ reverse("admin:core_haztrakprofile_changelist") + "?" + urlencode({"user": str(user)}) # noqa E501
)
return format_html("{}", url, user.haztrak_profile)
@@ -70,7 +70,7 @@ def number_of_sites(profile: HaztrakProfile) -> str:
return str(profile.site_permissions.all().count())
-@admin.register(RcraProfile)
+@admin.register(RcrainfoProfile)
class RcraProfileAdmin(admin.ModelAdmin):
list_display = ["__str__", "related_user", "rcra_username", "api_user"]
search_fields = ["haztrak_profile__user__username", "rcra_username"]
@@ -84,7 +84,7 @@ def related_user(self, user):
url = reverse("admin:core_haztrakuser_changelist") + "?" + urlencode({"q": str(user.id)})
return format_html("{}", url, user)
- def api_user(self, profile: RcraProfile) -> bool:
+ def api_user(self, profile: RcrainfoProfile) -> bool:
return profile.has_rcrainfo_api_id_key
api_user.boolean = True
diff --git a/server/apps/core/exceptions.py b/server/apps/core/exceptions.py
index f669eda8e..e9cf21679 100644
--- a/server/apps/core/exceptions.py
+++ b/server/apps/core/exceptions.py
@@ -6,7 +6,7 @@
from rest_framework.serializers import as_serializer_error
from rest_framework.views import exception_handler
-from apps.sites.services.site_services import HaztrakSiteServiceError
+from apps.site.services.site_services import HaztrakSiteServiceError
class InternalServer500(APIException):
diff --git a/server/apps/core/migrations/0001_initial.py b/server/apps/core/migrations/0001_initial.py
index b7af7d12b..78da9c7db 100644
--- a/server/apps/core/migrations/0001_initial.py
+++ b/server/apps/core/migrations/0001_initial.py
@@ -1,67 +1,129 @@
# Generated by Django 4.2.7 on 2023-11-20 16:47
+import uuid
+
import django.contrib.auth.models
import django.contrib.auth.validators
-from django.db import migrations, models
import django.utils.timezone
-import uuid
+from django.db import migrations, models
class Migration(migrations.Migration):
-
initial = True
- dependencies = [
- ]
+ dependencies = []
operations = [
migrations.CreateModel(
- name='HaztrakUser',
+ name="HaztrakUser",
fields=[
- ('password', models.CharField(max_length=128, verbose_name='password')),
- ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
- ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
- ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
- ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
- ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
- ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
- ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
- ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
- ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
- ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ ("password", models.CharField(max_length=128, verbose_name="password")),
+ (
+ "last_login",
+ models.DateTimeField(blank=True, null=True, verbose_name="last login"),
+ ),
+ (
+ "is_superuser",
+ models.BooleanField(
+ default=False,
+ help_text="Designates that this user has all permissions without explicitly assigning them.",
+ verbose_name="superuser status",
+ ),
+ ),
+ (
+ "username",
+ models.CharField(
+ error_messages={"unique": "A user with that username already exists."},
+ help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
+ max_length=150,
+ unique=True,
+ validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
+ verbose_name="username",
+ ),
+ ),
+ (
+ "first_name",
+ models.CharField(blank=True, max_length=150, verbose_name="first name"),
+ ),
+ (
+ "last_name",
+ models.CharField(blank=True, max_length=150, verbose_name="last name"),
+ ),
+ (
+ "email",
+ models.EmailField(blank=True, max_length=254, verbose_name="email address"),
+ ),
+ (
+ "is_staff",
+ models.BooleanField(
+ default=False,
+ help_text="Designates whether the user can log into this admin site.",
+ verbose_name="staff status",
+ ),
+ ),
+ (
+ "is_active",
+ models.BooleanField(
+ default=True,
+ help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
+ verbose_name="active",
+ ),
+ ),
+ (
+ "date_joined",
+ models.DateTimeField(
+ default=django.utils.timezone.now, verbose_name="date joined"
+ ),
+ ),
+ (
+ "id",
+ models.UUIDField(
+ default=uuid.uuid4, editable=False, primary_key=True, serialize=False
+ ),
+ ),
],
options={
- 'verbose_name': 'User',
- 'verbose_name_plural': 'Users',
- 'ordering': ['username'],
+ "verbose_name": "User",
+ "verbose_name_plural": "Users",
+ "ordering": ["username"],
},
managers=[
- ('objects', django.contrib.auth.models.UserManager()),
+ ("objects", django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
- name='HaztrakProfile',
+ name="HaztrakProfile",
fields=[
- ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ (
+ "id",
+ models.UUIDField(
+ default=uuid.uuid4, editable=False, primary_key=True, serialize=False
+ ),
+ ),
],
options={
- 'verbose_name': 'Haztrak Profile',
- 'ordering': ['user__username'],
- 'default_related_name': 'haztrak_profile',
+ "verbose_name": "Haztrak Profile",
+ "ordering": ["user__username"],
+ "default_related_name": "haztrak_profile",
},
),
migrations.CreateModel(
- name='RcraProfile',
+ name="RcrainfoProfile",
fields=[
- ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
- ('rcra_api_key', models.CharField(blank=True, max_length=128, null=True)),
- ('rcra_api_id', models.CharField(blank=True, max_length=128, null=True)),
- ('rcra_username', models.CharField(blank=True, max_length=128, null=True)),
- ('phone_number', models.CharField(blank=True, max_length=15, null=True)),
- ('email', models.EmailField(max_length=254)),
+ (
+ "id",
+ models.UUIDField(
+ default=uuid.uuid4, editable=False, primary_key=True, serialize=False
+ ),
+ ),
+ ("rcra_api_key", models.CharField(blank=True, max_length=128, null=True)),
+ ("rcra_api_id", models.CharField(blank=True, max_length=128, null=True)),
+ ("rcra_username", models.CharField(blank=True, max_length=128, null=True)),
+ ("phone_number", models.CharField(blank=True, max_length=15, null=True)),
+ ("email", models.EmailField(max_length=254)),
],
options={
- 'ordering': ['rcra_username'],
+ "ordering": ["rcra_username"],
},
),
]
diff --git a/server/apps/core/migrations/0002_initial.py b/server/apps/core/migrations/0002_initial.py
index 75eedc2d8..a320f9249 100644
--- a/server/apps/core/migrations/0002_initial.py
+++ b/server/apps/core/migrations/0002_initial.py
@@ -1,44 +1,73 @@
# Generated by Django 4.2.7 on 2023-11-20 16:47
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
-
initial = True
dependencies = [
- ('sites', '0001_initial'),
- ('core', '0001_initial'),
- ('auth', '0012_alter_user_first_name_max_length'),
+ ("site", "0001_initial"),
+ ("core", "0001_initial"),
+ ("auth", "0012_alter_user_first_name_max_length"),
]
operations = [
migrations.AddField(
- model_name='haztrakprofile',
- name='org',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='haztrak_profiles', to='sites.haztrakorg'),
+ model_name="haztrakprofile",
+ name="org",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="haztrak_profiles",
+ to="site.haztrakorg",
+ ),
),
migrations.AddField(
- model_name='haztrakprofile',
- name='rcrainfo_profile',
- field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='haztrak_profile', to='core.rcraprofile'),
+ model_name="haztrakprofile",
+ name="rcrainfo_profile",
+ field=models.OneToOneField(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="haztrak_profile",
+ to="core.rcrainfoprofile",
+ ),
),
migrations.AddField(
- model_name='haztrakprofile',
- name='user',
- field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='haztrak_profile', to=settings.AUTH_USER_MODEL),
+ model_name="haztrakprofile",
+ name="user",
+ field=models.OneToOneField(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="haztrak_profile",
+ to=settings.AUTH_USER_MODEL,
+ ),
),
migrations.AddField(
- model_name='haztrakuser',
- name='groups',
- field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups'),
+ model_name="haztrakuser",
+ name="groups",
+ field=models.ManyToManyField(
+ blank=True,
+ help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
+ related_name="user_set",
+ related_query_name="user",
+ to="auth.group",
+ verbose_name="groups",
+ ),
),
migrations.AddField(
- model_name='haztrakuser',
- name='user_permissions',
- field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions'),
+ model_name="haztrakuser",
+ name="user_permissions",
+ field=models.ManyToManyField(
+ blank=True,
+ help_text="Specific permissions for this user.",
+ related_name="user_set",
+ related_query_name="user",
+ to="auth.permission",
+ verbose_name="user permissions",
+ ),
),
]
diff --git a/server/apps/core/models.py b/server/apps/core/models.py
index bc40b8109..2f4502df3 100644
--- a/server/apps/core/models.py
+++ b/server/apps/core/models.py
@@ -38,14 +38,14 @@ class Meta:
related_name="haztrak_profile",
)
rcrainfo_profile = models.OneToOneField(
- "RcraProfile",
+ "core.RcrainfoProfile",
on_delete=models.SET_NULL,
related_name="haztrak_profile",
null=True,
blank=True,
)
org = models.ForeignKey(
- "sites.HaztrakOrg",
+ "site.HaztrakOrg",
on_delete=models.SET_NULL,
related_name="haztrak_profiles",
null=True,
@@ -61,9 +61,9 @@ def __str__(self):
return f"{self.user.username}"
-class RcraProfile(models.Model):
+class RcrainfoProfile(models.Model):
"""
- Contains a user's RcraProfile information, such as username, and API credentials.
+ Contains a user's RcrainfoProfile information, such as username, and API credentials.
Has a one-to-one relationship with the User model.
"""
diff --git a/server/apps/core/serializers.py b/server/apps/core/serializers.py
index 91da83b89..34d9f9f53 100644
--- a/server/apps/core/serializers.py
+++ b/server/apps/core/serializers.py
@@ -4,9 +4,9 @@
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
-from apps.core.models import HaztrakProfile, HaztrakUser, RcraProfile
-from apps.sites.serializers import HaztrakOrgSerializer, RcraSitePermissionSerializer
-from apps.sites.serializers.profile_serializer import SitePermissionSerializer
+from apps.core.models import HaztrakProfile, HaztrakUser, RcrainfoProfile
+from apps.site.serializers import HaztrakOrgSerializer, RcraSitePermissionSerializer
+from apps.site.serializers.profile_serializer import SitePermissionSerializer
class HaztrakUserSerializer(ModelSerializer):
@@ -14,6 +14,7 @@ class HaztrakUserSerializer(ModelSerializer):
Model serializer for marshalling/unmarshalling a user's HaztrakUser
"""
+ id = serializers.CharField(source="pk")
username = serializers.CharField(
required=False,
)
@@ -29,6 +30,7 @@ class HaztrakUserSerializer(ModelSerializer):
class Meta:
model = HaztrakUser
fields = [
+ "id",
"username",
"firstName",
"lastName",
@@ -57,8 +59,8 @@ class Meta:
]
-class RcraProfileSerializer(ModelSerializer):
- """Model serializer for marshalling/unmarshalling a user's RcraProfile"""
+class RcrainfoProfileSerializer(ModelSerializer):
+ """Model serializer for marshalling/unmarshalling a user's RcrainfoProfile"""
user = serializers.StringRelatedField(
source="haztrak_profile",
@@ -75,11 +77,15 @@ class RcraProfileSerializer(ModelSerializer):
rcraAPIID = serializers.CharField(
source="rcra_api_id",
required=False,
+ allow_null=True,
+ allow_blank=True,
)
rcraAPIKey = serializers.CharField(
source="rcra_api_key",
required=False,
write_only=True,
+ allow_blank=True,
+ allow_null=True,
)
rcraUsername = serializers.CharField(
source="rcra_username",
@@ -92,7 +98,7 @@ class RcraProfileSerializer(ModelSerializer):
)
class Meta:
- model = RcraProfile
+ model = RcrainfoProfile
fields = [
"user",
"rcraAPIID",
diff --git a/server/apps/core/services/profile_service.py b/server/apps/core/services/profile_service.py
index f74c88951..a6eff5e43 100644
--- a/server/apps/core/services/profile_service.py
+++ b/server/apps/core/services/profile_service.py
@@ -1,4 +1,4 @@
-"""business logic related to a user's Haztrak profile (note: not their RcraProfile)"""
+"""business logic related to a user's Haztrak profile (note: not their RcrainfoProfile)"""
from django.db import transaction
from apps.core.models import HaztrakProfile, HaztrakUser
diff --git a/server/apps/core/services/rcrainfo_service.py b/server/apps/core/services/rcrainfo_service.py
index 92d90ca30..393e9bcc9 100644
--- a/server/apps/core/services/rcrainfo_service.py
+++ b/server/apps/core/services/rcrainfo_service.py
@@ -5,8 +5,8 @@
from django.db import IntegrityError
from emanifest import RcrainfoClient, RcrainfoResponse # type: ignore
-from apps.core.models import RcraProfile # type: ignore
-from apps.sites.models.site_models import HaztrakOrg
+from apps.core.models import RcrainfoProfile # type: ignore
+from apps.site.models.site_models import HaztrakOrg
from apps.trak.models import WasteCode # type: ignore
logger = logging.getLogger(__name__)
@@ -23,13 +23,13 @@ class RcrainfoService(RcrainfoClient):
def __init__(
self,
*,
- rcra_profile: Optional[RcraProfile] = None,
+ rcra_profile: Optional[RcrainfoProfile] = None,
api_id: Optional[str] = None,
api_key: Optional[str] = None,
rcrainfo_env: Optional[Literal["preprod"] | Literal["prod"]] = None,
**kwargs,
):
- self.profile: RcraProfile | None = rcra_profile
+ self.profile: RcrainfoProfile | None = rcra_profile
self.api_id: str | None = api_id
self.api_key: str | None = api_key
self.rcrainfo_env: str = rcrainfo_env or "preprod"
@@ -67,7 +67,7 @@ def get_user_rcrainfo_profile(
"""
Retrieve a user's site permissions from RCRAInfo, It expects the
haztrak user to have their unique RCRAInfo user and API credentials in their
- RcraProfile
+ RcrainfoProfile
"""
return self.search_users(userId=rcrainfo_username)
diff --git a/server/apps/core/tests/conftest.py b/server/apps/core/tests/conftest.py
index 17c2c236a..b05c34202 100644
--- a/server/apps/core/tests/conftest.py
+++ b/server/apps/core/tests/conftest.py
@@ -4,7 +4,7 @@
import pytest
from faker import Faker
-from apps.sites.models import RcraSiteType
+from apps.site.models import RcraSiteType
@pytest.fixture
diff --git a/server/apps/core/tests/test_models.py b/server/apps/core/tests/test_models.py
index d71df4997..4d15a1b37 100644
--- a/server/apps/core/tests/test_models.py
+++ b/server/apps/core/tests/test_models.py
@@ -1,16 +1,16 @@
import pytest
-from apps.core.models import HaztrakProfile, RcraProfile
+from apps.core.models import HaztrakProfile, RcrainfoProfile
@pytest.mark.django_db
-class TestRcraProfileModel:
- """Test related to the RcraProfile model and its API"""
+class TestRcrainfoProfileModel:
+ """Test related to the RcrainfoProfile model and its API"""
def test_rcra_profile_factory(self, rcra_profile_factory):
"""simply check the model saves given our factory's defaults"""
rcra_profile = rcra_profile_factory()
- assert isinstance(rcra_profile, RcraProfile)
+ assert isinstance(rcra_profile, RcrainfoProfile)
@pytest.mark.parametrize("rcra_api_id", ["id", None])
@pytest.mark.parametrize("rcra_api_key", ["key", None])
diff --git a/server/apps/core/tests/test_rcra_profile_views.py b/server/apps/core/tests/test_rcrainfo_profile_views.py
similarity index 81%
rename from server/apps/core/tests/test_rcra_profile_views.py
rename to server/apps/core/tests/test_rcrainfo_profile_views.py
index 59da1fb6f..a3d3d3d95 100644
--- a/server/apps/core/tests/test_rcra_profile_views.py
+++ b/server/apps/core/tests/test_rcrainfo_profile_views.py
@@ -1,15 +1,15 @@
from rest_framework import status
from rest_framework.test import APIRequestFactory, force_authenticate
-from apps.core.views import RcraProfileView # type: ignore
+from apps.core.views import RcrainfoProfileView # type: ignore
-class TestRcraProfileView:
+class TestRcrainfoProfileView:
"""
- Tests the for the endpoints related to the user's RcraProfile
+ Tests the for the endpoints related to the user's RcrainfoProfile
"""
- URL = "/api/"
+ URL = "/api/user/"
id_field = "rcraAPIID"
key_field = "rcraAPIKey"
username_field = "rcraUsername"
@@ -26,7 +26,7 @@ def test_returns_a_user_profile(
rcra_profile = rcra_profile_factory()
haztrak_profile_factory(user=user, rcrainfo_profile=rcra_profile)
# Act
- response = client.get(f"{self.URL}rcra/profile/{user.username}")
+ response = client.get(f"{self.URL}rcrainfo-profile/{user.username}")
# Assert
assert response.headers["Content-Type"] == "application/json"
assert response.status_code == status.HTTP_200_OK
@@ -40,7 +40,7 @@ def test_rcra_profile_updates(
haztrak_profile_factory(user=user, rcrainfo_profile=rcra_profile)
factory = APIRequestFactory()
request = factory.put(
- f"{self.URL}rcra/profile/{user.username}",
+ f"{self.URL}rcrainfo-profile/{user.username}",
{
self.id_field: rcra_profile.rcra_api_id,
self.username_field: user.username,
@@ -50,7 +50,7 @@ def test_rcra_profile_updates(
)
force_authenticate(request, user)
# Act
- response = RcraProfileView.as_view()(request, username=user.username)
+ response = RcrainfoProfileView.as_view()(request, username=user.username)
assert response.data[self.id_field] == rcra_profile.rcra_api_id
assert response.data[self.username_field] == user.username
@@ -63,7 +63,7 @@ def test_update_does_not_return_api_key(
profile = haztrak_profile_factory(user=user, rcrainfo_profile=rcra_profile)
factory = APIRequestFactory()
request = factory.put(
- f"{self.URL}rcra/profile/{user.username}",
+ f"{self.URL}rcrainfo-profile/{user.username}",
{
self.id_field: rcra_profile.rcra_api_id,
self.username_field: user.username,
@@ -73,7 +73,7 @@ def test_update_does_not_return_api_key(
)
force_authenticate(request, user)
# Act
- response = RcraProfileView.as_view()(request, username=profile.user.username)
+ response = RcrainfoProfileView.as_view()(request, username=profile.user.username)
# Assert
assert self.key_field not in response.data
assert self.id_field in response.data
diff --git a/server/apps/core/tests/test_rcrainfo_service.py b/server/apps/core/tests/test_rcrainfo_service.py
index df8958114..1e97ca7ad 100644
--- a/server/apps/core/tests/test_rcrainfo_service.py
+++ b/server/apps/core/tests/test_rcrainfo_service.py
@@ -1,7 +1,6 @@
-from datetime import datetime, timezone
+from datetime import UTC, datetime
import emanifest
-import pytest
from responses import matchers
from rest_framework import status
@@ -72,7 +71,7 @@ class TestQuickerSign:
printed_name = "David Graham"
site_id = "VATESTGEN001"
site_type = "Generator"
- sign_date = datetime.utcnow().replace(tzinfo=timezone.utc)
+ sign_date = datetime.now(UTC)
def test_maps_keywords(
self,
diff --git a/server/apps/core/urls.py b/server/apps/core/urls.py
index fb24210d6..c5932c574 100644
--- a/server/apps/core/urls.py
+++ b/server/apps/core/urls.py
@@ -1,29 +1,28 @@
+from dj_rest_auth.views import LoginView, LogoutView, UserDetailsView
from django.urls import include, path
from .views import ( # type: ignore
HaztrakProfileView,
- HaztrakUserView,
LaunchExampleTaskView,
- Login,
- RcraProfileView,
- SyncRcraProfileView,
+ RcrainfoProfileView,
+ SyncRcrainfoProfileView,
TaskStatusView,
)
urlpatterns = [
- # Rcra Profile
+ path("task/example", LaunchExampleTaskView.as_view()),
+ path("task/", TaskStatusView.as_view()),
path(
- "rcra/",
+ "user",
include(
[
- path("profile/sync", SyncRcraProfileView.as_view()),
- path("profile/", RcraProfileView.as_view()),
+ path("", UserDetailsView.as_view(), name="rest_user_details"),
+ path("/login", LoginView.as_view(), name="rest_login"),
+ path("/logout", LogoutView.as_view(), name="rest_logout"),
+ path("/profile", HaztrakProfileView.as_view()),
+ path("/rcrainfo-profile/sync", SyncRcrainfoProfileView.as_view()),
+ path("/rcrainfo-profile/", RcrainfoProfileView.as_view()),
]
),
),
- path("profile", HaztrakProfileView.as_view()),
- path("user", HaztrakUserView.as_view()),
- path("user/login", Login.as_view()),
- path("task/example", LaunchExampleTaskView.as_view()),
- path("task/", TaskStatusView.as_view()),
]
diff --git a/server/apps/core/views/__init__.py b/server/apps/core/views/__init__.py
index 10e446c99..b7da1e2d0 100644
--- a/server/apps/core/views/__init__.py
+++ b/server/apps/core/views/__init__.py
@@ -1,8 +1,6 @@
-from .auth_view import Login
from .profile_views import (
HaztrakProfileView,
- HaztrakUserView,
- RcraProfileView,
- SyncRcraProfileView,
+ RcrainfoProfileView,
+ SyncRcrainfoProfileView,
)
from .task_views import LaunchExampleTaskView, TaskStatusView
diff --git a/server/apps/core/views/auth_view.py b/server/apps/core/views/auth_view.py
deleted file mode 100644
index bb9dccb43..000000000
--- a/server/apps/core/views/auth_view.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from rest_framework.authtoken.models import Token
-from rest_framework.authtoken.views import ObtainAuthToken
-from rest_framework.response import Response
-
-
-class Login(ObtainAuthToken):
- def post(self, request, *args, **kwargs):
- serializer = self.serializer_class(data=request.data, context={"request": request})
- serializer.is_valid(raise_exception=True)
- user = serializer.validated_data["user"]
- token, created = Token.objects.get_or_create(user=user)
- return Response({"token": token.key, "user": str(user)})
diff --git a/server/apps/core/views/profile_views.py b/server/apps/core/views/profile_views.py
index 1d8350675..ef1768297 100644
--- a/server/apps/core/views/profile_views.py
+++ b/server/apps/core/views/profile_views.py
@@ -5,23 +5,12 @@
from rest_framework.request import Request
from rest_framework.response import Response
-from apps.core.models import HaztrakProfile, HaztrakUser, RcraProfile
+from apps.core.models import HaztrakProfile, RcrainfoProfile
from apps.core.serializers import (
HaztrakProfileSerializer,
- HaztrakUserSerializer,
- RcraProfileSerializer,
+ RcrainfoProfileSerializer,
)
-from apps.sites.tasks import sync_user_rcrainfo_sites
-
-
-class HaztrakUserView(RetrieveUpdateAPIView):
- """Retrieve the current user's base information"""
-
- queryset = HaztrakUser.objects.all()
- serializer_class = HaztrakUserSerializer
-
- def get_object(self):
- return self.request.user
+from apps.site.tasks import sync_user_rcrainfo_sites
class HaztrakProfileView(RetrieveAPIView):
@@ -35,21 +24,21 @@ def get_object(self):
return HaztrakProfile.objects.get(user=self.request.user)
-class RcraProfileView(RetrieveUpdateAPIView):
+class RcrainfoProfileView(RetrieveUpdateAPIView):
"""
- Responsible for Create/Update operations related to the user RcraProfile,
+ Responsible for Create/Update operations related to the user RcrainfoProfile,
which maintains a user's RCRAInfo profile data. This info is necessary for
actions that interface with RCRAInfo.
"""
- queryset = RcraProfile.objects.all()
- serializer_class = RcraProfileSerializer
+ queryset = RcrainfoProfile.objects.all()
+ serializer_class = RcrainfoProfileSerializer
response = Response
lookup_field = "haztrak_profile__user__username"
lookup_url_kwarg = "username"
-class SyncRcraProfileView(GenericAPIView):
+class SyncRcrainfoProfileView(GenericAPIView):
"""
This endpoint launches a task to sync the logged-in user's RCRAInfo profile
with their haztrak (Rcra)profile.
@@ -58,11 +47,11 @@ class SyncRcraProfileView(GenericAPIView):
queryset = None
response = Response
- def get(self, request: Request) -> Response:
+ def post(self, request: Request) -> Response:
try:
task: CeleryTask = sync_user_rcrainfo_sites.delay(str(self.request.user))
return self.response({"taskId": task.id})
- except RcraProfile.DoesNotExist as exc:
+ except RcrainfoProfile.DoesNotExist as exc:
return self.response(data={"error": str(exc)}, status=status.HTTP_404_NOT_FOUND)
except CeleryError as exc:
return self.response(
diff --git a/server/apps/sites/README.md b/server/apps/site/README.md
similarity index 100%
rename from server/apps/sites/README.md
rename to server/apps/site/README.md
diff --git a/server/apps/sites/__init__.py b/server/apps/site/__init__.py
similarity index 100%
rename from server/apps/sites/__init__.py
rename to server/apps/site/__init__.py
diff --git a/server/apps/sites/admin.py b/server/apps/site/admin.py
similarity index 96%
rename from server/apps/sites/admin.py
rename to server/apps/site/admin.py
index f64a82f4e..64a7a1526 100644
--- a/server/apps/sites/admin.py
+++ b/server/apps/site/admin.py
@@ -4,13 +4,13 @@
from apps.core.admin import HiddenListView
from apps.core.models import HaztrakProfile
-from apps.sites.models import (
+from apps.site.models import (
Address,
Contact,
HaztrakSite,
RcraSite,
)
-from apps.sites.models.site_models import HaztrakOrg
+from apps.site.models.site_models import HaztrakOrg
@admin.register(RcraSite)
diff --git a/server/apps/sites/apps.py b/server/apps/site/apps.py
similarity index 83%
rename from server/apps/sites/apps.py
rename to server/apps/site/apps.py
index 2fe0c831b..263ff00dd 100644
--- a/server/apps/sites/apps.py
+++ b/server/apps/site/apps.py
@@ -3,4 +3,4 @@
class SitesConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
- name = "apps.sites"
+ name = "apps.site"
diff --git a/server/apps/site/migrations/0001_initial.py b/server/apps/site/migrations/0001_initial.py
new file mode 100644
index 000000000..f2a3f85c1
--- /dev/null
+++ b/server/apps/site/migrations/0001_initial.py
@@ -0,0 +1,479 @@
+# Generated by Django 4.2.7 on 2023-11-20 16:47
+
+import uuid
+
+import django.core.validators
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+import apps.site.models.contact_models
+
+
+class Migration(migrations.Migration):
+ initial = True
+
+ dependencies = [
+ ("core", "0001_initial"),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="Address",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("street_number", models.CharField(blank=True, max_length=12, null=True)),
+ ("address1", models.CharField(max_length=50, verbose_name="address 1")),
+ (
+ "address2",
+ models.CharField(
+ blank=True,
+ default=None,
+ max_length=50,
+ null=True,
+ verbose_name="address 2",
+ ),
+ ),
+ ("city", models.CharField(blank=True, max_length=25, null=True)),
+ (
+ "state",
+ models.CharField(
+ blank=True,
+ choices=[
+ ("AK", "Alaska"),
+ ("AL", "Alabama"),
+ ("AP", "Armed Forces Pacific"),
+ ("AR", "Arkansas"),
+ ("AZ", "Arizona"),
+ ("CA", "California"),
+ ("CO", "Colorado"),
+ ("CT", "Connecticut"),
+ ("DC", "Washington DC"),
+ ("DE", "Delaware"),
+ ("FL", "Florida"),
+ ("GA", "Georgia"),
+ ("GU", "Guam"),
+ ("HI", "Hawaii"),
+ ("IA", "Iowa"),
+ ("ID", "Idaho"),
+ ("IL", "Illinois"),
+ ("IN", "Indiana"),
+ ("KS", "Kansas"),
+ ("KY", "Kentucky"),
+ ("LA", "Louisiana"),
+ ("MA", "Massachusetts"),
+ ("MD", "Maryland"),
+ ("ME", "Maine"),
+ ("MI", "Michigan"),
+ ("MN", "Minnesota"),
+ ("MO", "Missouri"),
+ ("MS", "Mississippi"),
+ ("MT", "Montana"),
+ ("NC", "North Carolina"),
+ ("ND", "North Dakota"),
+ ("NE", "Nebraska"),
+ ("NH", "New Hampshire"),
+ ("NJ", "New Jersey"),
+ ("NM", "New Mexico"),
+ ("NV", "Nevada"),
+ ("NY", "New York"),
+ ("OH", "Ohio"),
+ ("OK", "Oklahoma"),
+ ("OR", "Oregon"),
+ ("PA", "Pennsylvania"),
+ ("PR", "Puerto Rico"),
+ ("RI", "Rhode Island"),
+ ("SC", "South Carolina"),
+ ("SD", "South Dakota"),
+ ("TN", "Tennessee"),
+ ("TX", "Texas"),
+ ("UT", "Utah"),
+ ("VA", "Virginia"),
+ ("VI", "Virgin Islands"),
+ ("VT", "Vermont"),
+ ("WA", "Washington"),
+ ("WI", "Wisconsin"),
+ ("WV", "West Virginia"),
+ ("WY", "Wyoming"),
+ ("XA", "REGION 01 PURVIEW"),
+ ("XB", "REGION 02 PURVIEW"),
+ ("XC", "REGION 03 PURVIEW"),
+ ("XD", "REGION 04 PURVIEW"),
+ ("XE", "REGION 05 PURVIEW"),
+ ("XF", "REGION 06 PURVIEW"),
+ ("XG", "REGION 07 PURVIEW"),
+ ("XH", "REGION 08 PURVIEW"),
+ ("XI", "REGION 09 PURVIEW"),
+ ("XJ", "REGION 10 PURVIEW"),
+ ],
+ max_length=3,
+ null=True,
+ ),
+ ),
+ (
+ "country",
+ models.CharField(
+ blank=True,
+ choices=[("US", "United States"), ("MX", "Mexico"), ("CA", "Canada")],
+ max_length=3,
+ null=True,
+ ),
+ ),
+ ("zip", models.CharField(blank=True, max_length=5, null=True)),
+ ],
+ options={
+ "ordering": ["address1"],
+ },
+ ),
+ migrations.CreateModel(
+ name="Contact",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("first_name", models.CharField(blank=True, max_length=38, null=True)),
+ ("middle_initial", models.CharField(blank=True, max_length=1, null=True)),
+ ("last_name", models.CharField(blank=True, max_length=38, null=True)),
+ ("email", models.EmailField(blank=True, max_length=254, null=True)),
+ ("company_name", models.CharField(blank=True, max_length=80, null=True)),
+ ],
+ options={
+ "ordering": ["first_name"],
+ },
+ ),
+ migrations.CreateModel(
+ name="HaztrakOrg",
+ fields=[
+ ("name", models.CharField(max_length=200, unique=True)),
+ (
+ "id",
+ models.UUIDField(
+ default=uuid.uuid4,
+ editable=False,
+ primary_key=True,
+ serialize=False,
+ unique=True,
+ ),
+ ),
+ (
+ "admin",
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "Organization",
+ "verbose_name_plural": "Organizations",
+ "ordering": ["name"],
+ },
+ ),
+ migrations.CreateModel(
+ name="HaztrakSite",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ (
+ "name",
+ models.CharField(
+ max_length=200,
+ validators=[
+ django.core.validators.MinLengthValidator(
+ 2, "site aliases must be longer than 2 characters"
+ )
+ ],
+ verbose_name="site alias",
+ ),
+ ),
+ (
+ "last_rcrainfo_manifest_sync",
+ models.DateTimeField(
+ blank=True, null=True, verbose_name="last RCRAInfo manifest sync date"
+ ),
+ ),
+ (
+ "org",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE, to="site.haztrakorg"
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "Haztrak Site",
+ "verbose_name_plural": "Haztrak Sites",
+ "ordering": ["rcra_site__epa_id"],
+ },
+ ),
+ migrations.CreateModel(
+ name="RcraPhone",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("number", apps.site.models.contact_models.RcraPhoneNumber(max_length=12)),
+ ("extension", models.CharField(blank=True, max_length=6, null=True)),
+ ],
+ options={
+ "ordering": ["number"],
+ },
+ ),
+ migrations.CreateModel(
+ name="RcraSite",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ (
+ "site_type",
+ models.CharField(
+ blank=True,
+ choices=[
+ ("Generator", "Generator"),
+ ("Transporter", "Transporter"),
+ ("Tsdf", "Tsdf"),
+ ("Broker", "Broker"),
+ ],
+ max_length=20,
+ null=True,
+ ),
+ ),
+ (
+ "epa_id",
+ models.CharField(max_length=25, unique=True, verbose_name="EPA ID number"),
+ ),
+ ("name", models.CharField(max_length=200)),
+ ("modified", models.BooleanField(blank=True, null=True)),
+ ("registered", models.BooleanField(blank=True, null=True)),
+ (
+ "gis_primary",
+ models.BooleanField(
+ blank=True, default=False, null=True, verbose_name="GIS primary"
+ ),
+ ),
+ (
+ "can_esign",
+ models.BooleanField(
+ blank=True, null=True, verbose_name="can electronically sign"
+ ),
+ ),
+ (
+ "limited_esign",
+ models.BooleanField(
+ blank=True, null=True, verbose_name="limited electronic signing ability"
+ ),
+ ),
+ (
+ "registered_emanifest_user",
+ models.BooleanField(
+ blank=True,
+ default=False,
+ null=True,
+ verbose_name="has registered e-manifest user",
+ ),
+ ),
+ (
+ "contact",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="site.contact",
+ verbose_name="contact information",
+ ),
+ ),
+ (
+ "emergency_phone",
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="site.rcraphone",
+ ),
+ ),
+ (
+ "mail_address",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="mail_address",
+ to="site.address",
+ ),
+ ),
+ (
+ "site_address",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="site_address",
+ to="site.address",
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "RCRAInfo Site",
+ "verbose_name_plural": "RCRAInfo Sites",
+ "ordering": ["epa_id"],
+ },
+ ),
+ migrations.CreateModel(
+ name="SitePermissions",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ (
+ "emanifest",
+ models.CharField(
+ choices=[("viewer", "view"), ("editor", "edit"), ("signer", "sign")],
+ default="view",
+ max_length=6,
+ ),
+ ),
+ (
+ "profile",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="site_permissions",
+ to="core.haztrakprofile",
+ ),
+ ),
+ (
+ "site",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE, to="site.haztraksite"
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "Site Permission",
+ "verbose_name_plural": "Site Permissions",
+ "ordering": ["profile"],
+ },
+ ),
+ migrations.CreateModel(
+ name="RcraSitePermissions",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("site_manager", models.BooleanField(default=False)),
+ (
+ "annual_report",
+ models.CharField(
+ choices=[
+ ("Certifier", "Certifier"),
+ ("Preparer", "Preparer"),
+ ("Viewer", "Viewer"),
+ ],
+ max_length=12,
+ ),
+ ),
+ (
+ "biennial_report",
+ models.CharField(
+ choices=[
+ ("Certifier", "Certifier"),
+ ("Preparer", "Preparer"),
+ ("Viewer", "Viewer"),
+ ],
+ max_length=12,
+ ),
+ ),
+ (
+ "e_manifest",
+ models.CharField(
+ choices=[
+ ("Certifier", "Certifier"),
+ ("Preparer", "Preparer"),
+ ("Viewer", "Viewer"),
+ ],
+ max_length=12,
+ ),
+ ),
+ (
+ "my_rcra_id",
+ models.CharField(
+ choices=[
+ ("Certifier", "Certifier"),
+ ("Preparer", "Preparer"),
+ ("Viewer", "Viewer"),
+ ],
+ max_length=12,
+ ),
+ ),
+ (
+ "wiets",
+ models.CharField(
+ choices=[
+ ("Certifier", "Certifier"),
+ ("Preparer", "Preparer"),
+ ("Viewer", "Viewer"),
+ ],
+ max_length=12,
+ ),
+ ),
+ (
+ "profile",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="permissions",
+ to="core.rcrainfoprofile",
+ ),
+ ),
+ (
+ "site",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE, to="site.rcrasite"
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "RCRAInfo Permission",
+ "verbose_name_plural": "RCRAInfo Permissions",
+ "ordering": ["site__epa_id"],
+ },
+ ),
+ migrations.AddField(
+ model_name="haztraksite",
+ name="rcra_site",
+ field=models.OneToOneField(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="site.rcrasite",
+ verbose_name="rcra_site",
+ ),
+ ),
+ migrations.AddField(
+ model_name="contact",
+ name="phone",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="site.rcraphone",
+ ),
+ ),
+ ]
diff --git a/server/apps/sites/migrations/__init__.py b/server/apps/site/migrations/__init__.py
similarity index 100%
rename from server/apps/sites/migrations/__init__.py
rename to server/apps/site/migrations/__init__.py
diff --git a/server/apps/sites/models/__init__.py b/server/apps/site/models/__init__.py
similarity index 100%
rename from server/apps/sites/models/__init__.py
rename to server/apps/site/models/__init__.py
diff --git a/server/apps/sites/models/base_models.py b/server/apps/site/models/base_models.py
similarity index 100%
rename from server/apps/sites/models/base_models.py
rename to server/apps/site/models/base_models.py
diff --git a/server/apps/sites/models/contact_models.py b/server/apps/site/models/contact_models.py
similarity index 100%
rename from server/apps/sites/models/contact_models.py
rename to server/apps/site/models/contact_models.py
diff --git a/server/apps/sites/models/site_models.py b/server/apps/site/models/site_models.py
similarity index 95%
rename from server/apps/sites/models/site_models.py
rename to server/apps/site/models/site_models.py
index 3579386ba..5109208a7 100644
--- a/server/apps/sites/models/site_models.py
+++ b/server/apps/site/models/site_models.py
@@ -7,10 +7,10 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
-from apps.sites.models import Address, Contact
-from apps.sites.models.contact_models import RcraPhone
+from apps.site.models import Address, Contact
+from apps.site.models.contact_models import RcraPhone
-from ...core.models import RcraProfile
+from ...core.models import RcrainfoProfile
from .base_models import SitesBaseManager, SitesBaseModel
logger = logging.getLogger(__name__)
@@ -45,16 +45,16 @@ class Meta:
def rcrainfo_api_id_key(self) -> tuple[str, str] | None:
"""Returns the RcraInfo API credentials for the admin user"""
try:
- rcrainfo_profile = RcraProfile.objects.get(haztrak_profile__user=self.admin)
+ rcrainfo_profile = RcrainfoProfile.objects.get(haztrak_profile__user=self.admin)
return rcrainfo_profile.rcra_api_id, rcrainfo_profile.rcra_api_key
- except RcraProfile.DoesNotExist:
+ except RcrainfoProfile.DoesNotExist:
return None
@property
def is_rcrainfo_integrated(self) -> bool:
"""Returns True if the admin user has RcraInfo API credentials"""
- if RcraProfile.objects.filter(haztrak_profile__user=self.admin).exists():
- return RcraProfile.objects.get(
+ if RcrainfoProfile.objects.filter(haztrak_profile__user=self.admin).exists():
+ return RcrainfoProfile.objects.get(
haztrak_profile__user=self.admin
).has_rcrainfo_api_id_key
else:
@@ -316,7 +316,7 @@ class Meta:
on_delete=models.CASCADE,
)
profile = models.ForeignKey(
- "core.RcraProfile",
+ "core.RcrainfoProfile",
on_delete=models.PROTECT,
related_name="permissions",
)
diff --git a/server/apps/sites/serializers/__init__.py b/server/apps/site/serializers/__init__.py
similarity index 100%
rename from server/apps/sites/serializers/__init__.py
rename to server/apps/site/serializers/__init__.py
diff --git a/server/apps/sites/serializers/address_serializer.py b/server/apps/site/serializers/address_serializer.py
similarity index 96%
rename from server/apps/sites/serializers/address_serializer.py
rename to server/apps/site/serializers/address_serializer.py
index 37ef47f80..9f6655ba6 100644
--- a/server/apps/sites/serializers/address_serializer.py
+++ b/server/apps/site/serializers/address_serializer.py
@@ -3,7 +3,7 @@
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
-from apps.sites.models import Address, RcraStates
+from apps.site.models import Address, RcraStates
from ..models.contact_models import RcraCountries
from .base_serializer import SitesBaseSerializer
diff --git a/server/apps/sites/serializers/base_serializer.py b/server/apps/site/serializers/base_serializer.py
similarity index 100%
rename from server/apps/sites/serializers/base_serializer.py
rename to server/apps/site/serializers/base_serializer.py
diff --git a/server/apps/sites/serializers/contact_serializer.py b/server/apps/site/serializers/contact_serializer.py
similarity index 97%
rename from server/apps/sites/serializers/contact_serializer.py
rename to server/apps/site/serializers/contact_serializer.py
index decd4db39..8b0e45cc6 100644
--- a/server/apps/sites/serializers/contact_serializer.py
+++ b/server/apps/site/serializers/contact_serializer.py
@@ -1,6 +1,6 @@
from rest_framework import serializers
-from apps.sites.models import Contact
+from apps.site.models import Contact
from .base_serializer import SitesBaseSerializer
diff --git a/server/apps/sites/serializers/profile_serializer.py b/server/apps/site/serializers/profile_serializer.py
similarity index 98%
rename from server/apps/sites/serializers/profile_serializer.py
rename to server/apps/site/serializers/profile_serializer.py
index e8875db64..2e6295b0c 100644
--- a/server/apps/sites/serializers/profile_serializer.py
+++ b/server/apps/site/serializers/profile_serializer.py
@@ -1,7 +1,7 @@
from rest_framework import serializers
from rest_framework.exceptions import APIException, ValidationError
-from apps.sites.models import RcraSitePermissions, SitePermissions
+from apps.site.models import RcraSitePermissions, SitePermissions
from .base_serializer import SitesBaseSerializer
from .site_serializer import HaztrakSiteSerializer
diff --git a/server/apps/sites/serializers/site_serializer.py b/server/apps/site/serializers/site_serializer.py
similarity index 94%
rename from server/apps/sites/serializers/site_serializer.py
rename to server/apps/site/serializers/site_serializer.py
index 36b1b4982..7a6183f6e 100644
--- a/server/apps/sites/serializers/site_serializer.py
+++ b/server/apps/site/serializers/site_serializer.py
@@ -1,7 +1,7 @@
from rest_framework import serializers
-from apps.sites.models import HaztrakOrg, HaztrakSite, RcraSite, RcraSiteType
-from apps.sites.serializers import AddressSerializer, ContactSerializer, RcraPhoneSerializer
+from apps.site.models import HaztrakOrg, HaztrakSite, RcraSite, RcraSiteType
+from apps.site.serializers import AddressSerializer, ContactSerializer, RcraPhoneSerializer
from .base_serializer import SitesBaseSerializer
diff --git a/server/apps/sites/services/__init__.py b/server/apps/site/services/__init__.py
similarity index 100%
rename from server/apps/sites/services/__init__.py
rename to server/apps/site/services/__init__.py
diff --git a/server/apps/sites/services/org_services.py b/server/apps/site/services/org_services.py
similarity index 94%
rename from server/apps/sites/services/org_services.py
rename to server/apps/site/services/org_services.py
index ac5d9fb55..95140dc6a 100644
--- a/server/apps/sites/services/org_services.py
+++ b/server/apps/site/services/org_services.py
@@ -1,6 +1,6 @@
from django.db.models import QuerySet
-from apps.sites.models.site_models import HaztrakOrg, HaztrakSite
+from apps.site.models.site_models import HaztrakOrg, HaztrakSite
def get_org(org_id: str) -> HaztrakOrg:
diff --git a/server/apps/sites/services/rcra_profile_services.py b/server/apps/site/services/rcra_profile_services.py
similarity index 85%
rename from server/apps/sites/services/rcra_profile_services.py
rename to server/apps/site/services/rcra_profile_services.py
index 6b8ef95d6..4964df2bd 100644
--- a/server/apps/sites/services/rcra_profile_services.py
+++ b/server/apps/site/services/rcra_profile_services.py
@@ -3,14 +3,14 @@
from django.db import transaction
-from apps.core.models import HaztrakProfile, HaztrakUser, RcraProfile # type: ignore
+from apps.core.models import HaztrakProfile, HaztrakUser, RcrainfoProfile # type: ignore
from apps.core.services import ( # type: ignore
RcrainfoService,
get_or_create_haztrak_profile,
get_rcrainfo_client,
)
-from apps.sites.models import HaztrakSite, RcraSite, RcraSitePermissions # type: ignore
-from apps.sites.serializers import RcraPermissionSerializer # type: ignore
+from apps.site.models import HaztrakSite, RcraSite, RcraSitePermissions # type: ignore
+from apps.site.serializers import RcraPermissionSerializer # type: ignore
from .rcra_site_services import RcraSiteService
from .site_services import HaztrakSiteService, HaztrakSiteServiceError # type: ignore
@@ -18,9 +18,11 @@
logger = logging.getLogger(__name__)
-def get_or_create_rcra_profile(*, username: str) -> tuple[RcraProfile, bool]:
- """Retrieve a user's RcraProfile"""
- profile, created = RcraProfile.objects.get_or_create(haztrak_profile__user__username=username)
+def get_or_create_rcra_profile(*, username: str) -> tuple[RcrainfoProfile, bool]:
+ """Retrieve a user's RcrainfoProfile"""
+ profile, created = RcrainfoProfile.objects.get_or_create(
+ haztrak_profile__user__username=username
+ )
if created:
haztrak_profile, created = get_or_create_haztrak_profile(username=username)
haztrak_profile.rcrainfo_profile = profile
@@ -38,14 +40,14 @@ def __init__(self, message: str):
class RcraProfileService:
"""
- RcraProfileService encapsulates the RcraProfile subdomain business logic
+ RcraProfileService encapsulates the RcrainfoProfile subdomain business logic
of a and exposes method corresponding to use cases.
"""
def __init__(self, *, username: str, rcrainfo: Optional[RcrainfoService] = None):
self.username = username
profile, created = get_or_create_rcra_profile(username=username)
- self.profile: RcraProfile = profile
+ self.profile: RcrainfoProfile = profile
self.rcrainfo = rcrainfo or get_rcrainfo_client(username=username)
def update_rcrainfo_profile(self, *, rcrainfo_username: Optional[str] = None) -> None:
@@ -64,7 +66,7 @@ def update_rcrainfo_profile(self, *, rcrainfo_username: Optional[str] = None) ->
)
permissions = self._parse_rcra_response(rcra_response=profile_response.json())
self._save_rcrainfo_profile_permissions(permissions)
- except (RcraProfile.DoesNotExist, RcraSite.DoesNotExist) as exc:
+ except (RcrainfoProfile.DoesNotExist, RcraSite.DoesNotExist) as exc:
raise RcraProfileServiceError(exc)
def _save_rcrainfo_profile_permissions(self, permissions: list[dict]) -> None:
diff --git a/server/apps/sites/services/rcra_site_services.py b/server/apps/site/services/rcra_site_services.py
similarity index 97%
rename from server/apps/sites/services/rcra_site_services.py
rename to server/apps/site/services/rcra_site_services.py
index 75b802c04..29174f488 100644
--- a/server/apps/sites/services/rcra_site_services.py
+++ b/server/apps/site/services/rcra_site_services.py
@@ -7,8 +7,8 @@
from rest_framework.exceptions import ValidationError
from apps.core.services import RcrainfoService, get_rcrainfo_client
-from apps.sites.models import RcraSite
-from apps.sites.serializers import RcraSiteSerializer
+from apps.site.models import RcraSite
+from apps.site.serializers import RcraSiteSerializer
class HandlerSearchResults(TypedDict):
diff --git a/server/apps/sites/services/site_services.py b/server/apps/site/services/site_services.py
similarity index 93%
rename from server/apps/sites/services/site_services.py
rename to server/apps/site/services/site_services.py
index 09022c5fd..f754da1be 100644
--- a/server/apps/sites/services/site_services.py
+++ b/server/apps/site/services/site_services.py
@@ -5,7 +5,7 @@
from django.db import transaction
from apps.core.services import RcrainfoService, get_rcrainfo_client
-from apps.sites.models import HaztrakSite
+from apps.site.models import HaztrakSite
from apps.trak.services import EManifest, PullManifestsResult, TaskResponse
from apps.trak.tasks import sync_site_manifests
@@ -23,11 +23,11 @@ class HaztrakSiteService:
"""
def __init__(
- self,
- *,
- username: str,
- site_id: Optional[str] = None,
- rcrainfo: Optional[RcrainfoService] = None,
+ self,
+ *,
+ username: str,
+ site_id: Optional[str] = None,
+ rcrainfo: Optional[RcrainfoService] = None,
):
self.username = username
self.rcrainfo = rcrainfo or get_rcrainfo_client(username=username)
@@ -63,6 +63,7 @@ def _get_updated_mtn(self, site_id: str, last_sync_date: datetime) -> list[str]:
emanifest = EManifest(username=self.username, rcrainfo=self.rcrainfo)
return emanifest.search(site_id=site_id, start_date=last_sync_date)
+
# ToDo: all of our current HaztrakSite service class (1) does not need to be a class and (2) should
# probably be moved to the manifest service module
# def get_user_sites(*, username: str) -> QuerySet[HaztrakSite]:
diff --git a/server/apps/sites/tasks/__init__.py b/server/apps/site/tasks/__init__.py
similarity index 100%
rename from server/apps/sites/tasks/__init__.py
rename to server/apps/site/tasks/__init__.py
diff --git a/server/apps/sites/tasks/profile_tasks.py b/server/apps/site/tasks/profile_tasks.py
similarity index 90%
rename from server/apps/sites/tasks/profile_tasks.py
rename to server/apps/site/tasks/profile_tasks.py
index 61087b843..bcdee6d85 100644
--- a/server/apps/sites/tasks/profile_tasks.py
+++ b/server/apps/site/tasks/profile_tasks.py
@@ -4,7 +4,7 @@
from celery.exceptions import Ignore, Reject
from requests import RequestException
-from apps.sites.services.rcra_profile_services import RcraProfileServiceError
+from apps.site.services.rcra_profile_services import RcraProfileServiceError
logger = logging.getLogger(__name__)
@@ -22,7 +22,7 @@ def sync_user_rcrainfo_sites(self: RcraProfileTasks, username: str) -> None:
This task initiates a call to the RcraProfileService to pull a user's RCRAInfo profile
and update that information in Haztrak.
"""
- from apps.sites.services import RcraProfileService
+ from apps.site.services import RcraProfileService
try:
rcra_profile = RcraProfileService(username=username)
diff --git a/server/apps/sites/tasks/site_tasks.py b/server/apps/site/tasks/site_tasks.py
similarity index 91%
rename from server/apps/sites/tasks/site_tasks.py
rename to server/apps/site/tasks/site_tasks.py
index edf90b5f6..9b70c217b 100644
--- a/server/apps/sites/tasks/site_tasks.py
+++ b/server/apps/site/tasks/site_tasks.py
@@ -3,7 +3,7 @@
from celery import shared_task, states
from celery.exceptions import Ignore
-from apps.sites.services import RcraSiteService
+from apps.site.services import RcraSiteService
logger = logging.getLogger(__name__)
diff --git a/server/apps/sites/tests/__init__.py b/server/apps/site/tests/__init__.py
similarity index 100%
rename from server/apps/sites/tests/__init__.py
rename to server/apps/site/tests/__init__.py
diff --git a/server/apps/sites/tests/conftest.py b/server/apps/site/tests/conftest.py
similarity index 89%
rename from server/apps/sites/tests/conftest.py
rename to server/apps/site/tests/conftest.py
index ddcc85614..65a0ab976 100644
--- a/server/apps/sites/tests/conftest.py
+++ b/server/apps/site/tests/conftest.py
@@ -2,8 +2,8 @@
import pytest
-from apps.core.models import RcraProfile
-from apps.sites.models import (
+from apps.core.models import RcrainfoProfile
+from apps.site.models import (
RcraSite,
RcraSitePermissions,
)
@@ -15,7 +15,7 @@ def rcra_permission_factory(db, rcra_site_factory, rcra_profile_factory):
def create_permission(
site: Optional[RcraSite] = None,
- profile: Optional[RcraProfile] = None,
+ profile: Optional[RcrainfoProfile] = None,
site_manager: Optional[bool] = True,
annual_report: Optional[str] = "Certifier",
biennial_report: Optional[str] = "Certifier",
diff --git a/server/apps/sites/tests/test_epa_site_views.py b/server/apps/site/tests/test_epa_site_views.py
similarity index 96%
rename from server/apps/sites/tests/test_epa_site_views.py
rename to server/apps/site/tests/test_epa_site_views.py
index 941c33b3f..d26e26118 100644
--- a/server/apps/sites/tests/test_epa_site_views.py
+++ b/server/apps/site/tests/test_epa_site_views.py
@@ -3,8 +3,8 @@
from rest_framework.response import Response
from rest_framework.test import APIClient, APIRequestFactory, force_authenticate
-from apps.sites.models import RcraSiteType # type: ignore
-from apps.sites.views import SearchRcraSiteView # type: ignore
+from apps.site.models import RcraSiteType # type: ignore
+from apps.site.views import SearchRcraSiteView # type: ignore
class TestEpaSiteView:
diff --git a/server/apps/sites/tests/test_handler_services.py b/server/apps/site/tests/test_handler_services.py
similarity index 94%
rename from server/apps/sites/tests/test_handler_services.py
rename to server/apps/site/tests/test_handler_services.py
index df678b535..ce8d4be75 100644
--- a/server/apps/sites/tests/test_handler_services.py
+++ b/server/apps/site/tests/test_handler_services.py
@@ -1,8 +1,8 @@
import pytest
from apps.core.services import RcrainfoService
-from apps.sites.models import RcraSite
-from apps.sites.services import RcraSiteService
+from apps.site.models import RcraSite
+from apps.site.services import RcraSiteService
class TestHandlerService:
diff --git a/server/apps/sites/tests/test_models.py b/server/apps/site/tests/test_models.py
similarity index 97%
rename from server/apps/sites/tests/test_models.py
rename to server/apps/site/tests/test_models.py
index b335a4022..4cd708df6 100644
--- a/server/apps/sites/tests/test_models.py
+++ b/server/apps/site/tests/test_models.py
@@ -2,8 +2,8 @@
from django.core.exceptions import ValidationError
from django.db import IntegrityError
-from apps.sites.models import Address, Contact, HaztrakSite
-from apps.sites.models.site_models import HaztrakOrg
+from apps.site.models import Address, Contact, HaztrakSite
+from apps.site.models.site_models import HaztrakOrg
@pytest.mark.django_db
diff --git a/server/apps/sites/tests/test_serializers.py b/server/apps/site/tests/test_serializers.py
similarity index 97%
rename from server/apps/sites/tests/test_serializers.py
rename to server/apps/site/tests/test_serializers.py
index 51c6ea8c5..e4224eae3 100644
--- a/server/apps/sites/tests/test_serializers.py
+++ b/server/apps/site/tests/test_serializers.py
@@ -1,7 +1,7 @@
import pytest
-from apps.sites.models import RcraSitePermissions
-from apps.sites.serializers import (
+from apps.site.models import RcraSitePermissions
+from apps.site.serializers import (
ContactSerializer,
HaztrakOrgSerializer,
RcraPermissionSerializer,
diff --git a/server/apps/sites/tests/test_services.py b/server/apps/site/tests/test_services.py
similarity index 88%
rename from server/apps/sites/tests/test_services.py
rename to server/apps/site/tests/test_services.py
index ecfb6d7b2..3645f08fa 100644
--- a/server/apps/sites/tests/test_services.py
+++ b/server/apps/site/tests/test_services.py
@@ -1,8 +1,8 @@
import uuid
-from apps.core.models import HaztrakProfile, RcraProfile
-from apps.sites.services.org_services import get_org, get_org_rcrainfo_api_credentials
-from apps.sites.services.rcra_profile_services import get_or_create_rcra_profile
+from apps.core.models import HaztrakProfile, RcrainfoProfile
+from apps.site.services.org_services import get_org, get_org_rcrainfo_api_credentials
+from apps.site.services.rcra_profile_services import get_or_create_rcra_profile
class TestOrgServices:
@@ -46,7 +46,7 @@ def test_get_or_create_returns_true_if_new_profile(self, user_factory):
username = "my_username"
user_factory(username=username)
retrieved_rcra_profile, created = get_or_create_rcra_profile(username=username)
- assert isinstance(retrieved_rcra_profile, RcraProfile)
+ assert isinstance(retrieved_rcra_profile, RcrainfoProfile)
assert created is True
def test_creates_creates_a_haztrak_profile_if_not_present(self, user_factory):
diff --git a/server/apps/sites/tests/test_site_views.py b/server/apps/site/tests/test_site_views.py
similarity index 95%
rename from server/apps/sites/tests/test_site_views.py
rename to server/apps/site/tests/test_site_views.py
index d0c55355d..3b9b8ebf1 100644
--- a/server/apps/sites/tests/test_site_views.py
+++ b/server/apps/site/tests/test_site_views.py
@@ -2,9 +2,9 @@
from rest_framework import status
from rest_framework.test import APIClient, APIRequestFactory, force_authenticate
-from apps.core.models import HaztrakUser, RcraProfile # type: ignore
-from apps.sites.models import HaztrakSite, RcraSite, RcraSitePermissions # type: ignore
-from apps.sites.views import SiteDetailView # type: ignore
+from apps.core.models import HaztrakUser, RcrainfoProfile # type: ignore
+from apps.site.models import HaztrakSite, RcraSite, RcraSitePermissions # type: ignore
+from apps.site.views import SiteDetailView # type: ignore
class TestHaztrakSiteListView:
diff --git a/server/apps/sites/urls.py b/server/apps/site/urls.py
similarity index 93%
rename from server/apps/sites/urls.py
rename to server/apps/site/urls.py
index 536a037a0..ad44d5fa3 100644
--- a/server/apps/sites/urls.py
+++ b/server/apps/site/urls.py
@@ -1,6 +1,6 @@
from django.urls import include, path
-from apps.sites.views import ( # type: ignore
+from apps.site.views import ( # type: ignore
GetRcraSiteView,
HaztrakOrgSitesListView,
HaztrakSiteListView,
diff --git a/server/apps/sites/views.py b/server/apps/site/views.py
similarity index 94%
rename from server/apps/sites/views.py
rename to server/apps/site/views.py
index 34dc73183..07fe508c4 100644
--- a/server/apps/sites/views.py
+++ b/server/apps/site/views.py
@@ -9,10 +9,10 @@
from rest_framework.response import Response
from rest_framework.views import APIView
-from apps.sites.models import HaztrakSite, RcraSite, RcraSiteType # type: ignore
-from apps.sites.serializers import HaztrakSiteSerializer, RcraSiteSerializer # type: ignore
-from apps.sites.services import RcraSiteService, get_org_sites # type: ignore
-from apps.sites.services.rcra_site_services import query_rcra_sites
+from apps.site.models import HaztrakSite, RcraSite, RcraSiteType # type: ignore
+from apps.site.serializers import HaztrakSiteSerializer, RcraSiteSerializer # type: ignore
+from apps.site.services import RcraSiteService, get_org_sites # type: ignore
+from apps.site.services.rcra_site_services import query_rcra_sites
logger = logging.getLogger(__name__)
diff --git a/server/apps/sites/migrations/0001_initial.py b/server/apps/sites/migrations/0001_initial.py
deleted file mode 100644
index 53a17932c..000000000
--- a/server/apps/sites/migrations/0001_initial.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# Generated by Django 4.2.7 on 2023-11-20 16:47
-
-import apps.sites.models.contact_models
-from django.conf import settings
-import django.core.validators
-from django.db import migrations, models
-import django.db.models.deletion
-import uuid
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('core', '0001_initial'),
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Address',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('street_number', models.CharField(blank=True, max_length=12, null=True)),
- ('address1', models.CharField(max_length=50, verbose_name='address 1')),
- ('address2', models.CharField(blank=True, default=None, max_length=50, null=True, verbose_name='address 2')),
- ('city', models.CharField(blank=True, max_length=25, null=True)),
- ('state', models.CharField(blank=True, choices=[('AK', 'Alaska'), ('AL', 'Alabama'), ('AP', 'Armed Forces Pacific'), ('AR', 'Arkansas'), ('AZ', 'Arizona'), ('CA', 'California'), ('CO', 'Colorado'), ('CT', 'Connecticut'), ('DC', 'Washington DC'), ('DE', 'Delaware'), ('FL', 'Florida'), ('GA', 'Georgia'), ('GU', 'Guam'), ('HI', 'Hawaii'), ('IA', 'Iowa'), ('ID', 'Idaho'), ('IL', 'Illinois'), ('IN', 'Indiana'), ('KS', 'Kansas'), ('KY', 'Kentucky'), ('LA', 'Louisiana'), ('MA', 'Massachusetts'), ('MD', 'Maryland'), ('ME', 'Maine'), ('MI', 'Michigan'), ('MN', 'Minnesota'), ('MO', 'Missouri'), ('MS', 'Mississippi'), ('MT', 'Montana'), ('NC', 'North Carolina'), ('ND', 'North Dakota'), ('NE', 'Nebraska'), ('NH', 'New Hampshire'), ('NJ', 'New Jersey'), ('NM', 'New Mexico'), ('NV', 'Nevada'), ('NY', 'New York'), ('OH', 'Ohio'), ('OK', 'Oklahoma'), ('OR', 'Oregon'), ('PA', 'Pennsylvania'), ('PR', 'Puerto Rico'), ('RI', 'Rhode Island'), ('SC', 'South Carolina'), ('SD', 'South Dakota'), ('TN', 'Tennessee'), ('TX', 'Texas'), ('UT', 'Utah'), ('VA', 'Virginia'), ('VI', 'Virgin Islands'), ('VT', 'Vermont'), ('WA', 'Washington'), ('WI', 'Wisconsin'), ('WV', 'West Virginia'), ('WY', 'Wyoming'), ('XA', 'REGION 01 PURVIEW'), ('XB', 'REGION 02 PURVIEW'), ('XC', 'REGION 03 PURVIEW'), ('XD', 'REGION 04 PURVIEW'), ('XE', 'REGION 05 PURVIEW'), ('XF', 'REGION 06 PURVIEW'), ('XG', 'REGION 07 PURVIEW'), ('XH', 'REGION 08 PURVIEW'), ('XI', 'REGION 09 PURVIEW'), ('XJ', 'REGION 10 PURVIEW')], max_length=3, null=True)),
- ('country', models.CharField(blank=True, choices=[('US', 'United States'), ('MX', 'Mexico'), ('CA', 'Canada')], max_length=3, null=True)),
- ('zip', models.CharField(blank=True, max_length=5, null=True)),
- ],
- options={
- 'ordering': ['address1'],
- },
- ),
- migrations.CreateModel(
- name='Contact',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('first_name', models.CharField(blank=True, max_length=38, null=True)),
- ('middle_initial', models.CharField(blank=True, max_length=1, null=True)),
- ('last_name', models.CharField(blank=True, max_length=38, null=True)),
- ('email', models.EmailField(blank=True, max_length=254, null=True)),
- ('company_name', models.CharField(blank=True, max_length=80, null=True)),
- ],
- options={
- 'ordering': ['first_name'],
- },
- ),
- migrations.CreateModel(
- name='HaztrakOrg',
- fields=[
- ('name', models.CharField(max_length=200, unique=True)),
- ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
- ('admin', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
- ],
- options={
- 'verbose_name': 'Organization',
- 'verbose_name_plural': 'Organizations',
- 'ordering': ['name'],
- },
- ),
- migrations.CreateModel(
- name='HaztrakSite',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=200, validators=[django.core.validators.MinLengthValidator(2, 'site aliases must be longer than 2 characters')], verbose_name='site alias')),
- ('last_rcrainfo_manifest_sync', models.DateTimeField(blank=True, null=True, verbose_name='last RCRAInfo manifest sync date')),
- ('org', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.haztrakorg')),
- ],
- options={
- 'verbose_name': 'Haztrak Site',
- 'verbose_name_plural': 'Haztrak Sites',
- 'ordering': ['rcra_site__epa_id'],
- },
- ),
- migrations.CreateModel(
- name='RcraPhone',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('number', apps.sites.models.contact_models.RcraPhoneNumber(max_length=12)),
- ('extension', models.CharField(blank=True, max_length=6, null=True)),
- ],
- options={
- 'ordering': ['number'],
- },
- ),
- migrations.CreateModel(
- name='RcraSite',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('site_type', models.CharField(blank=True, choices=[('Generator', 'Generator'), ('Transporter', 'Transporter'), ('Tsdf', 'Tsdf'), ('Broker', 'Broker')], max_length=20, null=True)),
- ('epa_id', models.CharField(max_length=25, unique=True, verbose_name='EPA ID number')),
- ('name', models.CharField(max_length=200)),
- ('modified', models.BooleanField(blank=True, null=True)),
- ('registered', models.BooleanField(blank=True, null=True)),
- ('gis_primary', models.BooleanField(blank=True, default=False, null=True, verbose_name='GIS primary')),
- ('can_esign', models.BooleanField(blank=True, null=True, verbose_name='can electronically sign')),
- ('limited_esign', models.BooleanField(blank=True, null=True, verbose_name='limited electronic signing ability')),
- ('registered_emanifest_user', models.BooleanField(blank=True, default=False, null=True, verbose_name='has registered e-manifest user')),
- ('contact', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.contact', verbose_name='contact information')),
- ('emergency_phone', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sites.rcraphone')),
- ('mail_address', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mail_address', to='sites.address')),
- ('site_address', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='site_address', to='sites.address')),
- ],
- options={
- 'verbose_name': 'RCRAInfo Site',
- 'verbose_name_plural': 'RCRAInfo Sites',
- 'ordering': ['epa_id'],
- },
- ),
- migrations.CreateModel(
- name='SitePermissions',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('emanifest', models.CharField(choices=[('viewer', 'view'), ('editor', 'edit'), ('signer', 'sign')], default='view', max_length=6)),
- ('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='site_permissions', to='core.haztrakprofile')),
- ('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.haztraksite')),
- ],
- options={
- 'verbose_name': 'Site Permission',
- 'verbose_name_plural': 'Site Permissions',
- 'ordering': ['profile'],
- },
- ),
- migrations.CreateModel(
- name='RcraSitePermissions',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('site_manager', models.BooleanField(default=False)),
- ('annual_report', models.CharField(choices=[('Certifier', 'Certifier'), ('Preparer', 'Preparer'), ('Viewer', 'Viewer')], max_length=12)),
- ('biennial_report', models.CharField(choices=[('Certifier', 'Certifier'), ('Preparer', 'Preparer'), ('Viewer', 'Viewer')], max_length=12)),
- ('e_manifest', models.CharField(choices=[('Certifier', 'Certifier'), ('Preparer', 'Preparer'), ('Viewer', 'Viewer')], max_length=12)),
- ('my_rcra_id', models.CharField(choices=[('Certifier', 'Certifier'), ('Preparer', 'Preparer'), ('Viewer', 'Viewer')], max_length=12)),
- ('wiets', models.CharField(choices=[('Certifier', 'Certifier'), ('Preparer', 'Preparer'), ('Viewer', 'Viewer')], max_length=12)),
- ('profile', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='permissions', to='core.rcraprofile')),
- ('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.rcrasite')),
- ],
- options={
- 'verbose_name': 'RCRAInfo Permission',
- 'verbose_name_plural': 'RCRAInfo Permissions',
- 'ordering': ['site__epa_id'],
- },
- ),
- migrations.AddField(
- model_name='haztraksite',
- name='rcra_site',
- field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='sites.rcrasite', verbose_name='rcra_site'),
- ),
- migrations.AddField(
- model_name='contact',
- name='phone',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sites.rcraphone'),
- ),
- ]
diff --git a/server/apps/trak/migrations/0001_initial.py b/server/apps/trak/migrations/0001_initial.py
index 6593568b9..97baac20e 100644
--- a/server/apps/trak/migrations/0001_initial.py
+++ b/server/apps/trak/migrations/0001_initial.py
@@ -7,249 +7,746 @@
class Migration(migrations.Migration):
-
initial = True
dependencies = [
- ('sites', '0001_initial'),
+ ("site", "0001_initial"),
]
operations = [
migrations.CreateModel(
- name='AdditionalInfo',
+ name="AdditionalInfo",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('original_mtn', models.JSONField(blank=True, help_text='Original manifest tracking number of rejected manifestRegex expression validation: [0-9]{9}[A-Z]{3}', null=True, validators=[apps.trak.models.manifest_models.validate_mtn])),
- ('new_destination', models.CharField(blank=True, choices=[('GEN', 'Generator'), ('TSD', 'Tsdf')], help_text='Destination of the new manifest created during rejection or residue.', max_length=255, null=True)),
- ('consent_number', models.CharField(blank=True, max_length=12, null=True)),
- ('comments', models.JSONField(blank=True, null=True)),
- ('handling_instructions', models.CharField(blank=True, help_text='Special Handling Instructions', max_length=4000, null=True)),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ (
+ "original_mtn",
+ models.JSONField(
+ blank=True,
+ help_text="Original manifest tracking number of rejected manifestRegex expression validation: [0-9]{9}[A-Z]{3}",
+ null=True,
+ validators=[apps.trak.models.manifest_models.validate_mtn],
+ ),
+ ),
+ (
+ "new_destination",
+ models.CharField(
+ blank=True,
+ choices=[("GEN", "Generator"), ("TSD", "Tsdf")],
+ help_text="Destination of the new manifest created during rejection or residue.",
+ max_length=255,
+ null=True,
+ ),
+ ),
+ ("consent_number", models.CharField(blank=True, max_length=12, null=True)),
+ ("comments", models.JSONField(blank=True, null=True)),
+ (
+ "handling_instructions",
+ models.CharField(
+ blank=True,
+ help_text="Special Handling Instructions",
+ max_length=4000,
+ null=True,
+ ),
+ ),
],
options={
- 'verbose_name': 'Additional Info',
- 'verbose_name_plural': 'Additional Info',
+ "verbose_name": "Additional Info",
+ "verbose_name_plural": "Additional Info",
},
),
migrations.CreateModel(
- name='DotLookup',
+ name="DotLookup",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('value', models.CharField(max_length=255)),
- ('value_type', models.CharField(choices=[('ID', 'Id'), ('GROUP', 'Group'), ('NAME', 'Name'), ('CLASS', 'Class')], max_length=5)),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("value", models.CharField(max_length=255)),
+ (
+ "value_type",
+ models.CharField(
+ choices=[
+ ("ID", "Id"),
+ ("GROUP", "Group"),
+ ("NAME", "Name"),
+ ("CLASS", "Class"),
+ ],
+ max_length=5,
+ ),
+ ),
],
options={
- 'verbose_name': 'DOT lookup',
- 'verbose_name_plural': 'DOT lookups',
- 'ordering': ['value_type', 'value'],
+ "verbose_name": "DOT lookup",
+ "verbose_name_plural": "DOT lookups",
+ "ordering": ["value_type", "value"],
},
),
migrations.CreateModel(
- name='Handler',
+ name="Handler",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
],
options={
- 'ordering': ['rcra_site'],
+ "ordering": ["rcra_site"],
},
),
migrations.CreateModel(
- name='Manifest',
+ name="Manifest",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('created_date', models.DateTimeField(auto_now=True, null=True)),
- ('update_date', models.DateTimeField(auto_now=True)),
- ('mtn', models.CharField(default=apps.trak.models.manifest_models.draft_mtn, max_length=30, unique=True, validators=[apps.trak.models.manifest_models.validate_mtn], verbose_name='manifest Tracking Number')),
- ('status', models.CharField(choices=[('NotAssigned', 'Not Assigned'), ('Pending', 'Pending'), ('Scheduled', 'Scheduled'), ('InTransit', 'In Transit'), ('ReadyForSignature', 'Ready for Signature'), ('Signed', 'Signed'), ('Corrected', 'Corrected'), ('UnderCorrection', 'Under Correction'), ('MtnValidationFailed', 'MTN Validation Failed')], default='NotAssigned', max_length=25)),
- ('submission_type', models.CharField(choices=[('FullElectronic', 'Full Electronic'), ('DataImage5Copy', 'Data + Image'), ('Hybrid', 'Hybrid'), ('Image', 'Image')], default='FullElectronic', max_length=25)),
- ('signature_status', models.BooleanField(blank=True, null=True)),
- ('origin_type', models.CharField(choices=[('Web', 'Web'), ('Service', 'Service'), ('Mail', 'Mail')], default='Service', max_length=25)),
- ('shipped_date', models.DateTimeField(blank=True, null=True)),
- ('potential_ship_date', models.DateTimeField(blank=True, null=True, verbose_name='potential ship date')),
- ('received_date', models.DateTimeField(blank=True, null=True)),
- ('certified_date', models.DateTimeField(blank=True, null=True)),
- ('certified_by', models.JSONField(blank=True, null=True)),
- ('broker', models.JSONField(blank=True, null=True)),
- ('rejection', models.BooleanField(blank=True, null=True)),
- ('rejection_info', models.JSONField(blank=True, null=True, verbose_name='Rejection Information')),
- ('discrepancy', models.BooleanField(blank=True, default=False)),
- ('residue', models.BooleanField(blank=True, default=False)),
- ('residue_new_mtn', models.JSONField(blank=True, default=list, verbose_name='residue new MTN')),
- ('import_flag', models.BooleanField(blank=True, default=False, verbose_name='import')),
- ('import_info', models.JSONField(blank=True, null=True, verbose_name='import information')),
- ('contains_residue_or_rejection', models.BooleanField(blank=True, null=True, verbose_name='contains previous rejection or residue waste')),
- ('printed_document', models.JSONField(blank=True, null=True)),
- ('form_document', models.JSONField(blank=True, null=True)),
- ('correction_info', models.JSONField(blank=True, null=True)),
- ('ppc_status', models.JSONField(blank=True, null=True, verbose_name='PPC info')),
- ('locked', models.BooleanField(blank=True, null=True)),
- ('lock_reason', models.CharField(blank=True, choices=[('ACS', 'AsyncSign'), ('ECB', 'EpaChangeBiller'), ('EPC', 'EpaCorrection')], max_length=25, null=True)),
- ('transfer_requested', models.BooleanField(blank=True, null=True)),
- ('transfer_status', models.CharField(blank=True, max_length=200, null=True)),
- ('original_sub_type', models.CharField(blank=True, choices=[('FullElectronic', 'Full Electronic'), ('DataImage5Copy', 'Data + Image'), ('Hybrid', 'Hybrid'), ('Image', 'Image')], max_length=25, null=True, verbose_name='original submission type')),
- ('transfer_count', models.IntegerField(blank=True, null=True)),
- ('next_transfer_time', models.DateTimeField(blank=True, null=True, verbose_name='next transfer time')),
- ('additional_info', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='trak.additionalinfo')),
- ('generator', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='generator', to='trak.handler')),
- ('tsdf', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='designated_facility', to='trak.handler', verbose_name='designated facility')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("created_date", models.DateTimeField(auto_now=True, null=True)),
+ ("update_date", models.DateTimeField(auto_now=True)),
+ (
+ "mtn",
+ models.CharField(
+ default=apps.trak.models.manifest_models.draft_mtn,
+ max_length=30,
+ unique=True,
+ validators=[apps.trak.models.manifest_models.validate_mtn],
+ verbose_name="manifest Tracking Number",
+ ),
+ ),
+ (
+ "status",
+ models.CharField(
+ choices=[
+ ("NotAssigned", "Not Assigned"),
+ ("Pending", "Pending"),
+ ("Scheduled", "Scheduled"),
+ ("InTransit", "In Transit"),
+ ("ReadyForSignature", "Ready for Signature"),
+ ("Signed", "Signed"),
+ ("Corrected", "Corrected"),
+ ("UnderCorrection", "Under Correction"),
+ ("MtnValidationFailed", "MTN Validation Failed"),
+ ],
+ default="NotAssigned",
+ max_length=25,
+ ),
+ ),
+ (
+ "submission_type",
+ models.CharField(
+ choices=[
+ ("FullElectronic", "Full Electronic"),
+ ("DataImage5Copy", "Data + Image"),
+ ("Hybrid", "Hybrid"),
+ ("Image", "Image"),
+ ],
+ default="FullElectronic",
+ max_length=25,
+ ),
+ ),
+ ("signature_status", models.BooleanField(blank=True, null=True)),
+ (
+ "origin_type",
+ models.CharField(
+ choices=[("Web", "Web"), ("Service", "Service"), ("Mail", "Mail")],
+ default="Service",
+ max_length=25,
+ ),
+ ),
+ ("shipped_date", models.DateTimeField(blank=True, null=True)),
+ (
+ "potential_ship_date",
+ models.DateTimeField(
+ blank=True, null=True, verbose_name="potential ship date"
+ ),
+ ),
+ ("received_date", models.DateTimeField(blank=True, null=True)),
+ ("certified_date", models.DateTimeField(blank=True, null=True)),
+ ("certified_by", models.JSONField(blank=True, null=True)),
+ ("broker", models.JSONField(blank=True, null=True)),
+ ("rejection", models.BooleanField(blank=True, null=True)),
+ (
+ "rejection_info",
+ models.JSONField(blank=True, null=True, verbose_name="Rejection Information"),
+ ),
+ ("discrepancy", models.BooleanField(blank=True, default=False)),
+ ("residue", models.BooleanField(blank=True, default=False)),
+ (
+ "residue_new_mtn",
+ models.JSONField(blank=True, default=list, verbose_name="residue new MTN"),
+ ),
+ (
+ "import_flag",
+ models.BooleanField(blank=True, default=False, verbose_name="import"),
+ ),
+ (
+ "import_info",
+ models.JSONField(blank=True, null=True, verbose_name="import information"),
+ ),
+ (
+ "contains_residue_or_rejection",
+ models.BooleanField(
+ blank=True,
+ null=True,
+ verbose_name="contains previous rejection or residue waste",
+ ),
+ ),
+ ("printed_document", models.JSONField(blank=True, null=True)),
+ ("form_document", models.JSONField(blank=True, null=True)),
+ ("correction_info", models.JSONField(blank=True, null=True)),
+ ("ppc_status", models.JSONField(blank=True, null=True, verbose_name="PPC info")),
+ ("locked", models.BooleanField(blank=True, null=True)),
+ (
+ "lock_reason",
+ models.CharField(
+ blank=True,
+ choices=[
+ ("ACS", "AsyncSign"),
+ ("ECB", "EpaChangeBiller"),
+ ("EPC", "EpaCorrection"),
+ ],
+ max_length=25,
+ null=True,
+ ),
+ ),
+ ("transfer_requested", models.BooleanField(blank=True, null=True)),
+ ("transfer_status", models.CharField(blank=True, max_length=200, null=True)),
+ (
+ "original_sub_type",
+ models.CharField(
+ blank=True,
+ choices=[
+ ("FullElectronic", "Full Electronic"),
+ ("DataImage5Copy", "Data + Image"),
+ ("Hybrid", "Hybrid"),
+ ("Image", "Image"),
+ ],
+ max_length=25,
+ null=True,
+ verbose_name="original submission type",
+ ),
+ ),
+ ("transfer_count", models.IntegerField(blank=True, null=True)),
+ (
+ "next_transfer_time",
+ models.DateTimeField(blank=True, null=True, verbose_name="next transfer time"),
+ ),
+ (
+ "additional_info",
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="trak.additionalinfo",
+ ),
+ ),
+ (
+ "generator",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="generator",
+ to="trak.handler",
+ ),
+ ),
+ (
+ "tsdf",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.PROTECT,
+ related_name="designated_facility",
+ to="trak.handler",
+ verbose_name="designated facility",
+ ),
+ ),
],
options={
- 'ordering': ['update_date', 'mtn'],
+ "ordering": ["update_date", "mtn"],
},
),
migrations.CreateModel(
- name='ManifestPhone',
+ name="ManifestPhone",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('number', apps.trak.models.contact_models.ManifestPhoneNumber(max_length=12)),
- ('extension', models.CharField(blank=True, max_length=6, null=True)),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("number", apps.trak.models.contact_models.ManifestPhoneNumber(max_length=12)),
+ ("extension", models.CharField(blank=True, max_length=6, null=True)),
],
options={
- 'ordering': ['number'],
+ "ordering": ["number"],
},
),
migrations.CreateModel(
- name='PaperSignature',
+ name="PaperSignature",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('printed_name', models.CharField(max_length=255)),
- ('sign_date', models.DateTimeField()),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("printed_name", models.CharField(max_length=255)),
+ ("sign_date", models.DateTimeField()),
],
options={
- 'ordering': ['pk'],
- 'abstract': False,
+ "ordering": ["pk"],
+ "abstract": False,
},
),
migrations.CreateModel(
- name='PortOfEntry',
+ name="PortOfEntry",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('state', models.CharField(blank=True, choices=[('AK', 'Alaska'), ('AL', 'Alabama'), ('AP', 'Armed Forces Pacific'), ('AR', 'Arkansas'), ('AZ', 'Arizona'), ('CA', 'California'), ('CO', 'Colorado'), ('CT', 'Connecticut'), ('DC', 'Washington DC'), ('DE', 'Delaware'), ('FL', 'Florida'), ('GA', 'Georgia'), ('GU', 'Guam'), ('HI', 'Hawaii'), ('IA', 'Iowa'), ('ID', 'Idaho'), ('IL', 'Illinois'), ('IN', 'Indiana'), ('KS', 'Kansas'), ('KY', 'Kentucky'), ('LA', 'Louisiana'), ('MA', 'Massachusetts'), ('MD', 'Maryland'), ('ME', 'Maine'), ('MI', 'Michigan'), ('MN', 'Minnesota'), ('MO', 'Missouri'), ('MS', 'Mississippi'), ('MT', 'Montana'), ('NC', 'North Carolina'), ('ND', 'North Dakota'), ('NE', 'Nebraska'), ('NH', 'New Hampshire'), ('NJ', 'New Jersey'), ('NM', 'New Mexico'), ('NV', 'Nevada'), ('NY', 'New York'), ('OH', 'Ohio'), ('OK', 'Oklahoma'), ('OR', 'Oregon'), ('PA', 'Pennsylvania'), ('PR', 'Puerto Rico'), ('RI', 'Rhode Island'), ('SC', 'South Carolina'), ('SD', 'South Dakota'), ('TN', 'Tennessee'), ('TX', 'Texas'), ('UT', 'Utah'), ('VA', 'Virginia'), ('VI', 'Virgin Islands'), ('VT', 'Vermont'), ('WA', 'Washington'), ('WI', 'Wisconsin'), ('WV', 'West Virginia'), ('WY', 'Wyoming'), ('XA', 'REGION 01 PURVIEW'), ('XB', 'REGION 02 PURVIEW'), ('XC', 'REGION 03 PURVIEW'), ('XD', 'REGION 04 PURVIEW'), ('XE', 'REGION 05 PURVIEW'), ('XF', 'REGION 06 PURVIEW'), ('XG', 'REGION 07 PURVIEW'), ('XH', 'REGION 08 PURVIEW'), ('XI', 'REGION 09 PURVIEW'), ('XJ', 'REGION 10 PURVIEW')], max_length=2, null=True)),
- ('city_port', models.CharField(blank=True, max_length=100, null=True)),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ (
+ "state",
+ models.CharField(
+ blank=True,
+ choices=[
+ ("AK", "Alaska"),
+ ("AL", "Alabama"),
+ ("AP", "Armed Forces Pacific"),
+ ("AR", "Arkansas"),
+ ("AZ", "Arizona"),
+ ("CA", "California"),
+ ("CO", "Colorado"),
+ ("CT", "Connecticut"),
+ ("DC", "Washington DC"),
+ ("DE", "Delaware"),
+ ("FL", "Florida"),
+ ("GA", "Georgia"),
+ ("GU", "Guam"),
+ ("HI", "Hawaii"),
+ ("IA", "Iowa"),
+ ("ID", "Idaho"),
+ ("IL", "Illinois"),
+ ("IN", "Indiana"),
+ ("KS", "Kansas"),
+ ("KY", "Kentucky"),
+ ("LA", "Louisiana"),
+ ("MA", "Massachusetts"),
+ ("MD", "Maryland"),
+ ("ME", "Maine"),
+ ("MI", "Michigan"),
+ ("MN", "Minnesota"),
+ ("MO", "Missouri"),
+ ("MS", "Mississippi"),
+ ("MT", "Montana"),
+ ("NC", "North Carolina"),
+ ("ND", "North Dakota"),
+ ("NE", "Nebraska"),
+ ("NH", "New Hampshire"),
+ ("NJ", "New Jersey"),
+ ("NM", "New Mexico"),
+ ("NV", "Nevada"),
+ ("NY", "New York"),
+ ("OH", "Ohio"),
+ ("OK", "Oklahoma"),
+ ("OR", "Oregon"),
+ ("PA", "Pennsylvania"),
+ ("PR", "Puerto Rico"),
+ ("RI", "Rhode Island"),
+ ("SC", "South Carolina"),
+ ("SD", "South Dakota"),
+ ("TN", "Tennessee"),
+ ("TX", "Texas"),
+ ("UT", "Utah"),
+ ("VA", "Virginia"),
+ ("VI", "Virgin Islands"),
+ ("VT", "Vermont"),
+ ("WA", "Washington"),
+ ("WI", "Wisconsin"),
+ ("WV", "West Virginia"),
+ ("WY", "Wyoming"),
+ ("XA", "REGION 01 PURVIEW"),
+ ("XB", "REGION 02 PURVIEW"),
+ ("XC", "REGION 03 PURVIEW"),
+ ("XD", "REGION 04 PURVIEW"),
+ ("XE", "REGION 05 PURVIEW"),
+ ("XF", "REGION 06 PURVIEW"),
+ ("XG", "REGION 07 PURVIEW"),
+ ("XH", "REGION 08 PURVIEW"),
+ ("XI", "REGION 09 PURVIEW"),
+ ("XJ", "REGION 10 PURVIEW"),
+ ],
+ max_length=2,
+ null=True,
+ ),
+ ),
+ ("city_port", models.CharField(blank=True, max_length=100, null=True)),
],
options={
- 'verbose_name': 'Port of Entry',
- 'verbose_name_plural': 'Ports of Entry',
+ "verbose_name": "Port of Entry",
+ "verbose_name_plural": "Ports of Entry",
},
),
migrations.CreateModel(
- name='WasteCode',
+ name="WasteCode",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('code', models.CharField(max_length=6, unique=True)),
- ('description', models.TextField(blank=True, null=True)),
- ('code_type', models.CharField(choices=[('ST', 'State'), ('FD', 'Federal')], max_length=2)),
- ('state_id', models.CharField(blank=True, choices=[('AK', 'Alaska'), ('AL', 'Alabama'), ('AP', 'Armed Forces Pacific'), ('AR', 'Arkansas'), ('AZ', 'Arizona'), ('CA', 'California'), ('CO', 'Colorado'), ('CT', 'Connecticut'), ('DC', 'District of Columbia'), ('DE', 'Delaware'), ('FL', 'Florida'), ('GA', 'Georgia'), ('GU', 'Guam'), ('HI', 'Hawaii'), ('IA', 'Iowa'), ('ID', 'Idaho'), ('IL', 'Illinois'), ('IN', 'Indiana'), ('KS', 'Kansas'), ('KY', 'Kentucky'), ('LA', 'Louisiana'), ('MA', 'Massachusetts'), ('MD', 'Maryland'), ('ME', 'Maine'), ('MI', 'Michigan'), ('MN', 'Minnesota'), ('MO', 'Missouri'), ('MS', 'Mississippi'), ('MT', 'Montana'), ('NC', 'North Carolina'), ('ND', 'North Dakota'), ('NE', 'Nebraska'), ('NH', 'New Hampshire'), ('NJ', 'New Jersey'), ('NM', 'New Mexico'), ('NV', 'Nevada'), ('NY', 'New York'), ('OH', 'Ohio'), ('OK', 'Oklahoma'), ('OR', 'Oregon'), ('PA', 'Pennsylvania'), ('PR', 'Puerto Rico'), ('RI', 'Rhode Island'), ('SC', 'South Carolina'), ('SD', 'South Dakota'), ('TN', 'Tennessee'), ('TX', 'Texas'), ('UT', 'Utah'), ('VA', 'Virginia'), ('VI', 'Virgin Islands'), ('VT', 'Vermont'), ('WA', 'Washington'), ('WI', 'Wisconsin'), ('WV', 'West Virginia'), ('WY', 'Wyoming')], max_length=3, null=True)),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("code", models.CharField(max_length=6, unique=True)),
+ ("description", models.TextField(blank=True, null=True)),
+ (
+ "code_type",
+ models.CharField(choices=[("ST", "State"), ("FD", "Federal")], max_length=2),
+ ),
+ (
+ "state_id",
+ models.CharField(
+ blank=True,
+ choices=[
+ ("AK", "Alaska"),
+ ("AL", "Alabama"),
+ ("AP", "Armed Forces Pacific"),
+ ("AR", "Arkansas"),
+ ("AZ", "Arizona"),
+ ("CA", "California"),
+ ("CO", "Colorado"),
+ ("CT", "Connecticut"),
+ ("DC", "District of Columbia"),
+ ("DE", "Delaware"),
+ ("FL", "Florida"),
+ ("GA", "Georgia"),
+ ("GU", "Guam"),
+ ("HI", "Hawaii"),
+ ("IA", "Iowa"),
+ ("ID", "Idaho"),
+ ("IL", "Illinois"),
+ ("IN", "Indiana"),
+ ("KS", "Kansas"),
+ ("KY", "Kentucky"),
+ ("LA", "Louisiana"),
+ ("MA", "Massachusetts"),
+ ("MD", "Maryland"),
+ ("ME", "Maine"),
+ ("MI", "Michigan"),
+ ("MN", "Minnesota"),
+ ("MO", "Missouri"),
+ ("MS", "Mississippi"),
+ ("MT", "Montana"),
+ ("NC", "North Carolina"),
+ ("ND", "North Dakota"),
+ ("NE", "Nebraska"),
+ ("NH", "New Hampshire"),
+ ("NJ", "New Jersey"),
+ ("NM", "New Mexico"),
+ ("NV", "Nevada"),
+ ("NY", "New York"),
+ ("OH", "Ohio"),
+ ("OK", "Oklahoma"),
+ ("OR", "Oregon"),
+ ("PA", "Pennsylvania"),
+ ("PR", "Puerto Rico"),
+ ("RI", "Rhode Island"),
+ ("SC", "South Carolina"),
+ ("SD", "South Dakota"),
+ ("TN", "Tennessee"),
+ ("TX", "Texas"),
+ ("UT", "Utah"),
+ ("VA", "Virginia"),
+ ("VI", "Virgin Islands"),
+ ("VT", "Vermont"),
+ ("WA", "Washington"),
+ ("WI", "Wisconsin"),
+ ("WV", "West Virginia"),
+ ("WY", "Wyoming"),
+ ],
+ max_length=3,
+ null=True,
+ ),
+ ),
],
options={
- 'ordering': ['code'],
+ "ordering": ["code"],
},
),
migrations.CreateModel(
- name='WasteLine',
+ name="WasteLine",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('dot_hazardous', models.BooleanField(verbose_name='DOT hazardous')),
- ('dot_info', models.JSONField(blank=True, null=True, verbose_name='DOT information')),
- ('quantity', models.JSONField(blank=True, null=True)),
- ('hazardous_waste', models.JSONField(blank=True, null=True)),
- ('line_number', models.PositiveIntegerField(verbose_name='waste line number')),
- ('br', models.BooleanField(verbose_name='BR info provided')),
- ('br_info', models.JSONField(blank=True, null=True, verbose_name='BR information')),
- ('management_method', models.JSONField(blank=True, null=True, verbose_name='management method code')),
- ('pcb', models.BooleanField(verbose_name='contains PCBs')),
- ('pcb_infos', models.JSONField(blank=True, null=True, verbose_name='PCB information')),
- ('discrepancy_info', models.JSONField(blank=True, null=True, verbose_name='discrepancy-residue information')),
- ('epa_waste', models.BooleanField(verbose_name='EPA waste')),
- ('additional_info', models.JSONField(blank=True, null=True)),
- ('manifest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wastes', to='trak.manifest')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("dot_hazardous", models.BooleanField(verbose_name="DOT hazardous")),
+ (
+ "dot_info",
+ models.JSONField(blank=True, null=True, verbose_name="DOT information"),
+ ),
+ ("quantity", models.JSONField(blank=True, null=True)),
+ ("hazardous_waste", models.JSONField(blank=True, null=True)),
+ ("line_number", models.PositiveIntegerField(verbose_name="waste line number")),
+ ("br", models.BooleanField(verbose_name="BR info provided")),
+ (
+ "br_info",
+ models.JSONField(blank=True, null=True, verbose_name="BR information"),
+ ),
+ (
+ "management_method",
+ models.JSONField(blank=True, null=True, verbose_name="management method code"),
+ ),
+ ("pcb", models.BooleanField(verbose_name="contains PCBs")),
+ (
+ "pcb_infos",
+ models.JSONField(blank=True, null=True, verbose_name="PCB information"),
+ ),
+ (
+ "discrepancy_info",
+ models.JSONField(
+ blank=True, null=True, verbose_name="discrepancy-residue information"
+ ),
+ ),
+ ("epa_waste", models.BooleanField(verbose_name="EPA waste")),
+ ("additional_info", models.JSONField(blank=True, null=True)),
+ (
+ "manifest",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="wastes",
+ to="trak.manifest",
+ ),
+ ),
],
options={
- 'ordering': ['manifest__mtn', 'line_number'],
+ "ordering": ["manifest__mtn", "line_number"],
},
),
migrations.CreateModel(
- name='Signer',
+ name="Signer",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('rcra_user_id', models.CharField(blank=True, max_length=100, null=True)),
- ('first_name', models.CharField(blank=True, max_length=38, null=True)),
- ('middle_initial', models.CharField(blank=True, max_length=1, null=True)),
- ('last_name', models.CharField(blank=True, max_length=38, null=True)),
- ('email', models.CharField(blank=True, max_length=38, null=True)),
- ('company_name', models.CharField(blank=True, max_length=80, null=True)),
- ('contact_type', models.CharField(blank=True, choices=[('email', 'Email'), ('voice', 'Voice'), ('text', 'Text')], max_length=5, null=True)),
- ('signer_role', models.CharField(choices=[('Industry', 'Industry'), ('PPC', 'Ppc'), ('EPA', 'Epa'), ('State', 'State')], max_length=10, null=True)),
- ('phone', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='trak.manifestphone')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("rcra_user_id", models.CharField(blank=True, max_length=100, null=True)),
+ ("first_name", models.CharField(blank=True, max_length=38, null=True)),
+ ("middle_initial", models.CharField(blank=True, max_length=1, null=True)),
+ ("last_name", models.CharField(blank=True, max_length=38, null=True)),
+ ("email", models.CharField(blank=True, max_length=38, null=True)),
+ ("company_name", models.CharField(blank=True, max_length=80, null=True)),
+ (
+ "contact_type",
+ models.CharField(
+ blank=True,
+ choices=[("email", "Email"), ("voice", "Voice"), ("text", "Text")],
+ max_length=5,
+ null=True,
+ ),
+ ),
+ (
+ "signer_role",
+ models.CharField(
+ choices=[
+ ("Industry", "Industry"),
+ ("PPC", "Ppc"),
+ ("EPA", "Epa"),
+ ("State", "State"),
+ ],
+ max_length=10,
+ null=True,
+ ),
+ ),
+ (
+ "phone",
+ models.ForeignKey(
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="trak.manifestphone",
+ ),
+ ),
],
options={
- 'ordering': ['first_name'],
+ "ordering": ["first_name"],
},
),
migrations.CreateModel(
- name='ImportInfo',
+ name="ImportInfo",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('import_generator', models.JSONField(blank=True, null=True)),
- ('port_of_entry', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='trak.portofentry')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("import_generator", models.JSONField(blank=True, null=True)),
+ (
+ "port_of_entry",
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ to="trak.portofentry",
+ ),
+ ),
],
options={
- 'verbose_name': 'Import Info',
- 'verbose_name_plural': 'Import Info',
+ "verbose_name": "Import Info",
+ "verbose_name_plural": "Import Info",
},
),
migrations.AddField(
- model_name='handler',
- name='paper_signature',
- field=models.OneToOneField(blank=True, help_text='The signature associated with hazardous waste custody exchange', null=True, on_delete=django.db.models.deletion.CASCADE, to='trak.papersignature'),
+ model_name="handler",
+ name="paper_signature",
+ field=models.OneToOneField(
+ blank=True,
+ help_text="The signature associated with hazardous waste custody exchange",
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="trak.papersignature",
+ ),
),
migrations.AddField(
- model_name='handler',
- name='rcra_site',
- field=models.ForeignKey(help_text='Hazardous waste rcra_site associated with the manifest', on_delete=django.db.models.deletion.CASCADE, to='sites.rcrasite'),
+ model_name="handler",
+ name="rcra_site",
+ field=models.ForeignKey(
+ help_text="Hazardous waste rcra_site associated with the manifest",
+ on_delete=django.db.models.deletion.CASCADE,
+ to="site.rcrasite",
+ ),
),
migrations.CreateModel(
- name='ESignature',
+ name="ESignature",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('sign_date', models.DateTimeField(blank=True, null=True)),
- ('cromerr_activity_id', models.CharField(blank=True, max_length=100, null=True)),
- ('cromerr_document_id', models.CharField(blank=True, max_length=100, null=True)),
- ('on_behalf', models.BooleanField(blank=True, default=False, null=True)),
- ('manifest_handler', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='e_signatures', to='trak.handler')),
- ('signer', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='trak.signer')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("sign_date", models.DateTimeField(blank=True, null=True)),
+ ("cromerr_activity_id", models.CharField(blank=True, max_length=100, null=True)),
+ ("cromerr_document_id", models.CharField(blank=True, max_length=100, null=True)),
+ ("on_behalf", models.BooleanField(blank=True, default=False, null=True)),
+ (
+ "manifest_handler",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="e_signatures",
+ to="trak.handler",
+ ),
+ ),
+ (
+ "signer",
+ models.OneToOneField(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="trak.signer",
+ ),
+ ),
],
options={
- 'verbose_name': 'e-Signature',
- 'ordering': ['sign_date'],
+ "verbose_name": "e-Signature",
+ "ordering": ["sign_date"],
},
),
migrations.CreateModel(
- name='CorrectionInfo',
+ name="CorrectionInfo",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('version_number', models.IntegerField(blank=True, null=True)),
- ('active', models.BooleanField(blank=True, null=True)),
- ('ppc_active', models.BooleanField(blank=True, null=True)),
- ('epa_site_id', models.CharField(blank=True, max_length=100, null=True)),
- ('initiator_role', models.CharField(blank=True, choices=[('IN', 'Industry'), ('PP', 'Ppc'), ('EP', 'Epa'), ('ST', 'State')], max_length=2, null=True)),
- ('update_role', models.CharField(blank=True, choices=[('IN', 'Industry'), ('PP', 'Ppc'), ('EP', 'Epa'), ('ST', 'State')], max_length=2, null=True)),
- ('electronic_signature_info', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='trak.esignature')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+ ),
+ ),
+ ("version_number", models.IntegerField(blank=True, null=True)),
+ ("active", models.BooleanField(blank=True, null=True)),
+ ("ppc_active", models.BooleanField(blank=True, null=True)),
+ ("epa_site_id", models.CharField(blank=True, max_length=100, null=True)),
+ (
+ "initiator_role",
+ models.CharField(
+ blank=True,
+ choices=[
+ ("IN", "Industry"),
+ ("PP", "Ppc"),
+ ("EP", "Epa"),
+ ("ST", "State"),
+ ],
+ max_length=2,
+ null=True,
+ ),
+ ),
+ (
+ "update_role",
+ models.CharField(
+ blank=True,
+ choices=[
+ ("IN", "Industry"),
+ ("PP", "Ppc"),
+ ("EP", "Epa"),
+ ("ST", "State"),
+ ],
+ max_length=2,
+ null=True,
+ ),
+ ),
+ (
+ "electronic_signature_info",
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.PROTECT,
+ to="trak.esignature",
+ ),
+ ),
],
options={
- 'verbose_name': 'Correction Info',
- 'verbose_name_plural': 'Correction Info',
+ "verbose_name": "Correction Info",
+ "verbose_name_plural": "Correction Info",
},
),
migrations.CreateModel(
- name='Transporter',
+ name="Transporter",
fields=[
- ('handler_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='trak.handler')),
- ('order', models.PositiveIntegerField()),
- ('manifest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transporters', to='trak.manifest')),
+ (
+ "handler_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="trak.handler",
+ ),
+ ),
+ ("order", models.PositiveIntegerField()),
+ (
+ "manifest",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="transporters",
+ to="trak.manifest",
+ ),
+ ),
],
options={
- 'ordering': ['manifest__mtn'],
+ "ordering": ["manifest__mtn"],
},
- bases=('trak.handler',),
+ bases=("trak.handler",),
),
]
diff --git a/server/apps/trak/models/handler_models.py b/server/apps/trak/models/handler_models.py
index 5a0537084..b998f8948 100644
--- a/server/apps/trak/models/handler_models.py
+++ b/server/apps/trak/models/handler_models.py
@@ -4,7 +4,7 @@
from django.core.exceptions import ValidationError
from django.db import models
-from apps.sites.models import RcraSite
+from apps.site.models import RcraSite
from . import ManifestPhone
from .base_models import TrakBaseManager, TrakBaseModel
@@ -60,7 +60,7 @@ class Meta:
objects = HandlerManager()
rcra_site = models.ForeignKey(
- "sites.RcraSite",
+ "site.RcraSite",
on_delete=models.CASCADE,
help_text="Hazardous waste rcra_site associated with the manifest",
)
diff --git a/server/apps/trak/models/manifest_models.py b/server/apps/trak/models/manifest_models.py
index 606fb9c27..e8d68a7eb 100644
--- a/server/apps/trak/models/manifest_models.py
+++ b/server/apps/trak/models/manifest_models.py
@@ -7,7 +7,7 @@
from django.db.models import Q, QuerySet
from django.utils.translation import gettext_lazy as _
-from apps.sites.models import RcraSiteType, RcraStates, Role
+from apps.site.models import RcraSiteType, RcraStates, Role
from apps.trak.models import Handler, Transporter
from .base_models import TrakBaseManager, TrakBaseModel
diff --git a/server/apps/trak/serializers/handler_serializer.py b/server/apps/trak/serializers/handler_serializer.py
index a5522746f..eaa3d762f 100644
--- a/server/apps/trak/serializers/handler_serializer.py
+++ b/server/apps/trak/serializers/handler_serializer.py
@@ -3,7 +3,7 @@
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
-from apps.sites.serializers import RcraSiteSerializer
+from apps.site.serializers import RcraSiteSerializer
from apps.trak.models import Handler, ManifestPhone, Transporter
from .signature_serializer import ESignatureSerializer, PaperSignatureSerializer
diff --git a/server/apps/trak/serializers/manifest_serializer.py b/server/apps/trak/serializers/manifest_serializer.py
index 0b87aafe7..3968b0341 100644
--- a/server/apps/trak/serializers/manifest_serializer.py
+++ b/server/apps/trak/serializers/manifest_serializer.py
@@ -3,7 +3,7 @@
from rest_framework import serializers
-from apps.sites.models import RcraStates, Role
+from apps.site.models import RcraStates, Role
from apps.trak.models import Manifest
from apps.trak.models.manifest_models import (
AdditionalInfo,
diff --git a/server/apps/trak/serializers/signature_serializer.py b/server/apps/trak/serializers/signature_serializer.py
index af9c0a7a2..dac273ac7 100644
--- a/server/apps/trak/serializers/signature_serializer.py
+++ b/server/apps/trak/serializers/signature_serializer.py
@@ -4,7 +4,7 @@
from rest_framework import serializers
-from apps.sites.serializers.contact_serializer import RcraPhoneSerializer
+from apps.site.serializers.contact_serializer import RcraPhoneSerializer
from apps.trak.models import ESignature, PaperSignature, QuickerSign, Signer
from .base_serializer import TrakBaseSerializer
diff --git a/server/apps/trak/services/manifest_services.py b/server/apps/trak/services/manifest_services.py
index 49a3276c5..3786f29f9 100644
--- a/server/apps/trak/services/manifest_services.py
+++ b/server/apps/trak/services/manifest_services.py
@@ -4,7 +4,7 @@
from django.db import transaction
from django.db.models import Q, QuerySet
-from apps.sites.models import HaztrakSite
+from apps.site.models import HaztrakSite
from apps.trak.models import Manifest
from apps.trak.serializers import ManifestSerializer
from apps.trak.services import EManifest, EManifestError, TaskResponse
diff --git a/server/apps/trak/signals.py b/server/apps/trak/signals.py
index 3e481162e..e9630bae1 100644
--- a/server/apps/trak/signals.py
+++ b/server/apps/trak/signals.py
@@ -2,13 +2,13 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
-from apps.core.models import RcraProfile
+from apps.core.models import RcrainfoProfile
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
- RcraProfile.objects.create(user=instance)
+ RcrainfoProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
diff --git a/server/apps/trak/tasks/manifest_task.py b/server/apps/trak/tasks/manifest_task.py
index 8fa6152c6..929402748 100644
--- a/server/apps/trak/tasks/manifest_task.py
+++ b/server/apps/trak/tasks/manifest_task.py
@@ -57,7 +57,7 @@ def sign_manifest(
@shared_task(name="sync site manifests", bind=True)
def sync_site_manifests(self, *, site_id: str, username: str):
"""asynchronous task to sync an EPA site's manifests"""
- from apps.sites.services import HaztrakSiteService
+ from apps.site.services import HaztrakSiteService
try:
site_service = HaztrakSiteService(username=username)
diff --git a/server/apps/trak/tests/conftest.py b/server/apps/trak/tests/conftest.py
index 7598f0042..e77b0e2bc 100644
--- a/server/apps/trak/tests/conftest.py
+++ b/server/apps/trak/tests/conftest.py
@@ -7,7 +7,7 @@
from faker import Faker
from faker.providers import BaseProvider
-from apps.sites.models import RcraSite, RcraSiteType
+from apps.site.models import RcraSite, RcraSiteType
from apps.trak.models import (
DotLookup,
ESignature,
@@ -38,8 +38,8 @@ def manifest_handler_factory(db, rcra_site_factory, paper_signature_factory):
"""Abstract factory for Haztrak Handler model"""
def create_manifest_handler(
- rcra_site: Optional[RcraSite] = None,
- paper_signature: Optional[PaperSignature] = None,
+ rcra_site: Optional[RcraSite] = None,
+ paper_signature: Optional[PaperSignature] = None,
) -> Handler:
return Handler.objects.create(
rcra_site=rcra_site or rcra_site_factory(),
@@ -54,10 +54,10 @@ def manifest_transporter_factory(db, rcra_site_factory, paper_signature_factory)
"""Abstract factory for Haztrak Handler model"""
def create_manifest_handler(
- rcra_site: Optional[RcraSite] = None,
- paper_signature: Optional[PaperSignature] = None,
- manifest: Manifest = None,
- order: Optional[int] = 1,
+ rcra_site: Optional[RcraSite] = None,
+ paper_signature: Optional[PaperSignature] = None,
+ manifest: Manifest = None,
+ order: Optional[int] = 1,
) -> Transporter:
return Transporter.objects.create(
manifest=manifest,
@@ -74,8 +74,8 @@ def paper_signature_factory(db, faker: Faker):
"""Abstract factory for Paper Signature"""
def create_signature(
- printed_name: Optional[str] = None,
- sign_date: Optional[datetime] = None,
+ printed_name: Optional[str] = None,
+ sign_date: Optional[datetime] = None,
) -> PaperSignature:
return PaperSignature.objects.create(
printed_name=printed_name or faker.name(),
@@ -90,8 +90,8 @@ def e_signature_factory(db, signer_factory, manifest_handler_factory, faker: Fak
"""Abstract factory for Haztrak Handler model"""
def create_e_signature(
- signer: Optional[Signer] = None,
- manifest_handler: Optional[Handler] = None,
+ signer: Optional[Signer] = None,
+ manifest_handler: Optional[Handler] = None,
) -> ESignature:
return ESignature.objects.create(
signer=signer or signer_factory(),
@@ -110,10 +110,10 @@ def waste_code_factory(db):
"""Abstract factory for waste codes"""
def create_waste_code(
- code: Optional[str] = "D001",
- description: Optional[str] = "IGNITABLE WASTE",
- code_type: Optional[WasteCode.CodeType] = WasteCode.CodeType.FEDERAL,
- state_id: Optional[str] = "VA",
+ code: Optional[str] = "D001",
+ description: Optional[str] = "IGNITABLE WASTE",
+ code_type: Optional[WasteCode.CodeType] = WasteCode.CodeType.FEDERAL,
+ state_id: Optional[str] = "VA",
) -> WasteCode:
if code_type == WasteCode.CodeType.STATE:
waste_code = WasteCode.objects.create(
@@ -138,12 +138,12 @@ def signer_factory(db, faker: Faker):
"""Abstract factory for Haztrak Signer model"""
def creat_signer(
- first_name: Optional[str] = None,
- middle_initial: Optional[str] = None,
- last_name: Optional[str] = None,
- signer_role: Optional[str] = "EP",
- company_name: Optional[str] = None,
- rcra_user_id: Optional[str] = None,
+ first_name: Optional[str] = None,
+ middle_initial: Optional[str] = None,
+ last_name: Optional[str] = None,
+ signer_role: Optional[str] = "EP",
+ company_name: Optional[str] = None,
+ rcra_user_id: Optional[str] = None,
) -> Signer:
return Signer.objects.create(
first_name=first_name or faker.first_name(),
@@ -162,10 +162,10 @@ def manifest_factory(db, manifest_handler_factory, rcra_site_factory):
"""Abstract factory for Haztrak Manifest model"""
def create_manifest(
- mtn: Optional[str] = None,
- generator: Optional[Handler] = None,
- tsdf: Optional[Handler] = None,
- status: Optional[str] = None,
+ mtn: Optional[str] = None,
+ generator: Optional[Handler] = None,
+ tsdf: Optional[Handler] = None,
+ status: Optional[str] = None,
) -> Manifest:
fake = Faker()
fake.add_provider(ManifestProvider)
@@ -175,12 +175,11 @@ def create_manifest(
created_date=datetime.now(UTC),
potential_ship_date=datetime.now(UTC),
generator=generator
- or manifest_handler_factory(
+ or manifest_handler_factory(
rcra_site=rcra_site_factory(site_type=RcraSiteType.GENERATOR)
),
tsdf=tsdf
- or manifest_handler_factory(
- rcra_site=rcra_site_factory(site_type=RcraSiteType.TSDF)),
+ or manifest_handler_factory(rcra_site=rcra_site_factory(site_type=RcraSiteType.TSDF)),
)
return create_manifest
@@ -191,13 +190,13 @@ def waste_line_factory(db):
"""Abstract factory for Haztrak DotLookup model"""
def create_waste_line(
- manifest: Manifest = None,
- dot_hazardous: Optional[bool] = True,
- quantity: Optional[dict] = None,
- line_number: Optional[int] = 1,
- br: Optional[bool] = False,
- pcb: Optional[bool] = False,
- epa_waste: Optional[bool] = True,
+ manifest: Manifest = None,
+ dot_hazardous: Optional[bool] = True,
+ quantity: Optional[dict] = None,
+ line_number: Optional[int] = 1,
+ br: Optional[bool] = False,
+ pcb: Optional[bool] = False,
+ epa_waste: Optional[bool] = True,
) -> WasteLine:
return WasteLine.objects.create(
manifest=manifest,
@@ -217,8 +216,8 @@ def dot_option_factory(db, faker: Faker):
"""Abstract factory for Haztrak DotLookup model"""
def create_dot_option(
- value: Optional[str] = None,
- value_type: Optional[DotLookupType] = DotLookupType.ID,
+ value: Optional[str] = None,
+ value_type: Optional[DotLookupType] = DotLookupType.ID,
) -> Manifest:
return DotLookup.objects.create(
value=value or faker.pystr(max_chars=10), value_type=value_type
diff --git a/server/apps/trak/tests/models/test_manifest_models.py b/server/apps/trak/tests/models/test_manifest_models.py
index 6db8351f3..533df5e95 100644
--- a/server/apps/trak/tests/models/test_manifest_models.py
+++ b/server/apps/trak/tests/models/test_manifest_models.py
@@ -4,7 +4,7 @@
from django.core.exceptions import ValidationError
from django.db.models import Q
-from apps.sites.models import RcraSiteType
+from apps.site.models import RcraSiteType
from apps.trak.models import WasteLine
from apps.trak.models.manifest_models import Manifest, draft_mtn, validate_mtn
from apps.trak.serializers import HandlerSerializer, WasteLineSerializer
diff --git a/server/apps/trak/views/manifest_view.py b/server/apps/trak/views/manifest_view.py
index ac43b449b..170b2d8f5 100644
--- a/server/apps/trak/views/manifest_view.py
+++ b/server/apps/trak/views/manifest_view.py
@@ -7,7 +7,7 @@
from rest_framework.response import Response
from rest_framework.views import APIView
-from apps.sites.services import HaztrakSiteService
+from apps.site.services import HaztrakSiteService
from apps.trak.models import Manifest
from apps.trak.serializers import ManifestSerializer
from apps.trak.serializers.signature_serializer import QuickerSignSerializer
diff --git a/server/fixtures/dev_data.yaml b/server/fixtures/dev_data.yaml
index da36978c2..a0f11d259 100644
--- a/server/fixtures/dev_data.yaml
+++ b/server/fixtures/dev_data.yaml
@@ -152,7 +152,7 @@
fields:
user: 4ac96f68-42cf-47ea-bffb-f24d423dbc35
created: 2022-12-18 13:10:40.552000+00:00
-- model: sites.address
+- model: site.address
pk: 1
fields:
street_number: null
@@ -167,12 +167,12 @@
fields:
number: 321-321-3214
extension: null
-- model: sites.rcraphone
+- model: site.rcraphone
pk: 1
fields:
number: 321-321-3214
extension: null
-- model: sites.contact
+- model: site.contact
pk: 1
fields:
first_name: David
@@ -181,7 +181,7 @@
phone: 1
email: testuser1@haztrak.net
company_name: haztrak
-- model: sites.rcrasite
+- model: site.rcrasite
pk: 1
fields:
site_type: Generator
@@ -197,7 +197,7 @@
can_esign: true
limited_esign: true
registered_emanifest_user: true
-- model: sites.rcrasite
+- model: site.rcrasite
pk: 2
fields:
site_type: Transporter
@@ -213,7 +213,7 @@
can_esign: true
limited_esign: true
registered_emanifest_user: true
-- model: sites.rcrasite
+- model: site.rcrasite
pk: 3
fields:
site_type: Transporter
@@ -229,7 +229,7 @@
can_esign: true
limited_esign: true
registered_emanifest_user: true
-- model: sites.rcrasite
+- model: site.rcrasite
pk: 4
fields:
site_type: Tsdf
@@ -245,25 +245,25 @@
can_esign: true
limited_esign: true
registered_emanifest_user: true
-- model: core.rcraprofile
+- model: core.rcrainfoprofile
pk: d74f904a-7843-4a60-862c-3b94b6051359
fields:
rcra_username: dpgraham4401
phone_number: null
email: orgadmin@generator.com
-- model: core.rcraprofile
+- model: core.rcrainfoprofile
pk: 1fd27bec-8743-4eb3-a44c-fd063ea62021
fields:
rcra_username: ''
phone_number: null
email: superadmin@gmail.com
-- model: core.rcraprofile
+- model: core.rcrainfoprofile
pk: 192c73f4-24f1-4f21-8239-dee2da43c547
fields:
rcra_username: emanifestpyuser1
phone_number: null
email: testuser1@haztrak.net
-- model: sites.haztrakorg
+- model: site.haztrakorg
pk: efb9e104-7f61-4365-a9af-9d7b55c854c4
fields:
name: Generators Org LLC
@@ -286,20 +286,20 @@
user: 4ac96f68-42cf-47ea-bffb-f24d423dbc35
rcrainfo_profile: 192c73f4-24f1-4f21-8239-dee2da43c547
org: efb9e104-7f61-4365-a9af-9d7b55c854c4
-- model: sites.haztraksite
+- model: site.haztraksite
pk: 1
fields:
name: VA TEST GEN 2021
rcra_site: 1
last_rcrainfo_manifest_sync: null
org: efb9e104-7f61-4365-a9af-9d7b55c854c4
-- model: sites.sitepermissions
+- model: site.sitepermissions
pk: 1
fields:
site: 1
emanifest: signer
profile: 87e355dd-1898-4a0a-81c3-1e9ac8473143
-- model: sites.rcrasitepermissions
+- model: site.rcrasitepermissions
pk: 1
fields:
site: 1
diff --git a/server/haztrak/settings/base.py b/server/haztrak/settings/base.py
index 04ec76aad..8d65278a9 100644
--- a/server/haztrak/settings/base.py
+++ b/server/haztrak/settings/base.py
@@ -22,28 +22,34 @@
WSGI_APPLICATION = "haztrak.wsgi.application"
+SITE_ID = 1
+
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
+ "django.contrib.sites",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"rest_framework.authtoken",
- "drf_spectacular",
+ "allauth",
+ "allauth.account",
+ "dj_rest_auth",
"corsheaders",
"django_extensions",
- "django_celery_results",
- "django_celery_beat",
"health_check",
"health_check.db",
"health_check.cache",
"health_check.contrib.celery",
"health_check.contrib.migrations",
"health_check.contrib.redis",
+ "django_celery_results",
+ "django_celery_beat",
+ "drf_spectacular",
"apps.trak",
- "apps.sites",
+ "apps.site",
"apps.core",
]
@@ -54,8 +60,9 @@
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
- "django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "allauth.account.middleware.AccountMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
@@ -101,6 +108,10 @@
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
+AUTHENTICATION_BACKENDS = [
+ "django.contrib.auth.backends.ModelBackend",
+ "allauth.account.auth_backends.AuthenticationBackend",
+]
# Internationalization
LANGUAGE_CODE = "en-us"
@@ -198,7 +209,7 @@
"handlers": ["console"],
"propagate": False,
},
- "apps.sites": {
+ "apps.site": {
"level": HT_TRAK_LOG_LEVEL,
"handlers": ["console"],
"propagate": False,
@@ -210,3 +221,5 @@
},
},
}
+
+REST_AUTH = {"USER_DETAILS_SERIALIZER": "apps.core.serializers.HaztrakUserSerializer"}
diff --git a/server/haztrak/urls.py b/server/haztrak/urls.py
index 00e272c82..3d29f2f04 100644
--- a/server/haztrak/urls.py
+++ b/server/haztrak/urls.py
@@ -29,7 +29,7 @@
[
path("", include("apps.core.urls")),
path("", include("apps.trak.urls")),
- path("", include("apps.sites.urls")),
+ path("", include("apps.site.urls")),
path("schema/", SpectacularAPIView.as_view(), name="schema"),
path(
"schema/swagger-ui",
diff --git a/server/requirements.txt b/server/requirements.txt
index 2fb600263..3cc759aa0 100644
--- a/server/requirements.txt
+++ b/server/requirements.txt
@@ -16,3 +16,5 @@ celery==5.3.6
redis==5.0.1
drf-spectacular==0.26.5
django-health-check==3.17.0
+django-allauth==0.59.0
+dj-rest-auth==5.0.2