Key Concepts
Understanding these concepts before reading any code will save you hours of confusion.
1. One database = one agency = one server
This is the single most important architectural fact about Orchid.
Each fertility agency gets its own isolated MySQL database and its own dedicated AWS Elastic Beanstalk instance. There is no shared database. No agency can ever see another agency’s data — not through a bug, not through a misconfigured query, because the databases are physically separate.
Agency A → EB Instance A → MySQL Database AAgency B → EB Instance B → MySQL Database BAgency C → EB Instance C → MySQL Database CConsequence for queries: Because each server only ever connects to one agency’s database, you will see this pattern constantly:
agency = models.Agency.query.first()This is not lazy code — it is the correct pattern. There is always exactly one Agency record in the database, because this database belongs to exactly one agency.
Consequence for cron jobs: cronapp.py is the exception. It runs once and iterates all databases by doing a SHOW DATABASES and creating a dynamic SQLAlchemy engine for each one. If you’re debugging a cron job, remember it runs against every agency.
2. The g object — the request context carrier
Flask’s g is a per-request object that lives for the duration of one HTTP request and is then discarded. Orchid uses it as the primary way to pass context through the call stack without threading it through function arguments.
The before_request hook in flaskapp.py populates g at the start of every request:
g.request_id # UUID4 — unique per request, used for log correlationg.request_start_time # monotonic timestamp for request durationg.agency # The Agency model instance for this serverg.admin # The logged-in Admin model (or None)g.user # The logged-in User model (or None)g.provider # The logged-in Provider model (or None)g.np_user # Non-participating user (or None)g.third_party # Third-party user (or None)g.client # Browser/mobile info (is_mobile, ip_address, browser_class)g.today # date.today() — use this, not date.today() directlyg.current_time # datetime.utcnow()g.FORM_KEY # Encryption key for form fieldsg.cloudfront_available # Whether CloudFront is configuredWhy does this matter? When you see g.admin in a view function, you don’t need to pass the admin in — it is already there from before_request. When writing utility functions, import from flask import g and access it directly. Never pass g as a function argument — just use it.
3. _LOCAL_MODE — dev vs. prod behavior
Many side effects are disabled in local development. The _LOCAL_MODE boolean flag (set from the config file) is checked throughout the codebase:
if not _LOCAL_MODE: send_email_via_sendgrid(...) # Only in production upload_to_s3(...) # Only in production log_to_cloudwatch(...) # Only in productionIn local mode: email sending is silenced (or logged to console), S3 uploads are skipped, CloudWatch logging falls back to console. This is why your local dev environment works without real AWS credentials.
The flag is set in flaskapp.py based on whether _LOCAL is in the config. If localflaskapp.cfg exists, it enables local mode automatically.
4. Blueprint pairs — views + API
Every major feature has two Python files:
orchid/views/matching.py → HTML pages (GET routes, returns render_template())orchid/api/matching.py → JSON API (mostly POST routes, returns jsonify())The views file handles page rendering. The API file handles data mutations and AJAX calls from the page. They register as separate Flask blueprints with different URL prefixes (/admin/matching vs /api/matching).
When you need to find where a button’s click handler goes, look in the API file for the feature. When you need to find how a page is rendered, look in the views file.
5. The Crudler pattern
orchid/utils/flaskutils.py contains a Crudler class that wraps SQLAlchemy operations with automatic Flask context:
from orchid.utils.flaskutils import Crudler
# Instead of:db.session.add(obj)db.session.commit()
# Use:Crudler.add(obj)Crudler.commit()Crudler automatically extracts user_id, user_type, user_agent, and request path from the Flask context and passes them to model-level change tracking. It also handles rollback and error logging on commit failure.
6. large_dicts.py — the giant config file
orchid/large_dicts.py is a 653 KB Python file containing lookup tables, display name mappings, and configuration data used throughout the app. Things like:
- All valid admin role types
- Case stage definitions with their display names and URL slugs
- Country/state/ethnicity lists
- Document type mappings
- Status label configs
When you see code like large_dicts.SOME_DICT[some_key], the value comes from this file. Before writing a hardcoded string, check if there’s already a dict in large_dicts.py for it.
7. Model inheritance and mixins
Most models extend BaseModel (defined in orchid/models/__init__.py), which provides:
idprimary keyadd(),update(),delete()instance methods with change trackingcreated_at/updated_attimestamps (on most models)
There are also mixins in orchid/models/mixins.py that add specific capabilities:
AddressMixin— street/city/state/zip/country fieldsDocumentMixin— S3 file reference fieldsFlagsMixin— boolean flag columnsTaskMixin— task completion trackingEncryptedSsnMixin— encrypted SSN storage
When adding new models, always check if an existing mixin covers what you need before adding raw columns.
8. Error models
orchid/models/__init__.py defines three error tracking tables:
Errors— general application errorsErrAPI— API-level errorsErr400— 400-series HTTP errors
They all use content-based deduplication: the same error message only creates one record. This prevents runaway error floods. The add_error() function handles this automatically — call it instead of just logging.
9. Jinja filters and macros
There are 70+ custom Jinja filters registered in orchid/jinja_filters.py. Before writing Python logic inside a template, check whether a filter already does what you need:
{{ amount | currency }} {# $1,234.56 #}{{ date_obj | date_readability }} {# January 15, 2025 #}{{ phone | phone_readability }} {# (555) 123-4567 #}{{ s3_key | cloudfront }} {# presigned CloudFront URL #}Similarly, check templates/macros.html (and the other macro files) before writing raw HTML. The macro system covers modals, file uploaders, date pickers, page headers, cards, and much more.
See Jinja Filters Reference and Jinja Macros for complete lists.