The π₯ Cannlytics Website provides an aesthetic user interface for users to learn about Cannlytics and get set up with everything that they need to utilize Cannlytics to its fullest extent. This documentation explains the Cannlytics Website architecture and how to build, develop, and publish the website.
- π₯ Introduction
- πͺ΄ Installation
- ποΈ Architecture
- π Development
- π§ͺ Testing
- π Publishing
The cannlytics
package is the core module implementing cannabis analytics logic. The cannlytics
module handles database interactions, file management, authentication and authorization, traceability, data importing and exporting, and the logic for all workflows, such as certificate creation, item transfers, and publishing results. The api
is the interface between the user application and the cannabis analytics logic of cannlytics
. The console
is the user application where user's can interface with the infrastructure, such as the database, and utilize the cannabis analytics logic. The docs
provide information about the project and the website
provides people with information about cannabis analytics. You can also use the ai
to automatically collect, augment, and make inferences from data.
In brief, installation entails:
- Cloning the repository.
- Setting your Firebase account credentials.
- Installing project dependencies and development tools.
The best place to begin is to clone the repository and get a lay of the architecture.
git clone https://github.com/cannlytics/cannlytics.git
The architecture of the Cannlytics codebase is as follows.
βββ .admin
β βββ tokens
β βββ your-firebase-service-account.json
βββ .firebase
β βββ firestore.indexes # Available database queries.
β βββ firestore.rules # Database access control and data validation.
| βββ storage.rules # File storage access control and validation.
βββ ai
β βββ curation # Tools for automatically cleaning and augmenting data.
β βββ functions # Tools for automatically collecting data.
| βββ inference # Tools for automatically making inferences from data.
βββ api
β βββ {endpoint}
β | βββ {endpoint}.py # Implementation of specific API endpoints.
β βββ urls.py # Defined API endpoints.
| βββ views.py # Implementation of general API endpoints.
βββ cannlytics
β βββ auth # Core authentication logic.
β βββ ccrs # Interface to Washington State's traceability system.
β βββ charts # Crispy, ready-to-use charts.
β βββ data # Data logic, from wrangling to market.
β βββ firebase # Interface to Firebase.
β βββ lims # All laboratory information management (LIMS) logic.
β βββ metrc # Interface to the Metrc traceability system.
β βββ models # Defined models.
β βββ paypal # Interface to PayPal.
β βββ quickbooks # Interface to QuickBooks.
β βββ stats # Nifty, ready-to-use statistical models.
β βββ utils # General utility functions.
β βββ cannlytics.py # Core Cannlytics interface.
| βββ requirements.txt # Package-specific requirements.
βββ console
β βββ assets
β | βββ css # Stylesheets that will be minified and bundled.
β | βββ js # JavaScript that will be bundled into a `cannlytics.min.js` module.
β βββ core # Required Django configuration.
β βββ static/console # Static files, such as images and Excel templates.
β βββ templates/console # User interface templates.
β βββ templatetags # Custom Django template utility functions.
β βββ views # Implementation of user interfaces and their context.
β | βββ {view}.py # Views for specific purposes.
β βββ db.sqlite3 # Unused SQL database.
β βββ Dockerfile # Production configuration.
β βββ requirements.txt # Console-specific requirements.
β βββ settings.py # Django configuration.
β βββ state.py # Static text for certain pages and sections.
β βββ urls.py # Console navigation.
| βββ webpack.config.js # Build configuration.
βββ docs
β βββ {src} # Specific documentation.
β βββ theme # The style of the documentation site.
| βββ Dockerfile # Documentation container configuration.
βββ node_modules
βββ public # Files hosted with Firebase hosting.
βββ tests # Tests for all features and functionality.
βββ tools # Administrative, developer, and data management tools.
βββ website # A company website with the same structure as the `console`.
βββ .env # Your secrets.
βββ .firebasesrc # Firebase hosting sources.
βββ .gitignore # Files not committed to GitHub.
βββ firebase.json # Firebase configuration file.
βββ manage.py # Django utility script.
βββ mkdocs.yaml # Documentation configuration.
βββ package.json # Node.js dependencies and scripts.
βββ requirements.txt # All Python requirements.
βββ setup.py # Python SDK configuration.
You will need to install the following technologies when developing or creating an installation of Cannlytics. We recommend using the latest stable version of each piece of software whenever possible. Cannlytics has been tested with Python 3.9.
The standard installation requires that you have an account with:
If you are developing Cannlytics, then you will need a couple extra tools:
We recommend using Anaconda if you are developing Cannlytics so that you can create a virtual environment to test, develop, and use the Cannlytics Console in isolation and in a reproducible manner. After installing Anaconda, you can open a terminal and run the following commands to create a ready-to-go environment.
conda create --name cannlytics python=3.9
conda activate cannlytics
pip install -r requirements.txt
python manage.py migrate
npm install
Note that
python manage.py migrate
creates a newdb.sqlite3
file if you do not have one already.
For a standard setup, you will need to create a Firebase account and a project. We recommend choosing a project name that is in kebab-case, e.g. your-lims
, so that you can safely use the project name in many places. Now, follow these steps to fill in your environment variables. When publishing, you will need to copy pertinent environment variables to a Google Cloud Secret.
-
Create an
.env
file in your project's root directory. See.env.example
for an example with all credentials. -
Create a Django secret key:
# tools/quickstart.py from django.utils.crypto import get_random_string # Generate a secret key for your project. chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)' generated_secret_key = get_random_string(50, chars) print(generated_secret_key)
And save it in your
.env
file:# .env SECRET_KEY=xyz
-
Create a Firebase web app. We recommend using the same namespace you chose for your project, e.g.
your-lims
oryour-lims-dev
, and setting up Firebase Hosting with your app. Once you have created a Firebase web app, navigate to your Firebase project settings, find your Firebase App Config, and set your Firebase configuration in your.env
file.# .env FIREBASE_API_KEY=xyz FIREBASE_AUTH_DOMAIN=your-lims.firebaseapp.com FIREBASE_DATABASE_URL=https://your-lims.firebaseio.com FIREBASE_PROJECT_ID=your-lims FIREBASE_STORAGE_BUCKET=your-lims.appspot.com FIREBASE_MESSAGING_SENDER_ID=123 FIREBASE_APP_ID=123 FIREBASE_MEASUREMENT_ID=G-abc
-
If you wish to leverage Cannlytics' email capabilities, then set
EMAIL_HOST_USER
andEMAIL_HOST_PASSWORD
environment variables in your.env
file. It is recommended that you create an app password if you are using Gmail. After you have created your app password, set your email and app password as environment variables,EMAIL_HOST_USER
andEMAIL_HOST_PASSWORD
respectively.# .env EMAIL_HOST_USER=admin@your-company.com EMAIL_HOST_PASSWORD=your-app-password
-
Finally, to facilitate Firebase management, create and download a service account and save the path to your service account as a
GOOGLE_APPLICATION_CREDENTIALS
environment variable. For accessing secrets, you will need to grant your service key Secret Manager Admin permissions in Cloud IAM Admin. For your security, this configuration file needs to be kept in a safe place.# .env GOOGLE_APPLICATION_CREDENTIALS=path/to/your/service/account.json
-
Finish by creating a
.firebaserc
in the root directory with your Firebase Hosting references. Seeexample.firebaserc
for an example.
You are now ready to develop!
The Cannlytics Console and Cannlytics Website are built with the Django framework and generally follow a model-template-view (MTV) architectural pattern, where:
Abstraction | Description |
---|---|
Model | The cannlytics package, Django, and all assets , such as JavaScript and CSS, that contain the core logic, which is provided to the views. |
Template | Django HTML files that describe the display and how data are presented. |
View | Python functions that control the model's logic, specify and provide data to templates, and manage user requests. Django describes views as follows: "Your view can read records from a database, or not. It can use a template system such as Django's β or a third-party Python template system β or not. It can generate a PDF file, output XML, create a ZIP file on the fly, anything you want, using whatever Python libraries you want." |
Cannlytics utilizes a number of Google Cloud services, including:
Service | Purpose |
---|---|
Firebase | Cloud services, such as dynamic links. |
Firebase Authentication | User authentication. |
Firebase Firestore | NoSQL database for real-time data management. |
Firebase Storage | File storage. |
Firebase Hosting | Console, website, and documentation hosting. |
Cloud Build | Used to containerize the app. |
Cloud Registry | Used to upload the app to storage. |
Cloud Run | Used to run the app as a stateless container. |
Cloud Storage | Used for console and website file storage. |
Cloud Secret Manager | Used for storing configurations and keeping secrets secret. |
The standard Cannlytics data models and their default fields are shown below. Cannlytics data models are highly flexible and can contain fewer, different, or additional fields.
Development can happen in many avenues. Frequent, small scope pull requests are encouraged. Any contribution, even if it needs future polishing, helps build the project and advance cannabis science. In general;
- Create a fork of the repository.
- Edit the project by improving the codebase in some manner. Be creative.
- Create a pull request for your changes to be reviewed and merged into the project upon approval or for you to receive feedback on how your changes can be approved.
The table below lists available development commands.
Command | Purpose |
---|---|
start |
Start the complete website development environment. |
serve |
Serve the assets with webpack-dev-server . |
livereload |
Serve Django with django-livereload-server . |
dev |
Serve Django with runserver . |
build |
Build the assets for production with webpack . |
collectstatic |
Gather all of the Django static files into the public directory. |
container |
Build a container image for the app. |
cloud |
Run the container image in the cloud. |
deploy |
Direct requests to the running container image. |
publish |
Perform all publishing steps: build , container , cloud , and deploy . |
All text material is either stored in JSON in state.py
or written in Markdown in docs
directories. See python-markdown
Extensions for more information on rendering Markdown. For help with storing static files, see serving static files on App Engine. You can configure static files to be served from Firebase Storage instead of from Firebase Hosting in console/settings.py
or website/settings.py
. If you modify assets, then you can gather all supporting files, located in each app's static
directory, into the public/static
directory with:
set PROJECT=website
python manage.py collectstatic --noinput
or
npm run collectstatic
The simplest way to run the app is to open a command line from the project's root directory, set a PROJECT
environment variable, and run:
set PROJECT=website
python manage.py runserver
or
npm run dev
You can also leverage django-livereload-server for hot-reloading while you develop.
npm run start
Hot-reloading is an important tool of development. You can use django-livereload-server
, which uses both python-livereload and django-livereload, for smooth reloading. You can install django-live-reload-server with:
pip install django-livereload-server
You can start hot-reloading by starting the livereload
server:
set PROJECT=website
python manage.py livereload
In another console, you start the Django development server as usual:
set PROJECT=website
python manage.py runserver
You can build the static assets, JavaScript and CSS, utilizing Webpack. The JavaScript bundle is a JavaScript module and is callable from the user interface with the cannlytics
namespace. You can run the build for development with:
webpack-dev-server --env production=False
or
npm run serve
It is an inconvenience to run multiple consoles, but a major convenience to have smooth hot-reloading. So, npm-run-all
is used to run multiple servers in the same console for smooth development. When you are setup, you can run the project for development simply with:
npm run start
You can serve the built project from anywhere you desire. The default option is to serve the project from Google App Engine, outlined below in publishing. Below are general instructions for building and serving the project in a Docker container image.
First, build the Docker container image:
# build docker image
docker build -t cannlytics .
You can register the container with:
# docker push to container registry.
docker push cannlytics
You can run the application container locally with:
# run docker
docker run -dp 8080:8080 --env-file docker.env cannlytics
Finally, you can quickly run the container, or multiple containers, with:
# bring up containers
docker-compose up -d --build
# bring down
docker-compose down
# logs
docker-compose logs
Congratulations, you can now build and run Cannlytics just about anywhere! The sky is the limit.
You can check for errors detectable by Django with:
python manage.py check
You can run tests for a specific app with.
python manage.py test your_app_name
You can also build the platform in a docker container for your specific purposes:
docker build . --tag gcr.io/your-lims/cannlytics
gcloud auth configure-docker
docker push gcr.io/your-lims/cannlytics
Perusing the tests
directory is actually a good place to find examples on how to use Cannlytics and can be a good place to begin your explorations.
See the publishing guide for complete instructions on how to publish Cannlytics for production. The guide is based on the Running Django on Cloud Run guide. After setup, publishing is done with one command:
npm run publish
If you need to change accounts or projects, then you can use:
gcloud config set account `ACCOUNT`
gcloud config set project `PROJECT ID`
The build process contains three steps:
- Containerize the app and upload it to Container Registry.
Build your container image using Cloud Build by running the following command from the directory containing the Dockerfile:
gcloud builds submit --tag gcr.io/your-lims/cannlytics
- Deploy the container image to Cloud Run.
gcloud beta run deploy your-lims --image gcr.io/your-lims/cannlytics --region us-central1 --allow-unauthenticated --service-account=${GCS_SA}
- Direct hosting requests to the containerized app.
This step provides access to this containerized app from a [Firebase Hosting] URL, so the app can generate dynamic content for the Firebase-hosted site.
firebase deploy --only hosting:production