Skip to content

Commit e3c6606

Browse files
authored
Merge pull request #50 from datakind/uitest
Add testing and ui element to download solutions
2 parents 1cc685b + 0df3f46 commit e3c6606

File tree

10 files changed

+93
-11
lines changed

10 files changed

+93
-11
lines changed

.github/workflows/docker-actions.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name: Create and publish a Docker image
22

3-
# Configures this workflow to run every time a change is pushed to the branch called `main`.
43
on:
54
push:
65
branches: ['main']
6+
workflow_dispatch:
77

88
# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
99
env:
@@ -49,3 +49,7 @@ jobs:
4949
push: true
5050
tags: ${{ steps.meta.outputs.tags }}
5151
labels: ${{ steps.meta.outputs.labels }}
52+
- name: Run tests
53+
run: |
54+
echo Running tests
55+
docker run --name tester --rm ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.tags }} /opt/conda/bin/python /src/tests/end_to_end/test_runner.py

dkroutingtool/Dockerfile.dev

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ COPY manual.ipynb .
9090

9191
COPY manual.sh .
9292

93+
COPY Makefile .
94+
9395
RUN mkdir -p py-lib
9496

9597
RUN mv osrm-backend/example/build/osrmbindings.cpython-37m-x86_64-linux-gnu.so py-lib/

dkroutingtool/Makefile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
build:
2+
sh s_build_docker_dev.sh
3+
4+
interact:
5+
docker run --name server --rm --network host -it dkroutingtool:dev bash
6+
7+
run_and_serve:
8+
docker run --name server --network host -d --rm dkroutingtool:dev /opt/conda/bin/python src/py/server.py
9+
10+
serve:
11+
/opt/conda/bin/python /src/py/server.py &
12+
13+
test:
14+
/opt/conda/bin/python /src/tests/end_to_end/test_runner.py
15+
16+
dockergui:
17+
cd src/py/ui && docker build -t dashboard -f dashboard.dockerfile .
18+
docker run --name dashboard --network host -d --rm dashboard:latest
19+
20+
gui:
21+
cd src/py/ui && streamlit run dashboard.py
22+
23+
#go to http://localhost:8501/ in your browser and it should work
24+
demo: run_and_serve dockergui

dkroutingtool/README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11

22
# CART General Instructions
33

4-
You'll need to install docker and yq.
4+
You'll need to install docker (e.g. Rancher Desktop) and yq.
55

66
The only vehicle profiles available are the ones defined in the directory `veh_profiles`, so make sure to have at least one profile there before building the solution. Feel free to add your own profiles as needed.
77

8+
Refer to the Makefile for common use cases, e.g.
9+
`make demo`
10+
then go to http://localhost:8501/ to use the web-based interface
11+
812
## User Manual
913
This is the draft for a user manual, it has information about configuration options and a few things that are more related to setting up your workflow with the tool.
1014
https://docs.google.com/document/d/1iOlXQk6_ElM_LdawJPREHNjVkv_2Qajam3is2hm5zyM
1115

1216
### Server API example
1317
docker run --network host dkroutingtool:latest /opt/conda/bin/python src/py/server.py
18+
or if you want to map ports instead:
19+
docker run -p 5001:5001 dkroutingtool:latest /opt/conda/bin/python src/py/server.py
1420

1521
curl -X "POST" "localhost:5001/provide_files" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "files=@local_data/config.json" -F "files=@local_data/customer_data.xlsx"
1622

@@ -20,6 +26,10 @@ curl -o download.zip http://localhost:5001/download
2026

2127
curl -X "POST" "localhost:5001/adjust_solution" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "files=@download/manual_edits/manual_routes_edits.xlsx"
2228

29+
### GUI
30+
31+
Refer to the src/py/ui directory to try out the web-based interface.
32+
2333
## Dev
2434

2535
### Build

dkroutingtool/local_data/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,6 @@
3131
"node_loader_options": {"num_containers_default": 2, "elevation_factor": 300},
3232
"global_solver_options": {
3333
"max_solver_time_min": 1,
34-
"fast_run": false
34+
"fast_run": true
3535
}
3636
}
File renamed without changes.
File renamed without changes.

dkroutingtool/scripts/start_server.sh

Lines changed: 0 additions & 3 deletions
This file was deleted.

dkroutingtool/src/py/ui/dashboard.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import base64
12
import requests
23
import datetime
34
import streamlit as st
@@ -12,8 +13,10 @@ def download_solution(solution_path, map_path):
1213
timestamp = datetime.datetime.now().strftime(format='%Y%m%d-%H-%M-%S')
1314
response = requests.get(f'{host_url}/download')
1415

16+
solution_zip = response.content
17+
1518
with open(f'solution_files_{timestamp}.zip', 'wb') as f:
16-
f.write(response.content)
19+
f.write(solution_zip)
1720

1821
with zipfile.ZipFile(f'solution_files_{timestamp}.zip', 'r') as zipped:
1922
zipped.extractall(f'solution_files_{timestamp}/')
@@ -24,13 +27,13 @@ def download_solution(solution_path, map_path):
2427
with open(f'solution_files_{timestamp}/{map_path}', 'r') as map_html:
2528
map = map_html.read()
2629

27-
return solution, map
30+
return solution, map, solution_zip
2831

2932
def request_solution():
3033
response = requests.get(f'{host_url}/get_solution')
31-
solution, map = download_solution(solution_path='solution.txt', map_path='/maps/route_map.html')
34+
solution, map, solution_zip = download_solution(solution_path='solution.txt', map_path='/maps/route_map.html')
3235

33-
return solution, map
36+
return solution, map, solution_zip
3437

3538
def adjust(adjusted_file):
3639
headers = {
@@ -82,7 +85,12 @@ def main():
8285

8386
if solution_requested:
8487
with st.spinner('Computing routes, please wait...'):
85-
solution, map = request_solution()
88+
solution, map, solution_zip = request_solution()
89+
#this button reloads the page, let's avoid it
90+
#st.download_button('Download solution files', solution_zip, file_name='solution.zip',
91+
# mime='application/octet-stream', help='Downloads all the files generated by the tool')
92+
b64 = base64.b64encode(solution_zip).decode()
93+
st.markdown(f'<a href="data:application/octet-stream;base64,{b64}" download="solution.zip">Download solution files</a>', unsafe_allow_html=True)
8694
components.html(map, height = 800)
8795
st.write(solution)
8896

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import zipfile
2+
import requests
3+
import time
4+
import subprocess
5+
6+
host_url = 'http://localhost:5001'
7+
8+
def download_solution(solution_path):
9+
response = requests.get(f'{host_url}/download')
10+
solution_zip = response.content
11+
12+
with open(f'solution_files.zip', 'wb') as f:
13+
f.write(solution_zip)
14+
15+
with zipfile.ZipFile(f'solution_files.zip', 'r') as zipped:
16+
zipped.extractall(f'solution_files/')
17+
18+
with open(f'solution_files/{solution_path}', 'r') as solution_txt:
19+
solution = solution_txt.read().replace('\n', ' \n')
20+
21+
return solution
22+
23+
def request_solution():
24+
response = requests.get(f'{host_url}/get_solution')
25+
solution = download_solution(solution_path='solution.txt')
26+
return solution
27+
28+
def does_it_work_at_all():
29+
start = time.time()
30+
solution = request_solution()
31+
assert len(solution) > 1, "The solution file is empty"
32+
print('Success:', time.time()-start, 'seconds')
33+
34+
subprocess.Popen("/opt/conda/bin/python /src/py/server.py &", shell=True)
35+
time.sleep(10)
36+
does_it_work_at_all()
37+

0 commit comments

Comments
 (0)