Welcome to WAI SDG Portal documentation!

Build Status Repo Size Languages Issues Last Commit Documentation Status

Installation Guide

Warning

Below step is for production-ready installation process. Please follow Developer-Guide to setup the development mode.

System Requirements

System Memory:

4 GiB

CPU:

2 GHz Dual Core Processor

Storage:

25 GiB or more Disk

Operating System:

Ubuntu Server 22.04


Prerequisite

Docker Engine:

20.10 or above

Git:

2.39 or above

3rd Party Service Providers:
  • Auth0

  • Mailjet

Preparation

Note

The following guide is an example installation on Ubuntu and Debian based systems. It has been with Ubuntu 22.04.

Install Docker Engine

You need the latest Docker version installed. If you do not have it, please see the following installation guide to get it.

  1. Update the apt package index and install packages to allow apt to use a repository over HTTPS:

    sudo apt update
    sudo apt install ca-certificates curl gnupg lsb-release
    
  2. Add Docker’s official GPG key:

    sudo mkdir -p /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    
  3. Use the following command to set up the repository:

    echo \
     "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
     $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
  4. Update the apt package index:

    sudo apt update
    
  5. Install Docker Engine, containerd, and Docker Compose.

    sudo apt-get install \
     docker-ce docker-ce-cli \
     containerd.io docker-compose-plugin
    
  6. Manage Docker as a non-root user

    sudo groupadd docker
    sudo usermod -aG docker $USER
    newgrp docker
    

Install Git Version Control

The WAI SDG Portal uses git as version control. Therefore it is better to install git to make it easier to retrieve updates instead download the repository zip.

sudo apt install git

Auth0 Identity Providers

This application DO NOT store directly any personal information. WAI SDG Portal uses AUTH0 for a flexible solution to add authentication services.

Please visit AUTH0, then follow below guide:

Create Auth0 Account

1. Visit Auth0.com

Step 1 screenshot

2. Sign up new account

Step 3 screenshot

3. Finish Sign Up

Step 4 screenshot
Creating New Tenant

1. Navigate to Left Corner Dropdown

Step 5 screenshot

2. Click create New Tenant

Step 6 screenshot

3. Fill the Tenant Domain

Note

Please use your website domain so it can be more consistent

Step 7 screenshot

4. Choose region that is closer to your server

Step 8 screenshot

5. Finish, Click Create

Step 10 screenshot

Now you have switched to your new tenant**

Step 11 screenshot

Create Application

To run this site, 2 types of applications are required for the authorization and authentication process. The first application is the Single Page Application (SPA) type authentication which needed by The JavaScript frontend to authenticate. The second application is for verifying the token that has been created by the frontend which will be used for the process of authorizing access to all the Backend endpoints (API)

Create New SPA Application

1. Click the 3rd icon in the right corner

Step 12 screenshot

2. Click Applications

Step 14 screenshot

3. Click Create Application

Step 15 screenshot

4. Fill the name field then Click Create

Step 16 screenshot Step 17 screenshot

5. Select Settings Tab

Step 18 screenshot

6. Add application logo

Step 19 screenshot

Note

This application has pre-defined logo, the url of image is available in your installation once this up is up and running in your domain. (eg. https://your-domain.com/wai-logo.png)


7. Change your-domain.com with your app-domain for all the field below

Step 20 screenshot

Step 21 screenshot

Step 22 screenshot

Step 23 screenshot

8. Modify the ID Token Expiration

Step 24 screenshot

9. Click Save Button

Step 25 screenshot

10. Select Connections Tab

Step 26 screenshot

11. Change below option field with Both

Step 27 screenshot

12. Click Save Changes

Step 28 screenshot

Create New Backend Application

1. Click on Create Application

Step 29 screenshot

2. Select Machine to Machine Application

Step 30 screenshot

3. Rename the Application

Step 31 screenshot

4. Click on Create

Step 32 screenshot

5. Click on option

Once you click create button, there will be a popup with dropdown selector to authorize this application. Please select Auth0 Management API

Step 33 screenshot

6. Authorize All the Permissions

Step 34 screenshot

7. Click on Authorize

Step 35 screenshot

8. Click on Settings

Step 36 screenshot

9. Change your-domain.com with your app-domain for all the field below

Step 20 screenshot

Step 21 screenshot

Step 22 screenshot

Step 23 screenshot

10. Modify the ID Token Expiration

Step 24 screenshot

11. Click Save Button

Step 25 screenshot

The Production Tentant

Tenants tagged as Production are granted higher rate limits than tenants tagged as Development or Staging. To ensure Auth0 recognizes your production tenant, be sure to set your production tenant with the production flag in the Support Center.

Note

Higher rate limits are applied to public cloud tenants tagged as Production with a paid subscription. See Auth0 Tenant Policy

1. Click Gear Icons

Step 29 screenshot

2. Select Production

Step 30 screenshot

3. Click Save

Step 31 screenshot

Back to Installation

Mailjet Service

You need to have MAILJET account to manage the notification deliverability.

Create Mailjet Account

1. Click on Sign up now!

Step 1 screenshot Step 2 screenshot

2. Click on Next

Step 3 screenshot

3. Click on Complete order then check your email for verification

Step 4 screenshot
Generating API Key

1. Click Account Dropdown

Step 6 screenshot

2. Click on Account settings

Step 7 screenshot

3. Click on API Key Management

Step 8 screenshot

4. Click on Generate Secret Key

Step 10 screenshot

Installation

Clone the Repository

git clone https://github.com/akvo/wai-sdg-portal.git

Environment Variable Setup

Install text editor to be able to edit .env file

sudo apt install nano

or

sudo apt install vim

Go to the repository directory, then edit the environment

cd wai-sdg-portal/deploy
vim .env

Example Environemnt:

POSTGRES_PASSWORD=postgres
WAI_DB_USER=yourname
WAI_DB_PASSWORD=sUpeRsTr0ngPa**word
INSTANCE_NAME=wai-demo
AUTH0_DOMAIN=your-domain.eu.auth0.com
AUTH0_CLIENT_ID=acad34xxxxxxxx
AUTH0_SECRET=938axxxxxxxxxxx
AUTH0_AUDIENCE=cdary8xxxxxxxx
AUTH0_SPA_DOMAIN=5a2axxxxxxxxxxx
AUTH0_SPA_CLIENT_ID=b821y8xxxxxxxx
STORAGE_LOCATION=/data/storage
MAILJET_SECRET=093asbalxxxxxxxx
MAILJET_APIKEY=9acadlkbxxxxxxxx
WEBDOMAIN=https://your-domain.com

Note

  • Use Domain and Client ID field from your Auth0 SPA application for AUTH_SPA_DOMAIN and AUTH_SPA_CLIENT_ID

  • Use Domain, Secret and Client ID field from your Auth0 Backend application for AUTH_DOMAIN, AUTH_SECRET and AUTH_CLIENT_ID.

  • For AUTH0_AUDIENCE, Go to your Auth0 backend application, click APIs Tab, expand Auth0 Management API. Use the Grant ID field.

Run the Application

./install.sh

Post-Installation

Once the app is started, we need to populate the database with the initial data set. The initial dataset are:

  • 1st Super Admin

  • 1st Organisation

  • Administration Levels Data

Run the database seeder:

docker compose exec backend ./seed.sh youremail@akvo.org "Your Full Name" "Your Organisation"

Example:

docker compose exec backend ./seed.sh youremail@akvo.org "Your Name" Akvo

Getting Started

Log in

Before, you start looking around and going into deep, please logging in by clicking on the Login or Signup.

Logging in

Datasets

Take a quick look at the datasets.

Datasets overview

List of datasets

A list of datasets is shown once you click on the Menu button

Datasets overview

Data

Each data has multiple questions and question groups.

Datasets overview

Maps

  • A map is on the page which shows all places, district, sub-district and village from where the data is collected.

  • This map can be zoomed out or in

Map

Maps Legend

A legend that displays the level of data services is shown on the page under the map.

Legend

Chart data

JMP

A chart is also on the page once the JMP tab is active

JMP chart

Bar

  • A disruption of a question is shown on a Bar char. To see this, you have to select a question.

  • This bar chart can also be saved as an image by clickin on the SAVE IMAGE icon.

  • If you would love to see the question value on a table, there is table icon that can be clicked to do so.

Bar chart Table

Admin page

Manage data

Filter

Searching data by - data point - District, sub-district and village - You can also reset your filter search.

Filtering

Advanced filter

If you wish to search in a more advanced way, you click on the ADVANCED FILTER button.

Advanced filter

Export filtered data

Export filtered data

List of data

A list of data is on the page as a table which has - data name - Region - Submitter - Last updated - Action

List of data

Editing data

A data entry can be edited.

Edit

Delete Data

A data entry can be deleted.

Delete

Add New Data

You also can add a new data entry.

Add

Export Data

You can see a list of exported data entries that you can download.

Exported data

Upload Data

You can also upload a new data entry from your file manager or download existing data by clicking on the Download button.

data upload

Manage users

A list of users is displayed on the page with their: - Name - Email - Organisation - Role

Manage users

Manage form passcode

A list of form that accessable via URL and is displayed on the page with the following columns: - Form name - URL - Passcode (The text field is encrypted by default)

Manage form Passcode

Copy URL

The URL field has a copy functionality to get URL easily

The URL copy functionality

Edit Passcode

You can edit text field by clicking the Edit passcode button

Edit passcode clicked

Once edit is clicked, a warning pop-up will appear to confirm the user

Edit confirmed yes

If you select yes, then the text field can be filled in with the new passcode. Otherwise, the text field will return readonly.

Input new passcode

Click the save button to save the passcode you just created

Save clicked

Development Installation

Environment Setup

export WAI_AUTH0_DOMAIN="string_url"
export WAI_AUTH0_CLIENT_ID="string"
export WAI_AUTH0_SECRET="string"
export WAI_AUTH0_AUDIENCE="string"
export WAI_AUTH0_SECRET="string"
export WAI_AUTH0_SPA_DOMAIN="string_url"
export WAI_AUTH0_SPA_CLIENT_ID="string"
export INSTANCE_NAME="wai-demo"

Start the App

Once you have all the required environment ready, then run the App using:

  • Run the application

    export INSTANCE_NAME=<project-name>
    docker compose up -d
    
  • Stop

    docker compose down
    
  • Reset the app

    docker compose down -v
    

Database Seeder

Config Requirements

Before you seed the baseline data, please make sure that you have all the required file in the following structure:

Folder Path: /backend/source/

/backend/source.
└── project-name
    ├── config.js
    ├── config.min.js
    ├── data
       └── organisation.csv
    ├── forms
       ├── 01-clts.json
       ├── 02-health.json
       ├── 03-hh.json
       ├── 04-school.json
       └── 05-wp.json
    └── topojson.js

config.json

config.min.js is pre-generated file to merge visualisation config.js, topojson.js and menu.

MINJS = jsmin("".join([
  "var levels=" + str([g["alias"] for g in GEO_CONFIG]) + ";"
  "var map_config={shapeLevels:" + str([g["name"]
                                      for g in GEO_CONFIG]) + "};",
  "var topojson=",
  open(f"{SOURCE_PATH}/topojson.json").read(),
  ";", JS_FILE, JS_i18n_FILE
]))
JS_FILE = f"{SOURCE_PATH}/config.min.js"
open(JS_FILE, 'w').write(MINJS)

forms.json

*.json files inside forms folder is the form definition of a questionnaire which contains detail of forms including question group setting and question definition.

Example:

{
  "form": "JMP Core Questions for Monitoring WASH in Households",
  "id": 567420165,
  "question_groups": [
     {
       "question_group": "Location Demographics",
       "questions": [
         {
           "question": "location",
           "order": 1,
           "required": true,
           "type": "administration",
           "meta": true
         },
         {
           "id": 554110154,
           "question": "Village",
           "order": 3,
           "meta": true,
           "type": "text",
           "required": true,
           "options": null
         },
         {
           "id": 554110155,
           "question": "GPS Coordinates of Household",
           "order": 4,
           "meta": true,
           "type": "geo",
           "required": true,
           "options": null
         }
       ]
     }
   ]
 }

Seeder CLI

Administration Level Seeder

docker compose exec backend python -m seeder.administration

Organisation Seeder

docker compose exec backend python -m seeder.organisation

Super Admin

docker compose exec backend python -m seeder.admin youremail@akvo.org "Your Name" Akvo

Form Seeder

docker compose exec backend python -m seeder.form

Seed Fake User

docker compose exec backend python -m seeder.fake_user <number_of_user> Akvo

Datapoint Seeder

docker compose exec backend python -m seeder.fake_datapoint youremail@akvo.org <number_of_datapoints>

JMP Logic implementation

JMP Logic implementation has been done by the AkvoResponseGrouper [1] library and we just need to create a category.json inside the source folder and place it in a specific instance.

For example, if we want to implement JMP logic on the Ethiopia instance, then category.json should be created on

backend
├── source
    ├── wai-ethiopia
        └── category.json

Properties

This section contains the properties that will be used in configuring the JMP logic in category.json.

Criteria’s fields

Field

Type

Description

name

String

Criteria name

form

Integer

Existing form ID in database

categories

Array of category

List of categories

Category’s fields

Field

Type

Description

name

String

Category name

questions

Array of question including the logic

List of existing questions and their logic

Question & logic’s fields

Field

Type

Required

Description

id

Integer

Yes

Existing question ID in database.

text

String

No

Question description.

options

Array of string

Yes

Set list of options that will have intersections with the answer to the question.

other

Array of other

No

Another set of lists of options that don’t have intersections in the options.

else

Object of else

No

Set category that has no intersections, either in options or other.

Other’s fields

Field

Type

Required

Description

name

String

Yes

Category name

options

Array of string

Yes

Set list of options that will have intersections with the answer to the question

questions

Array of question

Yes

List of existing questions and their logic and can be set empty of Array []

Else’s fields

Field

Type

Required

Description

name

String

No

Category name

ignore

Array of existing question IDs

No

Set list of question IDs that can be skipped based on the intersections in the options

Example

In this section, we provide an example use case to demonstrate how to create a category.json file. Please note that the presented use case, “Sanitation,” is intended for illustrative purposes only. While the example showcases the functionality and features of our library, it may not be an exact representation of real-world scenarios.

Logic visualisation

JMP logic visualisation

JSON File (category.json)

[
  {
    "name": "Sanitation Criteria",
    "form": 1,
    "categories": [
      {
        "name": "Basic",
        "questions": [
          {
              "id": 11,
              "text": "School has toilet?",
              "options": [
                  "Yes"
              ],
              "else": {
                  "name": "No service"
              }
          },
          {
              "id": 12,
              "text": "Type of toilets",
              "options": [
                  "Flush/Pour-flush toilets",
                  "Pit latrines with slab"
              ],
              "other": [
                  {
                      "name": "Unimproved",
                      "options": [
                          "Composting toilets",
                          "VIP latrine"
                      ],
                      "questions": []
                  }
              ],
              "else": {
                  "name": "Limited"
              }
          },
          {
              "id": 13,
              "text": "Is the school co-ed?",
              "options": [
                  "Yes"
              ],
              "else": {
                  "ignore": [
                      14
                  ]
              }
          },
          {
              "id": 14,
              "text": "is toilet separated?",
              "options": [
                  "Yes"
              ],
              "else": {
                  "name": "Limited"
              }
          },
          {
              "id": 15,
              "text: ":"Is toilet usable?",
              "options": [
                  "Yes"
              ],
              "else": {
                  "name": "Limited"
              }
          }
        ]
      }
    ]
  }
]

Running Tests

Backend Test

docker-compose exec backend ./test.sh

Frontend Test

docker-compose exec frontend yarn test

Database Schema

List of Table

SELECT relname, relkind
FROM   pg_class
WHERE relreplident = 'd'
AND relhasindex = true;
List of Table

relname

relkind

alembic_version

r

administration

r

access

r

question_group

r

form

r

question

r

data

r

answer

r

organisation

r

jobs

r

log

r

option

r

history

r

user

r

Table Relationship

List of Relationship

Schema

Name

Type

Owner

public

access

table

wai

public

access_id_seq

sequence

wai

public

administration

table

wai

public

administration_id_seq

sequence

wai

public

alembic_version

table

wai

public

answer

table

wai

public

answer_id_seq

sequence

wai

public

answer_search

view

wai

public

data

table

wai

public

data_id_seq

sequence

wai

public

form

table

wai

public

form_id_seq

sequence

wai

public

history

table

wai

public

history_id_seq

sequence

wai

public

jobs

table

wai

public

jobs_id_seq

sequence

wai

public

log

table

wai

public

log_id_seq

sequence

wai

public

option

table

wai

public

option_id_seq

sequence

wai

public

organisation

table

wai

public

organisation_id_seq

sequence

wai

public

question

table

wai

public

question_group

table

wai

public

question_group_id_seq

sequence

wai

public

question_id_seq

sequence

wai

public

score_view

view

wai

public

user

table

wai

public

user_id_seq

sequence

wai

Table Details

Administration

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'administration'
ORDER  BY ordinal_position;
List of Relationship

pos

column_name

data_type

udt_name

column_default

is_nullable

1

id

integer

int4

nextval(‘administration_id_seq’::regclass)

NO

2

parent

integer

int4

YES

3

name

character varying

varchar

YES

User

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'user'
ORDER  BY ordinal_position;
User Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘user_id_seq’::regclass)

NO

2

email

character varying

YES

3

active

boolean

YES

4

role

USER-DEFINED

YES

5

created

timestamp without time zone

YES

6

organisation

integer

YES

7

name

character varying

YES

8

__ts_vector__

tsvector

YES

9

manage_form_passcode

boolean

false

NO

User Access

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'access'
ORDER  BY ordinal_position;
Access Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘access_id_seq’::regclass)

NO

2

user

integer

YES

3

administration

integer

YES

Organisation

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'organisation'
ORDER  BY ordinal_position;
Organisation Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘organisation_id_seq’::regclass)

NO

2

name

character varying

YES

3

type

USER-DEFINED

YES

4

created

timestamp without time zone

YES

Form

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'form'
ORDER  BY ordinal_position;
Form Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘form_id_seq’::regclass)

NO

2

name

character varying

YES

3

description

text

YES

4

default_language

character varying

YES

5

languages

ARRAY

YES

6

translations

ARRAY

YES

7

version

double precision

YES

Question Group

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'question_group'
ORDER  BY ordinal_position;
Question Group Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘question_group_id_seq’::regclass)

NO

2

order

integer

YES

3

name

character varying

YES

4

form

integer

YES

5

description

text

YES

6

repeatable

boolean

false

YES

7

repeat_text

character varying

YES

8

translations

ARRAY

YES

Question

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'question'
ORDER  BY ordinal_position;
Question Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘question_id_seq’::regclass)

NO

2

order

integer

YES

3

name

character varying

YES

4

form

integer

YES

5

meta

boolean

NO

6

type

USER-DEFINED

YES

7

question_group

integer

YES

8

required

boolean

true

NO

9

rule

jsonb

YES

10

dependency

ARRAY

YES

11

tooltip

jsonb

YES

12

translations

ARRAY

YES

13

api

jsonb

YES

14

addons

jsonb

YES

Question Option

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'option'
ORDER  BY ordinal_position;
Question Option Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘option_id_seq’::regclass)

NO

2

order

integer

YES

3

name

character varying

YES

4

question

integer

YES

5

color

character varying

YES

6

score

integer

YES

7

code

character varying

YES

8

translations

ARRAY

YES

Data

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'data'
ORDER  BY ordinal_position;
Data Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘data_id_seq’::regclass)

NO

2

name

character varying

YES

3

form

integer

YES

4

administration

integer

YES

5

geo

ARRAY

YES

6

created_by

integer

YES

7

updated_by

integer

YES

8

created

timestamp without time zone

CURRENT_TIMESTAMP

YES

9

updated

timestamp without time zone

YES

Data Answer

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'answer'
ORDER  BY ordinal_position;
Data Answer Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘answer_id_seq’::regclass)

NO

2

question

integer

YES

3

data

integer

YES

4

value

double precision

YES

5

text

text

YES

6

options

ARRAY

YES

7

created_by

integer

YES

8

updated_by

integer

YES

9

created

timestamp without time zone

CURRENT_TIMESTAMP

YES

10

updated

timestamp without time zone

YES

Data History

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'history'
ORDER  BY ordinal_position;
Data Answer Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘history_id_seq’::regclass)

NO

2

question

integer

YES

3

data

integer

YES

4

value

double precision

YES

5

text

text

YES

6

options

ARRAY

YES

7

created_by

integer

YES

8

updated_by

integer

YES

9

created

timestamp without time zone

CURRENT_TIMESTAMP

YES

10

updated

timestamp without time zone

YES

Jobs

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'jobs'
ORDER  BY ordinal_position;
Jobs Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘jobs_id_seq’::regclass)

NO

2

type

USER-DEFINED

YES

3

status

USER-DEFINED

‘pending’::jobstatus

YES

4

payload

text

NO

5

info

jsonb

YES

6

attempt

integer

0

YES

7

created_by

integer

NO

8

created

timestamp without time zone

CURRENT_TIMESTAMP

YES

9

available

timestamp without time zone

YES

Logs

SELECT ordinal_position as pos, column_name, data_type, column_default, is_nullable
FROM   information_schema.columns
WHERE  table_name = 'log'
ORDER  BY ordinal_position;
Log Table

pos

column_name

data_type

column_default

is_nullable

1

id

integer

nextval(‘log_id_seq’::regclass)

NO

2

user

integer

YES

3

message

text

YES

4

at

timestamp without time zone

CURRENT_TIMESTAMP

YES

5

jobs

integer

YES

List of Services

** Services

#+NAME: Services
#+begin_src sh :results verbatim output :exports both
docker compose ps
#+end_src

#+RESULTS: Services
: NAME                           IMAGE                                              COMMAND                  SERVICE             CREATED             STATUS              PORTS
: wai-sdg-portal-backend-1       python:3.8.5                                       "./dev.sh"               backend             4 hours ago         Up 4 hours
: wai-sdg-portal-db-1            postgres:12-alpine                                 "docker-entrypoint.s…"   db                  4 hours ago         Up 4 hours          0.0.0.0:5432->5432/tcp, :::5432->5432/tcp
: wai-sdg-portal-frontend-1      akvo/akvo-node-18-alpine:20220923.084347.0558ee6   "run-as-user.sh ./st…"   frontend            4 hours ago         Up 4 hours
: wai-sdg-portal-mainnetwork-1   alpine:3.14.0                                      "tail -f /dev/null"      mainnetwork         4 hours ago         Up 4 hours          0.0.0.0:3000->3000/tcp, :::3000->3000/tcp, 0.0.0.0:5050->5050/tcp, :::5050->5050/tcp, 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp
: wai-sdg-portal-pgadmin-1       dpage/pgadmin4:5.7                                 "/entrypoint.sh"         pgadmin             4 hours ago         Up 4 hours
: wai-sdg-portal-worker-1        python:3.8.5                                       "./worker.sh"            worker              4 hours ago         Up 4 hours

** Container Detail

*** Backend

#+NAME: Backend Processes
#+begin_src sh :exports both
docker compose top backend | tail -n +2 | head -n -1
#+end_src

#+RESULTS: Backend Processes
| UID  |   PID |  PPID | C | STIME | TTY |     TIME | CMD                   |                        |          |                                  |        |              |                          |                |                        |
| root | 94628 | 94577 | 0 | 12:12 | ?   | 00:00:00 | bash                  | ./dev.sh               |          |                                  |        |              |                          |                |                        |
| root | 95594 | 94628 | 3 | 12:12 | ?   | 00:08:17 | /usr/local/bin/python | /usr/local/bin/uvicorn | main:app | --reload                         | --port | 5000         |                          |                |                        |
| root | 95595 | 95594 | 0 | 12:12 | ?   | 00:00:00 | /usr/local/bin/python | -c                     | from     | multiprocessing.resource_tracker | import | main;main(4) |                          |                |                        |
| root | 95596 | 95594 | 0 | 12:12 | ?   | 00:00:49 | /usr/local/bin/python | -c                     | from     | multiprocessing.spawn            | import | spawn_main;  | spawn_main(tracker_fd=5, | pipe_handle=7) | --multiprocessing-fork |

**** Commands

#+NAME: Backend Commands
#+begin_src sh :results verbatim :exports both
docker compose exec backend ./seed.sh
#+end_src

#+RESULTS: Backend Commands
: This script require more than 0 argument/s
: Example: ./test.sh dev@akvo.org "My Name" "My Organisation"

#+NAME: Seeder
#+begin_src sh :results verbatim :exports both
docker compose exec backend cat ./seed.sh | grep seeder. | sed 's/#\ //g'
#+end_src

#+RESULTS: Seeder
:     python -m seeder.administration
:     python -m seeder.admin "$@"
:     python -m seeder.fake_user 30 "$3"
:     python -m seeder.form
:     python -m seeder.datapoint "$1"

*** Frontend

**** Processes

#+NAME: Frontend Processes
#+begin_src sh :exports both
docker compose -f docker-compose.yml top frontend | tail -n +2 | head -n -1
#+end_src

#+RESULTS: Frontend Processes
| UID  |   PID |  PPID | C | STIME | TTY |     TIME | CMD                 |                                                  |       |
| root | 94620 | 94559 | 0 | 12:12 | ?   | 00:00:00 | bash                | ./start.sh                                       |       |
| root | 94820 | 94620 | 0 | 12:12 | ?   | 00:00:03 | node                | /opt/yarn-v1.22.19/bin/yarn.js                   | start |
| root | 94841 | 94820 | 0 | 12:12 | ?   | 00:00:00 | /usr/local/bin/node | /app/node_modules/.bin/react-scripts             | start |
| root | 94849 | 94841 | 0 | 12:12 | ?   | 00:00:21 | /usr/local/bin/node | /app/node_modules/react-scripts/scripts/start.js |       |

**** Commands

#+NAME: Frontend Commands
#+begin_src sh :results verbatim :exports both
docker compose exec frontend yarn run
#+end_src

#+RESULTS: Frontend Commands
#+begin_example
yarn run v1.22.19
info Commands available from binary scripts: acorn, ansi-html, autoprefixer, browserslist, browserslist-lint, cross-env, cross-env-shell, css-blank-pseudo, css-has-pseudo, css-prefers-color-scheme, cssesc, detect, detect-port, detective, ejs, escodegen, esgenerate, eslint, eslint-config-prettier, esparse, esvalidate, he, html-minifier-terser, import-local-fixture, is-docker, jake, jest, js-yaml, jsesc, json5, loose-envify, lz-string, mime, mkdirp, multicast-dns, nanoid, nmtree, node-which, parser, prettier, react-scripts, regjsparser, remarkable, resolve, rimraf, rollup, sass, semver, sha.js, svgo, synp, tailwind, tailwindcss, terser, topo2geo, topomerge, topoquantize, uuid, webpack, webpack-dev-server, yarn-audit-fix
info Project commands
   - build
      react-scripts build
   - eject
      react-scripts eject
   - lint
      eslint --config .eslintrc.json src --ext .js,.jsx
   - prettier
      prettier --check src
   - start
      react-scripts start
   - test
      react-scripts test --updateSnapshot --transformIgnorePatterns "node_modules/(?!d3|d3-geo|d3-array|internmap|delaunator|robust-predicates|react-leaflet)/"
   - test:ci
      CI=true react-scripts test --watchAll=false --coverage --transformIgnorePatterns "node_modules/(?!d3|d3-geo|d3-array|internmap|delaunator|robust-predicates)/"
Done in 0.02s.
#+end_example

*** Worker

#+NAME: Worker Processes
#+begin_src sh :exports both
docker compose top worker | tail -n +2 | head -n -1
#+end_src

#+RESULTS: Worker Processes
| UID  |   PID |  PPID | C | STIME | TTY |     TIME | CMD                   |                        |               |                                  |        |              |                          |                |                        |
| root | 94704 | 94679 | 0 | 12:12 | ?   | 00:00:00 | bash                  | ./worker.sh            |               |                                  |        |              |                          |                |                        |
| root | 95588 | 94704 | 3 | 12:12 | ?   | 00:08:19 | /usr/local/bin/python | /usr/local/bin/uvicorn | worker:worker | --reload                         | --port | 5001         |                          |                |                        |
| root | 95590 | 95588 | 0 | 12:12 | ?   | 00:00:00 | /usr/local/bin/python | -c                     | from          | multiprocessing.resource_tracker | import | main;main(4) |                          |                |                        |
| root | 95591 | 95588 | 0 | 12:12 | ?   | 00:00:55 | /usr/local/bin/python | -c                     | from          | multiprocessing.spawn            | import | spawn_main;  | spawn_main(tracker_fd=5, | pipe_handle=7) | --multiprocessing-fork |

*** Database

#+NAME: Database Processes
#+begin_src sh :exports both
docker compose top db | tail -n +2 | head -n -1
#+end_src

#+RESULTS: Database Processes
| UID |    PID |  PPID | C | STIME | TTY |     TIME | CMD       |              |             |                   |      |
|  70 |  94342 | 94316 | 0 | 12:12 | ?   | 00:00:00 | postgres  |              |             |                   |      |
|  70 |  94635 | 94342 | 0 | 12:12 | ?   | 00:00:00 | postgres: | checkpointer |             |                   |      |
|  70 |  94636 | 94342 | 0 | 12:12 | ?   | 00:00:00 | postgres: | background   | writer      |                   |      |
|  70 |  94637 | 94342 | 0 | 12:12 | ?   | 00:00:00 | postgres: | walwriter    |             |                   |      |
|  70 |  94638 | 94342 | 0 | 12:12 | ?   | 00:00:00 | postgres: | autovacuum   | launcher    |                   |      |
|  70 |  94639 | 94342 | 0 | 12:12 | ?   | 00:00:00 | postgres: | stats        | collector   |                   |      |
|  70 |  94640 | 94342 | 0 | 12:12 | ?   | 00:00:00 | postgres: | logical      | replication |          launcher |      |
|  70 |  95628 | 94342 | 0 | 12:12 | ?   | 00:00:00 | postgres: | wai          | wai_demo    | 172.20.0.3(38228) | idle |
|  70 |  95629 | 94342 | 0 | 12:12 | ?   | 00:00:02 | postgres: | wai          | wai_demo    | 172.20.0.3(38234) | idle |
|  70 |  95642 | 94342 | 0 | 12:12 | ?   | 00:00:00 | postgres: | wai          | wai_demo    | 172.20.0.3(38242) | idle |
|  70 | 103242 | 94342 | 0 | 12:17 | ?   | 00:00:00 | postgres: | wai          | wai_demo    | 172.20.0.3(36900) | idle |
|  70 | 103243 | 94342 | 0 | 12:17 | ?   | 00:00:00 | postgres: | wai          | wai_demo    | 172.20.0.3(36916) | idle |
|  70 | 103244 | 94342 | 0 | 12:17 | ?   | 00:00:00 | postgres: | wai          | wai_demo    | 172.20.0.3(36926) | idle |

*** Dev

**** PG Admin

#+NAME: PG Admin Processes
#+begin_src sh :exports both
docker compose top pgadmin | tail -n +2 | head -n -1
#+end_src

#+RESULTS: PG Admin Processes
| UID      |    PID |  PPID | C | STIME | TTY |     TIME | CMD                         |                    |           |       |        |           |    |   |           |    |                  |   |    |                    |                 |
| 5050     |  94608 | 94537 | 0 | 12:12 | ?   | 00:00:03 | /venv/bin/python3           | /venv/bin/gunicorn | --timeout | 86400 | --bind | [::]:5050 | -w | 1 | --threads | 25 | --access-logfile | - | -c | gunicorn_config.py | run_pgadmin:app |
| root     |  94812 | 94608 | 0 | 12:12 | ?   | 00:00:00 | /usr/libexec/postfix/master | -w                 |           |       |        |           |    |   |           |    |                  |   |    |                    |                 |
| systemd+ |  94814 | 94812 | 0 | 12:12 | ?   | 00:00:00 | qmgr                        | -l                 | -t        |  unix | -u     |           |    |   |           |    |                  |   |    |                    |                 |
| 5050     |  94905 | 94608 | 0 | 12:12 | ?   | 00:00:09 | /venv/bin/python3           | /venv/bin/gunicorn | --timeout | 86400 | --bind | [::]:5050 | -w | 1 | --threads | 25 | --access-logfile | - | -c | gunicorn_config.py | run_pgadmin:app |
| systemd+ | 425845 | 94812 | 0 | 15:32 | ?   | 00:00:00 | pickup                      | -l                 | -t        |  unix | -u     |           |    |   |           |    |                  |   |    |                    |                 |

**** Main Network

#+NAME: Main Network Processes
#+begin_src sh :exports both
docker compose top mainnetwork | tail -n +2 | head -n -1
#+end_src

#+RESULTS: Main Network Processes
| UID  |   PID |  PPID | C | STIME | TTY |     TIME | CMD  |    |           |
| root | 94393 | 94363 | 0 | 12:12 | ?   | 00:00:00 | tail | -f | /dev/null |