Skip to content

The πŸ”₯ Cannlytics Website 🌎 provides people with information about Cannlytics, cannabis analytics, and boasts a cannabis data market πŸ›οΈ

License

Notifications You must be signed in to change notification settings

cannlytics/cannlytics-website

Repository files navigation

Simple, easy, cannabis analytics.

https://cannlytics.com

License: MIT Version PyPI download month

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

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.

πŸͺ΄ Installation

In brief, installation entails:

  1. Cloning the repository.
  2. Setting your Firebase account credentials.
  3. Installing project dependencies and development tools.

1. Cloning the repository

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.

2. Installing project dependencies and development tools

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 new db.sqlite3 file if you do not have one already.

3. Setting your account credentials

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.

  1. Create an .env file in your project's root directory. See .env.example for an example with all credentials.

  2. 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
  3. Create a Firebase web app. We recommend using the same namespace you chose for your project, e.g. your-lims or your-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
  4. If you wish to leverage Cannlytics' email capabilities, then set EMAIL_HOST_USER and EMAIL_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 and EMAIL_HOST_PASSWORD respectively.

    # .env
    EMAIL_HOST_USER=admin@your-company.com
    EMAIL_HOST_PASSWORD=your-app-password
  5. 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
  6. Finish by creating a .firebaserc in the root directory with your Firebase Hosting references. See example.firebaserc for an example.

You are now ready to develop!

πŸ—οΈ Architecture

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

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;

  1. Create a fork of the repository.
  2. Edit the project by improving the codebase in some manner. Be creative.
  3. 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.

Editing the project

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

Running the project

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

Serving the project

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.

πŸ§ͺ Testing

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.

πŸš€ Publishing

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:

  1. 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
  1. 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}
  1. 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