Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .github/workflows/frontend_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ on:
- ".github/workflows/frontend_build.yml"

jobs:
frontend-unit-test:
uses: hotosm/gh-workflows/.github/workflows/test_pnpm.yml@3.2.0
with:
working_dir: frontend
Build_On_Ubuntu:
runs-on: ubuntu-latest
needs: [frontend-unit-test]
env:
CI: false

Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/frontend_build_push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ permissions:
contents: read

jobs:
frontend-unit-test:
uses: hotosm/gh-workflows/.github/workflows/test_pnpm.yml@3.2.0

with:
working_dir: frontend
build_and_upload:
runs-on: ubuntu-latest
needs: [frontend-unit-test]
environment: Production

env:
Expand Down
23 changes: 22 additions & 1 deletion docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ services:
platform: linux/amd64
container_name: api
restart: always
command: python manage.py runserver_with_q 0.0.0.0:8000
command: gunicorn --bind 0.0.0.0:8000 --workers 3 aiproject.wsgi:application
ports:
- "8000:8000"
volumes:
Expand All @@ -73,6 +73,27 @@ services:
interval: 30s
timeout: 10s
retries: 2

backend-api-django-q:
<<: *common-env
image: ghcr.io/hotosm/fair-api:${TAG:-latest}
platform: linux/amd64
container_name: api-django-q
restart: always
command: python manage.py qcluster
volumes:
- ${APP_LOGS:-/opt/fair-app/data/log}:/app/log
- ${RAMP_HOME:-/opt/fair-app/data/ramp}:/RAMP_HOME
- ${TRAINING_WORKSPACE:-/opt/fair-app/data/trainings}:/TRAINING_WORKSPACE
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
backend-api:
condition: service_healthy
networks:
- backend-network
deploy:
resources:
limits:
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/app/providers/auth-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ const AuthContext = createContext<TAuthContext>({
token: "",
user: {} as TUser,
authenticateUser: async () => Promise.resolve(),
logout: () => {},
logout: () => { },
isAuthenticated: false,
setUser: () => {},
setUser: () => { },
});

export const useAuth = () => {
Expand Down Expand Up @@ -91,7 +91,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const params = new URLSearchParams(window.location.search);
const code = params.get("code");
const state = params.get("state");
if (code && state && user === null) {
if (code && state && user === undefined) {
authenticateUser(state, code);
}
}, [user]);
Expand Down
1 change: 1 addition & 0 deletions frontend/src/app/routes/datasets/dataset-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export const TrainingDatasetsDetailPage = () => {
<ModelExplorer
title="Models Using this Dataset"
datasetId={data.id}
disableStatusFilter
/>
</div>
</div>
Expand Down
16 changes: 10 additions & 6 deletions frontend/src/components/shared/model-explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ export const ModelExplorer = ({
createRoute,
createButtonAlt,
userId,
datasetId,
datasetId, disableStatusFilter
}: {
disableCreateNewButton?: boolean;
title?: string;
createRoute?: string;
createButtonAlt?: string;
userId?: number;
datasetId?: number;
disableStatusFilter?: boolean;
}) => {
const { isOpened, openDialog, closeDialog } = useDialog();

Expand Down Expand Up @@ -121,11 +122,14 @@ export const ModelExplorer = ({
}
/>
<CategoryFilter disabled={isPending} />
<StatusFilter
disabled={isPending}
updateQuery={updateQuery}
query={query}
/>
{
disableStatusFilter ? null :
<StatusFilter
disabled={isPending}
updateQuery={updateQuery}
query={query}
/>
}
{/* Mobile filters */}
<div className="flex md:hidden items-center gap-x-4">
<MobileFilter openMobileFilterModal={openDialog} />
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/enums/start-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ export enum PredictedFeatureStatus {
REJECTED = "rejected",
UNTOUCHED = "untouched",
}


export enum FeedbackType {
ACCEPT = "ACCEPT",
REJECT = "REJECT"
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const FeedbacksLayer = ({
properties: {
...feature.properties,
comment_length:
feature?.properties && "comments" in feature.properties
feature?.properties && "comments" in feature.properties && feature.properties.comments
? feature.properties.comments.length
: 0,
},
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/features/start-mapping/api/create-feedbacks.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FeedbackType } from "@/enums/start-mapping";
import { API_ENDPOINTS, apiClient } from "@/services";
import { Feature, TModelPredictionFeature } from "@/types";

Expand Down Expand Up @@ -26,6 +27,7 @@ export const createFeedback = async ({
source_imagery,
zoom_level,
training,
action: FeedbackType.REJECT
})
).data;
};
Expand Down Expand Up @@ -53,11 +55,12 @@ export const createApprovedPrediction = async ({
user,
}: TCreateApprovedPredictionPayload): Promise<TModelPredictionFeature> => {
return await (
await apiClient.post(API_ENDPOINTS.CREATE_APPROVED_PREDICTION, {
await apiClient.post(API_ENDPOINTS.CREATE_FEEDBACK, {
config,
training,
geom,
user,
action: FeedbackType.ACCEPT
})
).data;
};
Expand All @@ -84,6 +87,6 @@ export const deleteApprovedModelPrediction = async ({
id,
}: TDeleteApprovedModelPredictionPayload): Promise<TModelPredictionFeature> => {
return await (
await apiClient.delete(API_ENDPOINTS.DELETE_APPROVED_PREDICTION(id))
await apiClient.delete(API_ENDPOINTS.DELETE_FEEDBACK(id))
).data;
};
12 changes: 12 additions & 0 deletions frontend/src/hooks/__tests__/use-history.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ import { vi, describe, it, expect } from "vitest";

vi.mock("react-router-dom", () => ({
useNavigate: vi.fn(),
useLocation: vi.fn(
() => ({
pathname: "/test",
search: "",
hash: "",
state: null,
key: "testKey",
}
),),
useParams: vi.fn(
() => ({ id: "123" }) // Mocking useParams to return a sample id
),
}));

describe("useHistory", () => {
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/services/api-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ export const API_ENDPOINTS = {

CREATE_FEEDBACK: "feedback/",
DELETE_FEEDBACK: (id: number) => `feedback/${id}/`,
CREATE_APPROVED_PREDICTION: "approved-prediction/",
DELETE_APPROVED_PREDICTION: (id: number) => `approved-prediction/${id}/`,

// KPIs

Expand Down Expand Up @@ -66,7 +64,7 @@ export const API_ENDPOINTS = {
GET_TRAINING_AREAS: (datasetId: number, offset: number, limit: number) =>
`aoi/?dataset=${datasetId}&offset=${offset}&limit=${limit}`,
GET_TRAINING_AREA: (aoiId: number) => `aoi/${aoiId}/`,
GET_TRAINING_STATUS: (taskId: string) => `training/status/${taskId}`,
GET_TRAINING_STATUS: (taskId: string) => `task/status/${taskId}`,
GET_TRAINING_HISTORY: "training/",
GET_TRAINING_FEEDBACKS: (trainingId: number) =>
`feedback/?training=${trainingId}`,
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/utils/__tests__/geo/geometry-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ describe("handleConflation", () => {
expect(result[0].properties.id).toBe("rejected-id");
});

it("should allow overlapping new features", () => {
it("should not allow overlapping new features", () => {
const existingFeatures: TModelPredictionFeature[] = [];

const newFeatures: Feature[] = [
Expand Down Expand Up @@ -299,7 +299,7 @@ describe("handleConflation", () => {
newFeatures,
predictionConfig,
);
expect(result.length).toBe(2);
expect(result.length).toBe(1);
expect(result.every((f) => f.properties.status === "untouched")).toBe(true);
});
});
Expand Down