Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MDS-6118] FE extract permits - DRAFT #3236

Merged
merged 13 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ generate_history_table_migration:
@echo "+\n++ Generating history table migration ...\n+"
@docker compose $(DC_FILE) exec backend bash -c "flask generate_history_table_migration $(TABLE)"


# Generates a migration file for the specified table
# Usage: make generate_migration TABLE=<table_name>
generate_table_migration:
@echo "+\n++ Generating history table migration ...\n+"
@docker compose $(DC_FILE) exec backend bash -c "flask generate_table_migration $(TABLE)"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a little script to auto-generate a CREATE TABLE migration for new tables.
Pretty much the same as the history table creation scripts, just with creating the actual table instead of a _version table

# initial project setup for local/codespaces development
init:
@./bin/setup_codespaces.sh
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- This file was generated by the generate_table_ddl command
-- The file contains the corresponding history table definition for the permit_extraction_task table
CREATE TABLE permit_extraction_task (
create_user VARCHAR(60) NOT NULL,
create_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,
update_user VARCHAR(60) NOT NULL,
update_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,
permit_extraction_task_id UUID NOT NULL,
task_id VARCHAR(255) NOT NULL,
task_status VARCHAR(255) NOT NULL,
task_meta JSON,
task_result JSON,
core_status_task_id VARCHAR(255),
permit_amendment_guid UUID NOT NULL,
permit_amendment_document_guid UUID NOT NULL,
PRIMARY KEY (permit_extraction_task_id),
FOREIGN KEY(permit_amendment_guid) REFERENCES permit_amendment (permit_amendment_guid),
FOREIGN KEY(permit_amendment_document_guid) REFERENCES permit_amendment_document (permit_amendment_document_guid)
);
CREATE INDEX IF NOT EXISTS permit_extraction_task_id_idx ON permit_extraction_task (task_id);
CREATE INDEX IF NOT EXISTS permit_extraction_amend_guid_idx ON permit_extraction_task (permit_amendment_guid);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE permit_conditions ADD COLUMN step VARCHAR(50);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE permit_conditions_version ADD COLUMN step VARCHAR(50);
4 changes: 4 additions & 0 deletions services/common/src/constants/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ export const STANDARD_PERMIT_CONDITIONS = (noticeOfWorkType) =>
export const STANDARD_PERMIT_CONDITION = (permitConditionGuid) =>
`/mines/permits/standard-conditions/${permitConditionGuid}`;

export const PERMIT_SERVICE_EXTRACTION = `/mines/permits/condition-extraction`;
export const POLL_PERMIT_SERVICE_EXTRACTION = (taskId: string) =>
`/mines/permits/condition-extraction/${taskId}`;

// Permits - Notices of Departure
export const NOTICES_OF_DEPARTURE = () => `/notices-of-departure`;
export const NOTICE_OF_DEPARTURE = (noticeOfDepartureGuid) =>
Expand Down
2 changes: 2 additions & 0 deletions services/common/src/redux/reducers/rootReducerShared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import verifiableCredentialsReducer from "@mds/common/redux/slices/verifiableCre
import regionsReducer from "@mds/common/redux/slices/regionsSlice";
import complianceCodeReducer, { complianceCodeReducerType } from "../slices/complianceCodesSlice";
import spatialDataReducer, { spatialDataReducerType } from "../slices/spatialDataSlice";
import permitServiceReducer, { permitServiceReducerType } from "../slices/permitServiceSlice";
export const sharedReducer = {
...activityReducer,
...authenticationReducer,
Expand Down Expand Up @@ -82,4 +83,5 @@ export const sharedReducer = {
regions: regionsReducer,
[spatialDataReducerType]: spatialDataReducer,
[complianceCodeReducerType]: complianceCodeReducer,
[permitServiceReducerType]: permitServiceReducer,
};
203 changes: 203 additions & 0 deletions services/common/src/redux/slices/permitServiceSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { createAppSlice, rejectHandler } from "@mds/common/redux/createAppSlice";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taraepp Put in actual implementations here for the

  • Fetch Status
  • Delete permit conditions
    actions

import { hideLoading, showLoading } from "react-redux-loading-bar";
import CustomAxios from "@mds/common/redux/customAxios";
import { ItemMap } from "@mds/common/interfaces";
import {
ENVIRONMENT,
PERMIT_SERVICE_EXTRACTION,
POLL_PERMIT_SERVICE_EXTRACTION,
} from "@mds/common/constants";
import { createSelector } from "@reduxjs/toolkit";

const createRequestHeader = REQUEST_HEADER.createRequestHeader;

export const permitServiceReducerType = "permitService";

export enum PermitExtractionStatus {
not_started = "Not Started",
in_progress = "In Progress",
complete = "Extraction Complete",
error = "Error Extracting",
}

const permitExtractionStatusMap = {
PENDING: PermitExtractionStatus.in_progress,
RECEIVED: PermitExtractionStatus.in_progress,
PROGRESS: PermitExtractionStatus.in_progress,
RETRY: PermitExtractionStatus.in_progress,
STARTED: PermitExtractionStatus.in_progress,
REVOKED: PermitExtractionStatus.error,
FAILURE: PermitExtractionStatus.error,
SUCCESS: PermitExtractionStatus.complete,
};

interface PermitExtraction {
task_status: PermitExtractionStatus;
task_id: string;
}

interface PermitServiceState {
// object of: permit_amendment_id: {status: x, task_id: y}
extractions: ItemMap<PermitExtraction>;
}

const initialState: PermitServiceState = {
extractions: {},
};

const permitServiceSlice = createAppSlice({
name: permitServiceReducerType,
initialState,
reducers: (create) => ({
initiatePermitExtraction: create.asyncThunk(
async (
payload: { permit_amendment_id: number; permit_amendment_document_guid: string },
thunkAPI
) => {
const headers = createRequestHeader();
thunkAPI.dispatch(showLoading());

const response = await CustomAxios({
errorToastMessage: "default",
}).post(`${ENVIRONMENT.apiUrl}${PERMIT_SERVICE_EXTRACTION}`, payload, headers);
thunkAPI.dispatch(hideLoading());

return response.data;
},
{
fulfilled: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
const { task_id, task_status } = action.payload;
state.extractions[permit_amendment_id] = {
task_id,
task_status: permitExtractionStatusMap[task_status],
};
},
pending: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
state.extractions[permit_amendment_id] = {
task_status: PermitExtractionStatus.in_progress,
task_id: null,
};
},
rejected: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
state.extractions[permit_amendment_id] = {
task_status: PermitExtractionStatus.error,
task_id: null,
};
rejectHandler(action);
},
}
),
fetchPermitExtractionTasks: create.asyncThunk(
async (payload: { permit_amendment_id: number }, thunkAPI) => {
const { permit_amendment_id } = payload;
const headers = createRequestHeader();
thunkAPI.dispatch(showLoading());

const response = await CustomAxios({
errorToastMessage: "default",
}).get(
`${ENVIRONMENT.apiUrl}${PERMIT_SERVICE_EXTRACTION}?permit_amendment_id=${permit_amendment_id}`,
headers
);

thunkAPI.dispatch(hideLoading());
return response.data.tasks[0];
},
{
fulfilled: (state, action) => {
if (!action.payload) return;
const { permit_amendment_id } = action.meta.arg;
const { task_id, task_status } = action.payload;
state.extractions[permit_amendment_id] = {
task_id: task_id,
task_status: permitExtractionStatusMap[task_status],
};
},
rejected: (state, action) => {
rejectHandler(action);
},
}
),

fetchPermitExtractionStatus: create.asyncThunk(
async (payload: { permit_amendment_id: number; task_id: string }, thunkAPI) => {
const { permit_amendment_id, task_id } = payload;

const headers = createRequestHeader();
thunkAPI.dispatch(showLoading());

const response = await CustomAxios({
errorToastMessage: "default",
}).get(`${ENVIRONMENT.apiUrl}${POLL_PERMIT_SERVICE_EXTRACTION(task_id)}`, headers);

thunkAPI.dispatch(hideLoading());
return response.data;
},
{
fulfilled: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
const { task_id, task_status } = action.payload;
state.extractions[permit_amendment_id] = {
task_id: task_id,
task_status: permitExtractionStatusMap[task_status],
};
},
rejected: (state, action) => {
rejectHandler(action);
},
}
),
deletePermitConditions: create.asyncThunk(
async (payload: { permit_amendment_id: number }, thunkAPI) => {
const headers = createRequestHeader();
thunkAPI.dispatch(showLoading());
const { permit_amendment_id } = payload;
const response = await CustomAxios({
errorToastMessage: "default",
}).delete(
`${ENVIRONMENT.apiUrl}${PERMIT_SERVICE_EXTRACTION}?permit_amendment_id=${permit_amendment_id}`,
headers
);

thunkAPI.dispatch(hideLoading());
return response.data;
},
{
fulfilled: (state, action) => {
const { permit_amendment_id } = action.meta.arg;
state.extractions[permit_amendment_id] = {
task_status: PermitExtractionStatus.not_started,
task_id: null,
};
},
rejected: (state, action) => {
rejectHandler(action);
},
}
),
}),
selectors: {
getPermitExtractionState: (state: PermitServiceState) => {
return state.extractions;
},
},
});

export const { getPermitExtractionState } = permitServiceSlice.selectors;
export const {
initiatePermitExtraction,
fetchPermitExtractionStatus,
fetchPermitExtractionTasks,
deletePermitConditions,
} = permitServiceSlice.actions;

export const getPermitExtractionByGuid = (permit_amendment_id: number) =>
createSelector([getPermitExtractionState], (extractions) => {
return extractions[permit_amendment_id];
});

const permitServiceReducer = permitServiceSlice.reducer;
export default permitServiceReducer;
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import uuid
from flask_restx import Resource, fields

from app.extensions import api, cache
from app.api.utils.access_decorators import requires_any_of, VIEW_ALL, MINESPACE_PROPONENT, GIS
from app.api.constants import DOWNLOAD_TOKEN, TIMEOUT_5_MINUTES
from app.api.services.document_manager_service import DocumentManagerService
from app.api.utils.access_decorators import (
GIS,
MINESPACE_PROPONENT,
VIEW_ALL,
requires_any_of,
)
from app.extensions import api
from flask_restx import Resource, fields

DOWNLOAD_TOKEN_MODEL = api.model('DownloadToken', {'token_guid': fields.String})

Expand All @@ -13,6 +17,4 @@ class DownloadTokenResource(Resource):
@api.marshal_with(DOWNLOAD_TOKEN_MODEL, code=200)
@requires_any_of([VIEW_ALL, MINESPACE_PROPONENT, GIS])
def get(self, document_guid):
token_guid = uuid.uuid4()
cache.set(DOWNLOAD_TOKEN(token_guid), document_guid, TIMEOUT_5_MINUTES)
return {'token_guid': token_guid}
return {'token_guid': DocumentManagerService.create_download_token(document_guid)}
Loading
Loading