Developer Setup Mac¶
This tutorial takes a new macOS install and gets you to running PlantTracer locally and on Amazon.
It makes use of the following services, which you will install:
Service |
Endpoint |
Purpose |
|---|---|---|
DynamoDBLocal.jar |
|
AWS DynamoDB Emulator |
Minio |
|
AWS S3 Emulator |
The following environment variables must be set to run Java programs on your Mac with homebrew (we install these below in your ~/.zshrc file):
Variable |
Value |
|---|---|
|
Must include |
|
Must include |
The DynamoDBLocal and Minio programs require that the following AWS variables be set. They can be set on the command line as environment variables (as is done in the Makefile), they can be set in your ~/.zshrc file, or they can be in your ~/.aws/credentials and ~/.aws/config files:
Variable |
Value |
|---|---|
|
|
|
|
|
|
|
|
|
|
You will also want to set these variables:
Variable |
Value for |
Purpose |
|---|---|---|
|
|
Bucket where videos are stored |
|
|
Prefix for all DynamoDB tables |
You may optionally set these variables:
Variable |
Value set for |
Purpose |
|---|---|---|
|
not set |
If set, Plant Tracer runs in demo mode and |
|
|
If set, all logging is at this log level |
Note that there are multiple ways that a single service can be sliced or partitioned:
A single server might have multiple Plant Tracer web app instances listening on different ports.
Each web app instance can store in its own S3 bucket. Alternatively, you can use the same S3 bucket for multiple web app instances, because each movie is stored with a UUID in the form
s3://{PLANTTRACER_S3_BUCKET/{COURSE_ID}/{MovieID/.Each web app instance stores its metadata in a set of DynamoDB tables that have a specific prefix. When testing with
pytest, tables are created with the randomized prefixtest-????where????is a randomly hexadecimal string.Within each web app instance, there can be one or more courses, each with its own course identifier (name).
Note that any course can be come a demo course. What makes it a demo course is that the web app instance has the DEMO_COURSE_ID environment variable set. This allows the same course to be accessed for non-demo purposes and demo purposes. When you access a web app instance that has the DEMO_COURSE_ID environment variable set, you are automatically authenticated as the demo user and can only access that user’s movies and public movies.
Mac Configuration¶
Prep your mac¶
Install developer tools.
From a clean install, open the Terminal window and type make. You should get an error message:
After a few moments, you’ll see this window. Click Install:
Agree to the license agreement.
Install
brewfrom https://brew.sh/
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
(You will need to enter your password and type RETURN/ENTER.)
Follow the instructions and type:
echo >> /Users/simsong/.zprofile
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/simsong/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
Install OpenJDK (you need it for DynamoDBLocal)
brew install openjdk
echo 'export PATH="/opt/homebrew/opt/openjdk/bin:$PATH"' >> ~/.zshrc
echo 'export CPPFLAGS="-I/opt/homebrew/opt/openjdk/include"' >> ~/.zshrc
source ~/.zshrc
Download and install the required software¶
Download the git repo.
% git clone --recursive https://github.com/Plant-Tracer/webapp.git
Cloning into 'webapp'...
remote: Enumerating objects: 10477, done.
remote: Counting objects: 100% (1404/1404), done.
remote: Compressing objects: 100% (577/577), done.
remote: Total 10477 (delta 866), reused 827 (delta 827), pack-reused 9073 (from 3)
Receiving objects: 100% (10477/10477), 106.38 MiB | 48.78 MiB/s, done.
Resolving deltas: 100% (7355/7355), done.
%
Use the macOS installer built into the Makefile to install additional software
cd webapp
make install-macos
Each time you reboot¶
Now you must start the servers. You will also need to do this every time you reboot and want to develop.
Start the local servers
make start_local_dynamodb
make start_local_minio
(First time through), make the local S3 bucket and verify it is there:
make make-local-bucket
make list-local-buckets
Validate the release¶
Check to make sure the commit that you checked out is valid:
make pylint
make pytest
If these do not work, speak with a maintainer, as the build is broken.
Developing locally¶
At this point you have checked out the git repo and verified that make pylint and make pytest run without error. This means:
You have all the necessary software installed.
DynamoDBlocal works
Minio works.
Plant Tracer’s webapp runs within a python virtual environment. You should cd into the top-level directory and activate the virtual environment in your command line shell, so that when you type python you run the correct python interpreter:
Note that our virtual environment is currently stored in the venv directory, although it is increasingly trendy to store the venv in .venv and we will be making this change at some point.
cd ~/gits/webapp # or wherever you have it installed
source venv/bin/activate
python --version # Verify you get the correct python
When you run pytest, a directory called var/ was created which contains the following files:
% ls -l var
total 22672
-rw-r--r--@ 1 simsong staff 6 Jul 12 13:40 dynamodb_local.pid
-rw-r--r--@ 1 simsong staff 6 Jul 12 13:40 minio.pid
drwxr-xr-x@ 3 simsong staff 96 Jul 12 13:40 planttracer-local
-rw-r--r--@ 1 simsong staff 10772480 Jul 12 13:40 shared-local-instance.db
dynamodb_local.pid— File containing the PID of the DynamoDBlocal processminio.pid— File containing the PID of the minio processplanttracer-local— Directory containing the objects in thes3://planttracer-local/Minio S3 bucket.shared-local-instance.db— SQLite 3.x database containing all of the objects in the DynamoDBlocal tables.
The bucket allows you to see how individual objects are stored in S3:
(venv) simsong@Seasons-2 webapp % find var/planttracer-local -type f
var/planttracer-local/demo-course/m0fa68c59-39ad-4166-8a07-7656b73ed74f_mp4.zip/xl.meta
var/planttracer-local/demo-course/m0fa68c59-39ad-4166-8a07-7656b73ed74f_mp4.zip/0fa25410-05dd-4f14-9eb5-bb3367f05b1f/part.1
var/planttracer-local/demo-course/m5dbcd625-06bb-4875-ae82-b218edfcb023.mov/8b6ab206-c2b0-4278-a931-b87bf49db6fd/part.1
var/planttracer-local/demo-course/m5dbcd625-06bb-4875-ae82-b218edfcb023.mov/xl.meta
var/planttracer-local/demo-course/mf97f7149-00d5-487e-8e75-df9b958a75ab/000000.jpg/xl.meta
var/planttracer-local/demo-course/mf97f7149-00d5-487e-8e75-df9b958a75ab.mov/xl.meta
var/planttracer-local/demo-course/mf97f7149-00d5-487e-8e75-df9b958a75ab.mov/6588491e-e797-4f0d-a16c-0aa65f742e52/part.1
var/planttracer-local/demo-course/mae9499f2-e9f2-4856-a353-ba26b04270ba.mov/xl.meta
var/planttracer-local/demo-course/mae9499f2-e9f2-4856-a353-ba26b04270ba.mov/931fdf84-c442-4896-8454-d21a3d94927d/part.1
var/planttracer-local/demo-course/m0fa68c59-39ad-4166-8a07-7656b73ed74f.mov/6922ff10-85d9-4e6b-a223-b0b33daa8595/part.1
var/planttracer-local/demo-course/m0fa68c59-39ad-4166-8a07-7656b73ed74f.mov/xl.meta
var/planttracer-local/demo-course/m0fa68c59-39ad-4166-8a07-7656b73ed74f/000000.jpg/xl.meta
(venv) simsong@Seasons-2 webapp %
There are two Makefile targets that you have to manage this:
make delete-local- Just deletes the local directory and doesn’t recreate anythingmake wipe-local- Wipes thevar/directory and recreates the local bucket.
Getting started¶
If you have reboot your computer, it’s likely that none the databases are running. To get going, you should do this:
make start_local_dynamodb
make start_local_minio
Here’s what it looks like when it runs:
(venv) simsong@Seasons-2 webapp % make start_local_dynamodb
bash bin/local_dynamodb_control.bash start
Starting DynamoDB Local...
Waiting for DynamoDBLocal to be ready (1)...
Waiting for DynamoDBLocal to be ready (2)...
DynamoDB Local is ready.
DynamoDB Local started in the background (PID: 12336).
DynamoDB Local endpoint: http://localhost:8010
(venv) simsong@Seasons-2 webapp % make start_local_minio
bash bin/local_minio_control.bash start
Starting Minio ...
Minio Local started in the background (PID: 12428).
Waiting for MinIO to be ready (1)...
Waiting for MinIO to be ready (2)...
Waiting for MinIO to be ready (3)...
Waiting for MinIO to be ready (4)...
Waiting for MinIO to be ready (5)...
MinIO is ready.
%
You can now create a web app instance within the DyanmoDBlocal and the Minio instances. This will:
Create all of the tables with a specific prefix (that you will specify)
Create a demo user with the email
demo@planttracer.com(created byodbmaint.create_course())Create an admin user for the course with the email
admin@planttracer.com(also created byodbmaint.create_course())Created a demo course with the CourseId
demo-courseCreate the demo movies (created by
dbutil.populate_demo_movies())
Notice that below we set all of the environment variables first. You might want to do this in a file that your source. _We do not recommend that you put this in your ~/.bashrc or ~/.zshrc files, becuase setting these variables will cause problems if you need to use Amazon Web Services for something else.
(venv) simsong@Seasons-2 webapp % AWS_ENDPOINT_URL_DYNAMODB=http://localhost:8010/\
AWS_ENDPOINT_URL_S3=http://localhost:9100/\
AWS_ACCESS_KEY_ID=minioadmin \
AWS_SECRET_ACCESS_KEY=minioadmin \
AWS_DEFAULT_REGION=us-east-1 \
PLANTTRACER_S3_BUCKET=planttracer-local \
DYNAMODB_TABLE_PREFIX=dev- \
LOG_LEVEL=DEBUG python dbutil.py --createdb
2025-08-10 09:43:16,090 odb.py:376 WARNING: NOTE: create_user does not check to make sure user admin@planttracer.com's course demo-course exists
Transaction succeeded: user inserted.
2025-08-10 09:43:16,131 odb.py:376 WARNING: NOTE: create_user does not check to make sure user demo@planttracer.com's course demo-course exists
Transaction succeeded: user inserted.
2025-08-10 09:43:16,154 odb.py:1302 WARNING: INEFFICIENT CALL. Just return movie_id.course_id
2025-08-10 09:43:16,309 odb.py:1302 WARNING: INEFFICIENT CALL. Just return movie_id.course_id
(venv) simsong@Seasons-2 webapp %
Notice that the admin user’s email is assumed to be admin@planttracer.com and the demo user’s email is demo@planttracer.com.
That WARNING: INEFFICIENT CALL is a note to the developer that this code could be cleaned up at some point in the future.
(We could have just run make make-local-demo, but here we expand so that you can see all of the variables.)
Now we can run the web app:
(venv) simsong@Seasons-2 webapp % AWS_ENDPOINT_URL_DYNAMODB=http://localhost:8010/ \
AWS_ENDPOINT_URL_S3=http://localhost:9100/ \
AWS_ACCESS_KEY_ID=minioadmin \
AWS_SECRET_ACCESS_KEY=minioadmin \
AWS_DEFAULT_REGION=us-east-1 \
PLANTTRACER_S3_BUCKET=planttracer-local \
DYNAMODB_TABLE_PREFIX=dev- \
LOG_LEVEL=DEBUG venv/bin/flask --debug --app deploy.app.bottle_app:app run --port 8080 --with-threads
2025-08-10 09:49:33,073 bottle_app.py:59 INFO: new Flask(__name__=webapp.deploy.app.bottle_app) log_level=DEBUG
2025-08-10 09:49:33,073 db_object.py:72 DEBUG: make_urn urn=s3://planttracer-local/
2025-08-10 09:49:33,073 bottle_app.py:60 INFO: make_urn('')=s3://planttracer-local/
* Serving Flask app 'deploy.app.bottle_app:app'
* Debug mode: on
2025-08-10 09:49:33,126 _internal.py:97 INFO: WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:8080
2025-08-10 09:49:33,127 _internal.py:97 INFO: Press CTRL+C to quit
2025-08-10 09:49:33,127 _internal.py:97 INFO: * Restarting with stat
2025-08-10 09:49:33,375 bottle_app.py:59 INFO: new Flask(__name__=webapp.deploy.app.bottle_app) log_level=DEBUG
2025-08-10 09:49:33,375 db_object.py:72 DEBUG: make_urn urn=s3://planttracer-local/
2025-08-10 09:49:33,375 bottle_app.py:60 INFO: make_urn('')=s3://planttracer-local/
2025-08-10 09:49:33,393 _internal.py:97 WARNING: * Debugger is active!
2025-08-10 09:49:33,402 _internal.py:97 INFO: * Debugger PIN: 594-409-847
(Once again, you could just do a make run-local-debug.)
Now connect to http://127.0.0.1:8080:
Alternatively, we could run in demo mode:
AWS_ENDPOINT_URL_DYNAMODB=http://localhost:8010/ AWS_ENDPOINT_URL_S3=http://localhost:9100/ AWS_ACCESS_KEY_ID=minioadmin AWS_SECRET_ACCESS_KEY=minioadmin AWS_DEFAULT_REGION=us-east-1 PLANTTRACER_S3_BUCKET=planttracer-local DYNAMODB_TABLE_PREFIX=dev- LOG_LEVEL=DEBUG DEMO_COURSE_ID=demo-course venv/bin/flask --debug --app deploy.app.bottle_app:app run --port 8080 --with-threads
And now if we go to http://127.0.0.1:8080 we see:
Click on the link and you see:
Creating a course and a user¶
While the application is running, open another window.
We will first use the --report option to see what is in the database:
simsong@Seasons-2 ~ % cd gits/webapp
simsong@Seasons-2 webapp % source venv/bin/activate
(venv) simsong@Seasons-2 webapp % AWS_ENDPOINT_URL_DYNAMODB=http://localhost:8010/ AWS_ENDPOINT_URL_S3=http://localhost:9100/ AWS_ACCESS_KEY_ID=minioadmin AWS_SECRET_ACCESS_KEY=minioadmin AWS_DEFAULT_REGION=us-east-1 PLANTTRACER_S3_BUCKET=planttracer-local DYNAMODB_TABLE_PREFIX=dev- LOG_LEVEL=INFO python dbutil.py --report
2025-08-10 09:53:21,580 odbmaint.py:347 WARNING: scan table dynamodb.Table(name='dev-api_keys')
2025-08-10 09:53:21,583 odbmaint.py:347 WARNING: scan table dynamodb.Table(name='dev-users')
2025-08-10 09:53:21,586 odbmaint.py:347 WARNING: scan table dynamodb.Table(name='dev-movies')
2025-08-10 09:53:21,588 odbmaint.py:347 WARNING: scan table dynamodb.Table(name='dev-movie_frames')
2025-08-10 09:53:21,592 odbmaint.py:347 WARNING: scan table dynamodb.Table(name='dev-courses')
2025-08-10 09:53:21,594 odbmaint.py:347 WARNING: scan table dynamodb.Table(name='dev-course_users')
2025-08-10 09:53:21,596 odbmaint.py:347 WARNING: scan table dynamodb.Table(name='dev-logs')
table .item_count count_table_items()
---------------- ------------- ---------------------
dev-api_keys 1 1
dev-users 2 2
dev-movies 2 2
dev-movie_frames 0 0
dev-courses 1 1
dev-course_users 2 2
dev-logs 0 0
(venv) simsong@Seasons-2 webapp %
Notice that our code automatically generates a WARNING of every table scan operaiton, as these operaitons are typically expensive in DynamoDB. This is just for development purposes. (The report option will be expanded over time.)
Notice that there is only one API_KEY but there are two users. This means that one of the users cannot log in. To facilitate local developiong, we want to use the --makelink option for the admin user.
Courses can be created the with dbutil --create_course command. Please review the source code in dbutil.py. You will see that this option requires the following arguments:
https://github.com/Plant-Tracer/webapp/blob/e46ac75396755687c50d4dc87f2358e9206031e1/dbutil.py#L127-L144
--course_id– a brief name of the course (e.g.PLANT101)--course_name— a longer name, (e.g."Introduction to Plant Movement")--admin_email— The admin’s email--admin_name— The admin’s name--max_enrollment— The maximum number of studnets (defaults to 50)
Let’s try it:
(venv) simsong@Seasons-2 webapp % AWS_ENDPOINT_URL_DYNAMODB=http://localhost:8010/ AWS_ENDPOINT_URL_S3=http://localhost:9100/ AWS_ACCESS_KEY_ID=minioadmin AWS_SECRET_ACCESS_KEY=minioadmin AWS_DEFAULT_REGION=us-east-1 PLANTTRACER_S3_BUCKET=planttracer-local DYNAMODB_TABLE_PREFIX=dev- LOG_LEVEL=INFO python dbutil.py --create_course --course_id PLANT101 --course_name "Introduction to Plant Movement" --admin_email "simsong@gmail
.com" --admin_name "Simson Garfinkel"
creating course...
2025-08-10 10:40:25,248 odb.py:376 WARNING: NOTE: create_user does not check to make sure user simsong@gmail.com's course PLANT101 exists
Transaction succeeded: user inserted.
created PLANT101
{
"admins_for_course": [
"u2a66cd2e-b18e-474c-b902-248530dd8612"
],
"course_id": "PLANT101",
"max_enrollment": "50",
"course_name": "Introduction to Plant Movement",
"course_key": "8f61-414e"
}
(venv) simsong@Seasons-2 webapp %
Now let’s try the report:
(venv) simsong@Seasons-2 webapp % AWS_ENDPOINT_URL_DYNAMODB=http://localhost:8010/ AWS_ENDPOINT_URL_S3=http://localhost:9100/ AWS_ACCESS_KEY_ID=minioadmin AWS_SECRET_ACCESS_KEY=minioadmin AWS_DEFAULT_REGION=us-east-1 PLANTTRACER_S3_BUCKET=planttracer-local DYNAMODB_TABLE_PREFIX=dev- LOG_LEVEL=INFO python dbutil.py --report
table .item_count count_table_items()
---------------- ------------- ---------------------
dev-api_keys 1 1
dev-users 3 3
dev-movies 2 2
dev-movie_frames 0 0
dev-courses 2 2
dev-course_users 3 3
dev-logs 0 0
(venv) simsong@Seasons-2 webapp %
Notice:
dev-usershas gone from 2 to 3dev-courseshas gone from 1 to 2dev-course_usershas gone from 2 to 3
To log in we will need a magic link that works with our existing endpoint (http://localhost:8080):
(venv) simsong@Seasons-2 webapp % AWS_ENDPOINT_URL_DYNAMODB=http://localhost:8010/ AWS_ENDPOINT_URL_S3=http://localhost:9100/ AWS_ACCESS_KEY_ID=minioadmin AWS_SECRET_ACCESS_KEY=minioadmin AWS_DEFAULT_REGION=us-east-1 PLANTTRACER_S3_BUCKET=planttracer-local DYNAMODB_TABLE_PREFIX=dev- LOG_LEVEL=INFO python dbutil.py --makelink simsong@gmail.com --planttracer_endpoint http://localhost:8080/
*****
***** Login with http://localhost:8080/list?api_key=a71358e5d3c6f453cb363d0aa69ce5664
*****
(venv) simsong@Seasons-2 webapp %
And I should be able to log in with this: