Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/jhonyes04/new-wayfy/llms.txt

Use this file to discover all available pages before exploring further.

WayFy’s backend persists all application data in a relational database managed through SQLAlchemy and Flask-Migrate (Alembic). PostgreSQL is the recommended engine for both local development and production, but the stack also supports SQLite for quick local testing. The connection is configured entirely through the DATABASE_URL environment variable, and schema changes are versioned as Alembic migration files in backend/migrations/versions/ — there are currently eleven migration revisions tracking the full history of the schema.

Supported Database Engines

Running Migrations

WayFy uses Flask-Migrate, which wraps Alembic and ties migration commands into the Flask CLI. All migration operations are run from the backend/ directory with Pipenv using the scripts defined in backend/Pipfile.
1

Apply all pending migrations

Run the upgrade script to bring your database schema up to the latest revision. This is the command you run after cloning the repo for the first time, and after pulling new migration files from teammates:
cd backend
pipenv run upgrade
Internally this runs flask db upgrade, which applies any unapplied migration files in backend/migrations/versions/ in order.
2

Generate a new migration after model changes

After modifying a SQLAlchemy model in backend/src/api/models/, auto-generate a new migration file by running:
cd backend
pipenv run migrate
Alembic compares the current model definitions against the last known schema state and writes a new versioned file in backend/migrations/versions/. Always review the generated file before committing — Alembic cannot detect all changes (e.g. column renames) automatically.
3

Roll back the last migration

If you need to undo the most recent migration (e.g. to revise a model change before it is merged), run:
cd backend
pipenv run downgrade
This runs flask db downgrade, reverting one revision. Run it repeatedly to step back through multiple revisions.
4

Reset the database (development only)

To drop all tables and re-apply every migration from scratch, use the reset_db script:
cd backend
pipenv run reset_db
reset_db is destructive — it permanently deletes all data in the database. Never run this against a production database.

Seeding Test Data

WayFy provides two Flask CLI commands for populating a development database with realistic data:

Insert Test Users

Creates a specified number of test user accounts with hashed passwords:
cd backend
flask insert-test-users 5
Pass any integer to control how many users are created. Useful for testing pagination, admin dashboards, and user management flows.

Insert Test Data

Runs the full seed script from backend/src/commands.py, which populates places, trips, accessibility reviews, and related records:
cd backend
pipenv run insert-test-data
Use this to get a fully populated local environment with representative data across all eight tables.

Data Model Reference

WayFy’s schema spans eight tables. The diagram below shows the relationships, followed by a detailed field reference for each model.
users
 ├── user_favorites         (user_id → users.id)
 ├── accessibility_reviews  (last_modified_by_id → users.id)
 │    └── accessibility_photos (review_id → accessibility_reviews.id,
 │                              user_id → users.id)
 └── trips                  (user_id → users.id,
                              original_trip_id → trips.id self-FK)
      └── trip_days          (trip_id → trips.id)
           └── trip_day_places (trip_day_id → trip_days.id,
                                favorite_id → user_favorites.id)

places                       (standalone, created_by → users.id)
Stores WayFy account records. Passwords are stored as bcrypt hashes — never plain text.
ColumnTypeNotes
idintegerPrimary key
firstnamestring(50)User’s first name
lastnamestring(100)User’s last name
emailstring(120)Unique; used as login credential
passwordstring(255)bcrypt hash
selected_mobilitytext (JSON)User’s mobility profile stored as a JSON array string (e.g. '["wheelchair", "walker"]'); parsed to a list on serialize
avatarstring(255)Filename served from /api/users/avatar/<filename>; defaults to default_avatar.png
is_activebooleanWhether the account is active
is_adminbooleanGrants access to admin endpoints and AdminRoute pages
Saved places for a user, sourced from OpenStreetMap. The combination of user_id + osm_id is enforced as unique via a UniqueConstraint, preventing duplicate saves of the same place by the same user.
ColumnTypeNotes
idintegerPrimary key
user_idintegerFK → users.id
osm_idstring(50)OpenStreetMap element identifier; always stored as a string
place_namestring(200)Display name of the place
place_labelstring(100)Full formatted address label
longitudefloatWGS 84 longitude
latitudefloatWGS 84 latitude
wheelchairstring(20)OSM wheelchair tag value (e.g. yes, limited, no)
osm_typestring(20)OSM element type (node, way, relation)
sub_typestring(100)Place category (e.g. restaurant, museum)
all_tagstext (JSON)Full OSM tags dictionary as a JSON string
created_atdatetimeTimestamp when the favorite was saved
Community-submitted accessibility assessments for a place identified by osm_id. The osm_id column has a unique index — one review record per OSM element.
ColumnTypeNotes
idintegerPrimary key
osm_idstring(50)Unique OSM identifier for the reviewed place
osm_typestring(10)OSM element type; defaults to 'node'
place_namestring(200)Display name at time of review
last_modified_by_idintegerFK → users.id — most recent editor
wheelchairstring(10)Overall wheelchair accessibility rating (yes, limited, no)
has_rampbooleanEntrance ramp present
has_elevatorbooleanElevator available
has_accessible_toiletbooleanAccessible toilet on premises
has_accessible_parkingbooleanDesignated accessible parking available
automatic_doorbooleanAutomatic or power-assisted door at entrance
descriptiontextFree-text notes from the reviewer
created_atdatetimeFirst submission timestamp
updated_atdatetimeLast update timestamp
Photos attached to an accessibility review, uploaded via Cloudinary.
ColumnTypeNotes
idintegerPrimary key
review_idintegerFK → accessibility_reviews.id
user_idintegerFK → users.id — uploader
filenamestring(255)Cloudinary public ID or filename; served from /api/accessibility/photos/<filename>
captionstring(200)Optional descriptive caption
created_atdatetimeUpload timestamp
User-created itineraries. Trips can be public or private, and a trip can be forked from another via original_trip_id.
ColumnTypeNotes
idintegerPrimary key
user_idintegerFK → users.id — owner
titlestring(200)Trip name
descriptiontextOptional overview text
is_publicbooleanWhether the trip is visible to other users; defaults to false
original_trip_idintegerSelf-FK → trips.id; set when a trip is forked/cloned; nullable
cover_imagestring(200)Filename served from /api/trips/cover/<filename>; nullable
created_atdatetimeCreation timestamp
updated_atdatetimeLast modification timestamp
Individual days within a trip, each representing one day of the itinerary. Days are ordered by day_number.
ColumnTypeNotes
idintegerPrimary key
trip_idintegerFK → trips.id
day_numberintegerSequential day index within the trip
datedateCalendar date for this day (optional)
titlestring(200)Optional day label (e.g. “Arrival Day”)
notestextFree-text notes for the day
Ordered places visited on a specific trip day. Each entry links a day to either a saved favorite or a raw place coordinate. Places within a day are ordered by the order column.
ColumnTypeNotes
idintegerPrimary key
trip_day_idintegerFK → trip_days.id
favorite_idintegerFK → user_favorites.id; nullable — not set when a place is added without saving it as a favorite
place_namestring(200)Display name of the place
latitudefloatWGS 84 latitude
longitudefloatWGS 84 longitude
osm_idstring(50)OSM identifier; may be null for manually added places
sub_typestring(100)Place category
orderintegerPosition within the day’s itinerary
notestextVisit-specific notes
visit_timetimePlanned arrival time
visit_time_endtimePlanned departure time
User-submitted places not yet in OpenStreetMap, subject to admin moderation. New submissions start as pending; admins approve them via the Flask-Admin interface, after which they appear on the map as the community-approved layer.
ColumnTypeNotes
idintegerPrimary key
namestring(200)Place name
longitudefloatWGS 84 longitude
latitudefloatWGS 84 latitude
sub_typestring(100)Place category
place_labelstring(100)Full formatted address label
created_byintegerFK → users.id — submitter
statusstring(20)'pending' (awaiting review) or 'approved' (visible to all users)
created_atdatetimeSubmission timestamp

Alembic Migration Workflow

Always commit your migration files alongside the model changes that produced them. If two developers generate migrations independently from the same base revision, Alembic will detect a branch and refuse to upgrade until the branches are merged with flask db merge.
The full migration lifecycle for a typical model change looks like this:
# 1. Edit a model in backend/src/api/models/

# 2. Generate the migration file
cd backend
pipenv run migrate
# → Creates backend/migrations/versions/<hash>_description.py

# 3. Review the generated file
cat backend/migrations/versions/<hash>_description.py

# 4. Apply the migration to your local database
pipenv run upgrade

# 5. Commit both the model change and the migration file
git add src/api/models/<name>_model.py migrations/versions/<hash>_description.py
git commit -m "feat: add cover_image column to trips"
To target a specific Alembic revision rather than always stepping one up or down, use Flask’s db commands directly:
# Upgrade to a specific revision
pipenv run flask db upgrade <revision_hash>

# Downgrade to a specific revision
pipenv run flask db downgrade <revision_hash>

# Show current revision
pipenv run flask db current

# Show full migration history
pipenv run flask db history

Build docs developers (and LLMs) love