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

Feature - Project Details Mapper UI #861

Merged
merged 74 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
d9f0c8f
fix (create new project): create new project url updated
NSUWAL123 Sep 26, 2023
baca147
fix (create new project): upload area - select a file issue solved
NSUWAL123 Sep 26, 2023
9dd336a
fix (create new project): data extract - select a file issue solved
NSUWAL123 Sep 26, 2023
887689b
fix (radiobutton): option selection on label click, UI fix
NSUWAL123 Sep 26, 2023
287cf7d
fix (create new project): initial values for project details form added
NSUWAL123 Sep 26, 2023
e5f6caa
feat (select): custom select component added
NSUWAL123 Sep 26, 2023
4fd5dae
fix (create new project): data extract - shadcn select replaced with …
NSUWAL123 Sep 26, 2023
b14952c
fix (custom select): updated props
NSUWAL123 Sep 26, 2023
73d4dc0
fix (create new project): project detials - shadcn select replaced wi…
NSUWAL123 Sep 26, 2023
90c2a8a
fix (select): shadcn components export removed
NSUWAL123 Sep 26, 2023
e85db74
fix (create new project): select form - shadcn select replaced with c…
NSUWAL123 Sep 26, 2023
b791c65
fix: style fixes on input field
varun2948 Sep 26, 2023
4485f68
Merge branch 'feat-create-new-project' of https://github.com/NSUWAL12…
varun2948 Sep 26, 2023
b9c0960
fix radiobutton): - value prop added
NSUWAL123 Sep 26, 2023
7595499
Merge branch 'feat-create-new-project' of https://github.com/NSUWAL12…
varun2948 Sep 26, 2023
df036ce
fix (create new project): upload area - value prop added to radiobutt…
NSUWAL123 Sep 26, 2023
60a7b1a
fix (create new project): event cleanup added
NSUWAL123 Sep 26, 2023
ce64621
Merge branch 'feat-create-new-project' of https://github.com/NSUWAL12…
varun2948 Sep 26, 2023
5d270dd
fix (create new project): geojson file cleanup added
NSUWAL123 Sep 26, 2023
5265bba
fix (create new project): upload area - drawgeojson added, geojson fi…
NSUWAL123 Sep 26, 2023
196ab84
Feat create new project (#844)
NSUWAL123 Sep 26, 2023
03bbd9a
fix (create project): uploadArea/dataExtract - reset and select same …
NSUWAL123 Sep 26, 2023
6342607
feat(select): added responsive
varun2948 Sep 26, 2023
cb2b701
feat(validation) : added validation file on new create project
varun2948 Sep 26, 2023
3349b33
feat(data-extract): data extract useform integration
varun2948 Sep 26, 2023
9a14fc4
Merge branch 'feat-create-new-project' of https://github.com/NSUWAL12…
varun2948 Sep 26, 2023
45d0aba
fix(dataextract): removed formcategory actions
varun2948 Sep 26, 2023
5058091
feat (create project): upload area - total area calculation on upload…
NSUWAL123 Sep 27, 2023
73c7a0e
feat: changed step from data extract to select form
varun2948 Sep 27, 2023
87a0e8b
Merge branch 'feat-create-new-project' of https://github.com/NSUWAL12…
varun2948 Sep 27, 2023
2bcbb16
feat: changes on steps of create project
varun2948 Sep 27, 2023
9deaf75
feat(File Input): file input component addition
varun2948 Sep 27, 2023
2738a62
feat(component) : used component for fileinput
varun2948 Sep 27, 2023
48ad752
feat/fix (create new project): split tasks - radiobutton state change…
NSUWAL123 Sep 27, 2023
be4b97e
Merge branch 'feat-createproject-projectdetails-new' of github.com:ho…
varun2948 Sep 27, 2023
32676d0
fix (create new project): merge conflict solved
NSUWAL123 Sep 27, 2023
70a2f36
fix (create new project): merge conflict solved
NSUWAL123 Sep 27, 2023
1b2b68e
fix (Accordion): custom accordion added
NSUWAL123 Sep 27, 2023
3f3fc85
fix (project details): project options accordion added for small screen
NSUWAL123 Sep 27, 2023
f3621a9
fix (project detials): map full screen for small screen on project de…
NSUWAL123 Sep 27, 2023
2836e3c
feat (project details): map legends accordion added for small screen
NSUWAL123 Sep 27, 2023
e507750
fix (project details): map legend lock icon size fixed
NSUWAL123 Sep 28, 2023
b0d749e
feat (modal): shadcn modal integrated
NSUWAL123 Sep 28, 2023
e99f3ce
feat (project details): map tasks popup replaced with modal
NSUWAL123 Sep 28, 2023
7b7e71f
feat (project details): tasks section modal replaced with tasks secti…
NSUWAL123 Sep 28, 2023
7ded778
feat (project details): map scroll in view added when map clicked
NSUWAL123 Sep 28, 2023
f76ff13
Merge branch 'development' of https://github.com/hotosm/fmtm into fea…
NSUWAL123 Sep 28, 2023
9b5f8e7
Merge branch 'feat-mobile-ui-project-details' of https://github.com/N…
varun2948 Sep 28, 2023
c4f0692
Merge branch 'development' into feat-project-details-mapper-ui
spwoodcock Oct 2, 2023
577f1ff
Merge branch 'development' into feat-project-details-mapper-ui
spwoodcock Oct 2, 2023
f4667a2
ci: merge pytest workflow fix
spwoodcock Oct 2, 2023
6985bd1
Various: task splitting error handling, compose cleanup, default svcf…
spwoodcock Oct 2, 2023
4c01fa4
ci: debug pytest workflow vars
spwoodcock Oct 2, 2023
f6e7432
Merge branch 'feat-project-details-mapper-ui' of https://github.com/h…
spwoodcock Oct 2, 2023
9c7ba06
build: remove bind mount for migration backend container
spwoodcock Oct 2, 2023
e345bf6
docs: info on creating and applying db migrations
spwoodcock Oct 2, 2023
0b4593a
build: remove gosu from backend ci dockerfile
spwoodcock Oct 2, 2023
a4b2527
build: optimise ci image build RUN commands
spwoodcock Oct 2, 2023
af8b8ba
build: backend ci img install pkgs globally
spwoodcock Oct 2, 2023
5d81e8d
ci: revert pytest workflow
spwoodcock Oct 2, 2023
325bc0c
refactor: replace all process.env refs with environment.ts
spwoodcock Oct 3, 2023
7cdc645
build: merge in backend dockerfile edits from dev
spwoodcock Oct 3, 2023
4664f72
fix: set default project user to svcfmtm (if not logged in)
spwoodcock Oct 3, 2023
9efb685
build: migration entrypoint, create svcfmtm user on start
spwoodcock Oct 3, 2023
d54217c
refactor: revert set svcfmtm on frontend (set as backend default)
spwoodcock Oct 3, 2023
6efd752
build: update migrations for all envs, restart 3 times on fail
spwoodcock Oct 3, 2023
0788f51
fix: remove redundant nullable from DbUser.role (default set)
spwoodcock Oct 3, 2023
70f4528
build: fix migrate-entrypoint role enum type
spwoodcock Oct 3, 2023
5ef6b17
fix: remove additional DbUser nullable=false when default set
spwoodcock Oct 3, 2023
a9537f5
build: update migrate-entrypoint to handle incorrect nulllable
spwoodcock Oct 3, 2023
c8f2b13
build: additional logging to migration script
spwoodcock Oct 3, 2023
aa8bc65
fix: incorrect defaults for DbProject.author & author_id
spwoodcock Oct 3, 2023
9a2d886
refactor: set webpack env vars with defaults (instead of dev/prod)
spwoodcock Oct 3, 2023
14c3cd9
Various: task splitting error handling, compose cleanup, default svcf…
spwoodcock Oct 2, 2023
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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
ODK_CENTRAL_VERSION=v2023.2.1
ODK_CENTRAL_URL=https://central-proxy
[email protected]
ODK_CENTRAL_PASSWD=fmtm
ODK_CENTRAL_PASSWD=testuserpassword

### FMTM ###
# DEBUG=True
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build_and_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,6 @@ jobs:
- name: Deploy
run: |
docker compose --file docker-compose.deploy.yml pull
docker compose --file docker-compose.deploy.yml up --detach
docker compose --file docker-compose.deploy.yml up --detach --remove-orphans
env:
DOCKER_HOST: ${{ vars.DOCKER_HOST }}
4 changes: 2 additions & 2 deletions .github/workflows/r-pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Run pytest as appuser
- name: Run pytest
run: |
cd src/backend
gosu appuser pytest
pytest
4 changes: 2 additions & 2 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ Make sure to replace `<your-username>` with your GitHub username.

1. You will need to [Install Docker](https://docs.docker.com/engine/install/) and ensure that it is running on your local machine.
2. From the command line: navigate to the top level directory of the FMTM project.
3. From the command line run: `docker compose build ui-main`
3. From the command line run: `docker compose build ui`
This is essential, as the development container for the frontend is different to production.
4. Once everything is built, from the command line run: `docker compose up -d ui-main`
4. Once everything is built, from the command line run: `docker compose up -d ui`

5. If everything goes well you should now be able to **navigate to the project in your browser:** <http://127.0.0.1:8080>

Expand Down
7 changes: 4 additions & 3 deletions docker-compose.deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ services:
- .env
networks:
- fmtm-net
entrypoint: ["/migrate-entrypoint.sh"]
command: ["alembic", "upgrade", "head"]
restart: "no"
restart: "on-failure:3"

ui-main:
ui:
image: "ghcr.io/hotosm/fmtm/frontend:${FRONTEND_MAIN_VERSION}-${GIT_BRANCH}"
build:
context: src/frontend
Expand All @@ -126,7 +127,7 @@ services:
APP_VERSION: ${FRONTEND_MAIN_VERSION}
API_URL: ${URL_SCHEME}://${API_URL}
FRONTEND_MAIN_URL: ${URL_SCHEME}://${FRONTEND_MAIN_URL}
container_name: fmtm_main
container_name: fmtm
depends_on:
- api
- traefik
Expand Down
7 changes: 4 additions & 3 deletions docker-compose.noodk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,11 @@ services:
- .env
networks:
- fmtm-net
entrypoint: ["/migrate-entrypoint.sh"]
command: ["alembic", "upgrade", "head"]
restart: "no"
restart: "on-failure:3"

ui-main:
ui:
image: "ghcr.io/hotosm/fmtm/frontend:debug"
build:
context: src/frontend
Expand All @@ -88,7 +89,7 @@ services:
APP_VERSION: ${FRONTEND_MAIN_VERSION}
API_URL: ${URL_SCHEME}://${API_URL}
FRONTEND_MAIN_URL: ${URL_SCHEME}://${FRONTEND_MAIN_URL}
container_name: fmtm_main
container_name: fmtm
depends_on:
- api
volumes:
Expand Down
11 changes: 7 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,19 @@ services:
- .env
networks:
- fmtm-dev
entrypoint: ["/migrate-entrypoint.sh"]
command: ["alembic", "upgrade", "head"]
restart: "no"
restart: "on-failure:3"

ui-main:
ui:
image: "ghcr.io/hotosm/fmtm/frontend:debug"
build:
context: src/frontend
dockerfile: debug.dockerfile
args:
APP_NAME: main
API_URL: ${URL_SCHEME}://${API_URL}
FRONTEND_MAIN_URL: ${URL_SCHEME}://${FRONTEND_MAIN_URL}
container_name: fmtm_main
container_name: fmtm
depends_on:
- api
volumes:
Expand All @@ -104,6 +104,9 @@ services:
environment:
- API_URL=${URL_SCHEME}://${API_URL}
- FRONTEND_MAIN_URL=${URL_SCHEME}://${FRONTEND_MAIN_URL}
- ODK_CENTRAL_URL=${ODK_CENTRAL_URL}
- ODK_CENTRAL_USER=${ODK_CENTRAL_USER}
- ODK_CENTRAL_PASSWD=${ODK_CENTRAL_PASSWD}
ports:
- "8080:8080"
networks:
Expand Down
18 changes: 18 additions & 0 deletions docs/dev/Backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ The API should now be accessible at: <http://127.0.0.1:8000/docs>

To add authentication to an endpoint, import `login_required` from `auth` module, you can use it as a decorator or use fastapi `Depends(login_required)` on endpoints.

### Database Migration

#### Creating Migration Files

- Exec into the API container: `docker compose exec api bash`.
- Run the command to generate migrations: `alembic revision`.
- The migration file should be generated under `src/backend/migrations/versions`.
- Commit the file to the repo.

#### Applying Migrations

- Should occur automatically as part of the docker compose stack (migration service).
- To run manually:

```bash
alembic upgrade head
```

## Backend Debugging

- The `docker-compose.yml` builds FMTM using the `debug-with-odk` target in the Dockerfile.
Expand Down
4 changes: 2 additions & 2 deletions docs/dev/Frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ For details on how to run the API first, please see: [DEV 2. Backend](https://gi

1. You will need to [Install Docker](https://docs.docker.com/engine/install/) and ensure that it is running on your local machine.
2. From the command line: navigate to the top level directory of the FMTM project.
3. From the command line run: `docker compose build ui-main`
3. From the command line run: `docker compose build ui`
This is essential, as the development container for the frontend is different to production.
4. Once everything is built, from the command line run: `docker compose up -d ui-main`
4. Once everything is built, from the command line run: `docker compose up -d ui`

5. If everything goes well you should now be able to **navigate to the project in your browser:** <http://127.0.0.1:8080>

Expand Down
3 changes: 2 additions & 1 deletion src/backend/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
!**/*.py
!app
!tests
!container-entrypoint.sh
!app-entrypoint.sh
!migrate-entrypoint.sh
!pyproject.toml
!pdm.lock
!migrations
Expand Down
6 changes: 3 additions & 3 deletions src/backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ RUN set -ex \
"libproj25" \
"libgeos-c1v5" \
&& rm -rf /var/lib/apt/lists/*
COPY container-entrypoint.sh /
ENTRYPOINT ["/container-entrypoint.sh"]
COPY *-entrypoint.sh /
ENTRYPOINT ["/app-entrypoint.sh"]
# Copy Python deps from build to runtime
COPY --from=build \
/root/.local \
Expand All @@ -113,7 +113,7 @@ COPY alembic.ini /opt/
RUN useradd -r -u 1001 -m -c "hotosm account" -d /home/appuser -s /bin/false appuser \
&& mkdir -p /opt/logs /opt/tiles \
&& chown -R appuser:appuser /opt /home/appuser \
&& chmod +x /container-entrypoint.sh
&& chmod +x /app-entrypoint.sh /migrate-entrypoint.sh
# Add volumes for persistence
VOLUME /opt/logs
VOLUME /opt/tiles
Expand Down
File renamed without changes.
27 changes: 26 additions & 1 deletion src/backend/app/central/central_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,32 @@ async def test_form_validity(xform_content: str, form_type: str):
f.write(xform_content)

xls2xform_convert(xlsform_path=xlsform_path, xform_path=outfile, validate=False)
return {"message": "Your form is valid"}

namespaces = {
"h": "http://www.w3.org/1999/xhtml",
"odk": "http://www.opendatakit.org/xforms",
"xforms": "http://www.w3.org/2002/xforms",
}

import xml.etree.ElementTree as ET

with open(outfile, "r") as xml:
data = xml.read()

root = ET.fromstring(data)
instances = root.findall(".//xforms:instance[@src]", namespaces)

geojson_list = []
for inst in instances:
try:
if "src" in inst.attrib:
if (inst.attrib["src"].split("."))[1] == "geojson":
parts = (inst.attrib["src"].split("."))[0].split("/")
geojson_name = parts[-1]
geojson_list.append(geojson_name)
except Exception:
continue
return {"required media": geojson_list, "message": "Your form is valid"}
except Exception as e:
return JSONResponse(
content={"message": "Your form is invalid", "possible_reason": str(e)},
Expand Down
16 changes: 10 additions & 6 deletions src/backend/app/db/db_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class DbUser(Base):

id = Column(BigInteger, primary_key=True, index=True)
username = Column(String, unique=True)
role = Column(Enum(UserRole), default=UserRole.MAPPER, nullable=False)
role = Column(Enum(UserRole), default=UserRole.MAPPER)

name = Column(String)
city = Column(String)
Expand All @@ -77,11 +77,12 @@ class DbUser(Base):
is_expert = Column(Boolean, default=False)

mapping_level = Column(
Enum(MappingLevel), default=MappingLevel.BEGINNER, nullable=False
Enum(MappingLevel),
default=MappingLevel.BEGINNER,
)
tasks_mapped = Column(Integer, default=0, nullable=False)
tasks_validated = Column(Integer, default=0, nullable=False)
tasks_invalidated = Column(Integer, default=0, nullable=False)
tasks_mapped = Column(Integer, default=0)
tasks_validated = Column(Integer, default=0)
tasks_invalidated = Column(Integer, default=0)
projects_mapped = Column(ARRAY(Integer))

# mentions_notifications = Column(Boolean, default=True, nullable=False)
Expand Down Expand Up @@ -396,7 +397,10 @@ class DbProject(Base):

# PROJECT CREATION
author_id = Column(
BigInteger, ForeignKey("users.id", name="fk_users"), nullable=False
BigInteger,
ForeignKey("users.id", name="fk_users"),
nullable=False,
server_default="20386219",
)
author = relationship(DbUser)
created = Column(DateTime, default=timestamp, nullable=False)
Expand Down
51 changes: 42 additions & 9 deletions src/backend/app/projects/project_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,44 +646,63 @@ async def split_into_tasks(
all_results = []
boundary_data = []
result = []

if outline["type"] == "FeatureCollection":
log.debug("Project boundary GeoJSON = FeatureCollection")
boundary_data.extend(feature["geometry"] for feature in outline["features"])
result.extend(
process_polygon(db, project_id, data, no_of_buildings, has_data_extracts)
split_polygon_into_tasks(
db, project_id, data, no_of_buildings, has_data_extracts
)
for data in boundary_data
)

for inner_list in result:
all_results.extend(iter(inner_list))
if inner_list:
all_results.extend(iter(inner_list))

elif outline["type"] == "GeometryCollection":
log.debug("Project boundary GeoJSON = GeometryCollection")
geometries = outline["geometries"]
boundary_data.extend(iter(geometries))
result.extend(
process_polygon(db, project_id, data, no_of_buildings, has_data_extracts)
split_polygon_into_tasks(
db, project_id, data, no_of_buildings, has_data_extracts
)
for data in boundary_data
)
for inner_list in result:
all_results.extend(iter(inner_list))
if inner_list:
all_results.extend(iter(inner_list))

elif outline["type"] == "Feature":
log.debug("Project boundary GeoJSON = Feature")
boundary_data = outline["geometry"]
result = process_polygon(
result = split_polygon_into_tasks(
db, project_id, boundary_data, no_of_buildings, has_data_extracts
)
all_results.extend(iter(result))
else:

elif outline["type"] == "Polygon":
log.debug("Project boundary GeoJSON = Polygon")
boundary_data = outline
result = process_polygon(
result = split_polygon_into_tasks(
db, project_id, boundary_data, no_of_buildings, has_data_extracts
)
all_results.extend(result)

else:
log.error(
"Project boundary not one of: Polygon, Feature, GeometryCollection,"
" FeatureCollection. Task splitting failed."
)
return {
"type": "FeatureCollection",
"features": all_results,
}


def process_polygon(
def split_polygon_into_tasks(
db: Session,
project_id: uuid.UUID,
boundary_data: str,
Expand Down Expand Up @@ -727,16 +746,30 @@ def process_polygon(
)
result = db.execute(query)
db.commit()

# TODO replace with fmtm_splitter algo
with open("app/db/split_algorithm.sql", "r") as sql_file:
query = sql_file.read()
log.debug(f"STARTED project {project_id} task splitting")
result = db.execute(text(query), params={"num_buildings": no_of_buildings})
result = result.fetchall()
db.query(db_models.DbBuildings).delete()
db.query(db_models.DbOsmLines).delete()
db.query(db_models.DbProjectAOI).delete()
db.commit()
log.debug(f"COMPLETE project {project_id} task splitting")

return result[0][0]["features"]
features = result[0][0]["features"]
if not features:
log.warning(
f"Project {project_id}: no tasks returned from splitting algorithm. "
f"Params: 'num_buildings': {no_of_buildings}"
)
return []

features = json.loads(features)
log.debug(f"Project {project_id} split into {len(features)} tasks")
return features


# def update_project_boundary(
Expand Down
36 changes: 36 additions & 0 deletions src/backend/migrate-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash

set -eo pipefail

while !</dev/tcp/${FMTM_DB_HOST:-fmtm-db}/5432;
do
sleep 1;
done;

# Check all vars present
if [ -z "${FMTM_DB_HOST}" ]; then
echo "Environment variable FMTM_DB_HOST is not set."
exit 1
fi
if [ -z "${FMTM_DB_USER}" ]; then
echo "Environment variable FMTM_DB_USER is not set."
exit 1
fi
if [ -z "${FMTM_DB_PASSWORD}" ]; then
echo "Environment variable FMTM_DB_PASSWORD is not set."
exit 1
fi
if [ -z "${FMTM_DB_NAME}" ]; then
echo "Environment variable FMTM_DB_NAME is not set."
exit 1
fi

echo "Crerating default svcfmtm user"
# Create default svc user
psql "postgresql://${FMTM_DB_USER}:${FMTM_DB_PASSWORD}@${FMTM_DB_HOST}/${FMTM_DB_NAME}" \
-c "INSERT INTO users (id, username, role, mapping_level, tasks_mapped, tasks_validated, tasks_invalidated) \
VALUES (20386219, 'svcfmtm', 'MAPPER', 'BEGINNER', 0, 0, 0) \
ON CONFLICT (id) DO NOTHING;"
echo "User creation complete"

exec "$@"
Loading