An open API service indexing awesome lists of open source software.

https://github.com/cannlytics/cannlytics-website

The ๐Ÿ”ฅ Cannlytics Website ๐ŸŒŽ provides people with information about Cannlytics, cannabis analytics, and boasts a cannabis data market ๐Ÿ›๏ธ
https://github.com/cannlytics/cannlytics-website

analytics cannabis cannabis-api cannabis-app cannabis-data cannabisapp data-marketplace django firebase metrc python statistics

Last synced: 4 days ago
JSON representation

The ๐Ÿ”ฅ Cannlytics Website ๐ŸŒŽ provides people with information about Cannlytics, cannabis analytics, and boasts a cannabis data market ๐Ÿ›๏ธ

Awesome Lists containing this project

README

          




Simple, easy, cannabis analytics.


[![License: MIT](https://img.shields.io/badge/License-MIT-darkgreen.svg)](https://opensource.org/licenses/MIT)
[![Version](https://img.shields.io/pypi/v/cannlytics.svg)](https://pypi.org/project/cannlytics)
[![PyPI download month](https://img.shields.io/pypi/dm/cannlytics.svg)](https://pypi.python.org/pypi/cannlytics/)

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](#introduction)
- [๐Ÿชด Installation](#installation)
- [๐Ÿ—๏ธ Architecture](#architecture)
- [๐Ÿšœ Development](#development)
* [Editing](#editing)
* [Running](#running)
* [Serving](#serving)
- [๐Ÿงช Testing](#testing)
- [๐Ÿš€ Publishing](#publishing)

## ๐Ÿ”ฅ Introduction

The `cannlytics` package is the core module implementing cannabis analytics logic. The `cannlytics` module handles [database interactions](#data), [file management](#storage), [authentication and authorization](#authentication), 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.](#cloning-the-repository)
2. [Setting your Firebase account credentials.](#setting-your-account-credentials)
3. [Installing project dependencies and development tools.](#installing-dependencies)

### 1. Cloning the repository

The best place to begin is to clone the repository and get a lay of the architecture.

```shell
git clone https://github.com/cannlytics/cannlytics.git
```

The architecture of the Cannlytics codebase is as follows.

```bash
โ”œโ”€โ”€ .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.

* [Python](https://www.python.org/psf/)
* [Docker](https://docs.docker.com/get-docker/)
* [Node.js](https://nodejs.org/en/about/)

The standard installation requires that you have an account with:

* [Firebase](https://firebase.google.com/)
* [Google Cloud Platform](https://cloud.google.com/gcp)

If you are developing Cannlytics, then you will need a couple extra tools:

* [Firebase Tools](https://firebase.google.com/docs/cli)
* [Google Cloud SDK](https://dl.google.com/dl/cloudsdk/channels/rapid/GoogleCloudSDKInstaller.exe)

We recommend using [Anaconda](https://docs.anaconda.com/anaconda/install/index.html) 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.

```bash
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](https://console.firebase.google.com/). 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](https://codelabs.developers.google.com/codelabs/cloud-run-django/index.html?index=..%2F..index#4).

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:
```py
# 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:

```bash
# .env
SECRET_KEY=xyz
```
3. [Create a Firebase web app](https://firebase.google.com/docs/web/setup). We recommend using the same namespace you chose for your project, e.g. `your-lims` or `your-lims-dev`, and setting up [Firebase Hosting](https://firebase.google.com/docs/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.
```bash
# .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](https://support.google.com/accounts/answer/185833/sign-in-with-app-passwords?hl=en) 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.
```bash
# .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](https://cloud.google.com/secret-manager), you will need to grant your service key *Secret Manager Admin* permissions in [Cloud IAM Admin](https://console.cloud.google.com/iam-admin/iam). For your security, this configuration file needs to be kept in a safe place.
```bash
# .env
GOOGLE_APPLICATION_CREDENTIALS=path/to/your/service/account.json
```
6. Finish by creating a `.firebaserc` in the root directory with your [Firebase Hosting](https://firebase.google.com/docs/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](https://www.djangoproject.com/) 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](https://docs.djangoproject.com/en/3.1/intro/tutorial03/#write-views-that-actually-do-something) 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](https://console.cloud.google.com/) services, including:

| Service | Purpose |
|---------|---------|
| [Firebase](https://firebase.google.com/) | Cloud services, such as [dynamic links](https://firebase.google.com/docs/dynamic-links). |
| [Firebase Authentication](https://firebase.google.com/docs/auth) | User authentication. |
| [Firebase Firestore](https://firebase.google.com/docs/firestore) | NoSQL database for real-time data management. |
| [Firebase Storage](https://firebase.google.com/docs/storage) | File storage. |
| [Firebase Hosting](https://firebase.google.com/docs/hosting) | Console, website, and documentation hosting. |
| [Cloud Build](https://cloud.google.com/build) | Used to containerize the app. |
| [Cloud Registry](https://cloud.google.com/container-registry) | Used to upload the app to storage. |
| [Cloud Run](https://cloud.google.com/run) | Used to run the app as a stateless container. |
| [Cloud Storage](https://cloud.google.com/storage) | Used for console and website file storage. |
| [Cloud Secret Manager](https://cloud.google.com/secret-manager) | Used for storing configurations and keeping secrets secret. |

The [standard Cannlytics data models](https://firebasestorage.googleapis.com/v0/b/cannlytics.appspot.com/o/public%2Flims%2Fdiagrams%2Fcannlytics_standard_data_models_0_0_8.svg?alt=media&token=de8e81a9-6250-4aac-857e-3936d26b4f1b) 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](https://docs.github.com/en/github/getting-started-with-github/quickstart/fork-a-repo) of the repository.
2. [Edit the project](#edit) by improving the codebase in some manner. Be creative.
3. [Create a pull request](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-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](https://python-markdown.github.io/extensions/) for more information on rendering Markdown. For help with storing static files, see [serving static files on App Engine](https://cloud.google.com/appengine/docs/standard/python3/serving-static-files). You can configure static files to be served from [Firebase Storage](https://firebase.google.com/docs/storage) instead of from [Firebase Hosting](https://firebase.google.com/docs/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:

```shell
set PROJECT=website
python manage.py collectstatic --noinput
```

or

```shell
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:

```shell
set PROJECT=website
python manage.py runserver
```

or

```shell
npm run dev
```

You can also leverage [django-livereload-server](https://github.com/tjwalch/django-livereload-server) for hot-reloading while you develop.

```shell
npm run start
```

Hot-reloading is an important tool of development. You can use `django-livereload-server`, which uses both [python-livereload](https://github.com/lepture/python-livereload) and [django-livereload](https://github.com/Fantomas42/django-livereload), for smooth reloading. You can install [django-live-reload-server](https://github.com/tjwalch/django-livereload-server) with:

```shell
pip install django-livereload-server
```

You can start hot-reloading by starting the `livereload` server:

```shell
set PROJECT=website
python manage.py livereload
```

In another console, you start the Django development server as usual:

```shell
set PROJECT=website
python manage.py runserver
```

You can build the static assets, JavaScript and CSS, utilizing [Webpack](https://webpack.js.org/). 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:

```shell
webpack-dev-server --env production=False
```

or

```shell
npm run serve
```

It is an inconvenience to run multiple consoles, but a major convenience to have smooth hot-reloading. So, [`npm-run-all`](https://www.npmjs.com/package/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:

```shell
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](#publishing). Below are general instructions for building and serving the project in a [Docker container image](https://docs.docker.com/get-started/02_our_app/#build-the-apps-container-image).

First, build the Docker container image:

```shell
# build docker image
docker build -t cannlytics .
```

You can register the container with:

```shell
# docker push to container registry.
docker push cannlytics
```

You can [run the application container](https://docs.docker.com/get-started/02_our_app/#start-an-app-container) locally with:

```shell
# run docker
docker run -dp 8080:8080 --env-file docker.env cannlytics
```

Finally, you can quickly run the container, or multiple containers, with:

```shell
# 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:

```shell
python manage.py check
```

You can run tests for a specific app with.

```shell
python manage.py test your_app_name
```

You can also build the platform in a docker container for your specific purposes:

```shell
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](https://docs.cannlytics.com/developers/publishing/) for complete instructions on how to publish Cannlytics for production. The guide is based on the [Running Django on Cloud Run guide](https://cloud.google.com/python/django/run#windows). After setup, publishing is done with one command:

```shell
npm run publish
```

If you need to change accounts or projects, then you can use:

```shell
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](https://cloud.google.com/build) by running the following command from the directory containing the Dockerfile:

```shell
gcloud builds submit --tag gcr.io/your-lims/cannlytics
```

2. Deploy the container image to Cloud Run.

```shell
gcloud beta run deploy your-lims --image gcr.io/your-lims/cannlytics --region us-central1 --allow-unauthenticated --service-account=${GCS_SA}
```

3. 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.

```shell
firebase deploy --only hosting:production
```