Skip to content

Cannlytics Firebase Module

The cannlytics.firebase module is a wrapper of the firebase_admin package to make interacting with Firebase services, such as Firestore databases and Firebase Storage buckets, even easier. Firebase is initialized with firebase.initialize_firebase, which returns a Firestore database instance. The following is a simple example of how to initialize a Firestore database.

from cannlytics.firebase import initialize_firebase

# Initialize Firebase with a .env file.
# with a `GOOGLE_APPLICATION_CREDENTIALS`
# variable of the path of your service account.
database = initialize_firebase('./env')

You will need to provide credentials for your application by setting the GOOGLE_APPLICATION_CREDENTIALS environment variable. Set the environment variable GOOGLE_APPLICATION_CREDENTIALS to the file path of the JSON file that contains your service account key. This variable only applies to your current shell session, so if you open a new session, set the variable again.

Function Description
initialize_firebase(env_file=None, key_path=None, bucket_name=None, project_id=None) Initialize Firebase, unless already initialized. Searches for environment credentials if key_path is not specified.

Firestore

The Firestore functions utilize create_reference to turn a path into a document or collection reference, depending on the length of the path. Odd length paths refer to collections and even length paths refer to documents. For example, users is a collection of users, users/{uid} is a user's document, and users/{uid}/logs is a sub-collection of logs for the user. With this functionality, you can easily get documents as follows.

# Get all user documents.
users = firebase.get_collection("users")

# Get a document.
user = firebase.get_document("users/xyz")

And create or update documents as follows.

from datetime import datetime

# Create a user log.
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
firebase.update_document(f"users/xyz/logs/{timestamp}", {
  "activity": "Something happened",
  "created_at": timestamp,
  "updated_at": timestamp
})

# Update the user.
firebase.update_document(f"users/xyz", {
  "recent_activity": timestamp,
})

If you need to work with arrays or simply increment a value, then there are utility functions for you.

# Add an element to an array in a document.
firebase.add_to_array("tests/firebase_test", "likes", "Testing")
data = firebase.get_document("tests/firebase_test")

# Remove an element from an array in a document.
firebase.remove_from_array("tests/firebase_test", "likes", "Sandals")
data = firebase.get_document("tests/firebase_test")

# Increment a value in a document.
firebase.increment_value("tests/firebase_test", "runs")
data = firebase.get_document("tests/firebase_test")

You can query a collection of documents.

# Get a collection.
limit = 1000
order_by = "time"
filters = [{
  "key": "test",
  "operation": "==",
  "value":
  "firebase_test",
}]
docs = firebase.get_collection("tests", limit=limit, order_by=order_by, filters=filters)

Finally, you can import and export data.

# Import .csv data to Firestore.
ref = "tests/test_collections/licensees"
data_file = "./assets/data/licensees_partial.csv"
firebase.import_data(db, ref, data_file)

# Export data to .csv from Firestore.
output_csv_file = "./assets/data/licensees_test.csv"
output_xlsx_file = "./assets/data/licensees_test.xlsx"
firebase.export_data(db, ref, output_csv_file)
Function Description
add_to_array(ref, field, value, database=None) Add an element to a given field for a given reference.
create_document(ref, values, database=None) Create a given document with given values, this leverages the same functionality as update_document thanks to set with merge=True.
create_reference(database, path) Create a database reference for a given path.
delete_collection(ref, batch_size=420, database=None) Delete a given collection, a batch at a time.
delete_document(ref, database=None) Delete a given document.
delete_field(ref, field, database=None) Delete a given field from a document.
remove_from_array(ref, field, value, database=None) Remove an element from a given field for a given reference.
increment_value(ref, field, amount=1, database=None) Increment a given field for a given reference.
update_document(ref, values, database=None) Update a given document with given values.
update_documents(refs, data, database=None) Batch update documents, up to the MAX_BATCH_SIZE, 420 by default.
get_document(ref, database=None) Get a given document.
get_collection(ref, limit=None, order_by=None, desc=False, filters=None, database=None, start_at=None) Get documents from a collection. Filters are dictionaries of the form {'key': '', 'operation': '', 'value': ''}. Filters apply Firebase queries to the given key for the given value. Operators include: ==, >=, <=, >, <, !=, in, not_in, array_contains, array_contains_any.
import_data(database, ref, data_file) Import data into Firestore.
export_data(database, ref, data_file) Export data from Firestore.
create_id() Generate a universal ID.
create_id_from_datetime(timestamp) Create an ID from an existing datetime.
get_id_timestamp(uid) Get the datetime that an ID was created.

Authentication

You can use Firebase to add authentication to your app. If you choose to do so, then you can manage permissions for your users.

First, you can create a user.

name = "CannBot"
email = "contact@cannlytics.com"
user, password = firebase.create_account(name, email, notification=True)

You can add custom claims for a user to control granular permissions.

# Create and get custom claims.
claims = {"organizations": ["Cannlytics"]}
firebase.create_custom_claims(user.uid, email=email, claims=claims)
custom_claims = firebase.get_custom_claims(email)

You can get a user token to authenticate in your client-side code.

# Create custom token.
token = firebase.create_custom_token(user.uid, email=None, claims=custom_claims)

You can get a user or users.

# Get user.
user = firebase.get_user(email)

# Get all users.
all_users = firebase.get_users()

You can update a user's photo_url, display_name, email, phone_number, email_verified, and disabled fields. Pass a dictionary with the desired key/value pairs that you wish to change.

# Update user.
photo_url = f"https://cannlytics.com/robohash/{user.email}/?width=420&height=420"
user = firebase.update_user(user, {"photo_url": photo_url})
Function Description
create_user(name, email) Given user name and email, create an account. If the email is already being used, then nothing is returned.
create_custom_claims(uid, email=None, claims=None) Create custom claims for a user to grant granular permission. The new custom claims will propagate to the user's ID token the next time a new one is issued.
update_custom_claims(uid, email=None, claims=None) Update custom claims for a user. The new custom claims will propagate to the user's ID token the next time a new one is issued.
get_custom_claims(name) Get custom claims for a user.
create_custom_token(uid='', email=None, claims=None) Create a custom token for a given user, expires after one hour.
create_session_cookie(id_token, expires_in=None) Create a session cookie.
revoke_refresh_tokens(token) Revoke a user's refresh token.
verify_token(token) Verify a user's custom token.
verify_session_cookie(session_cookie, check_revoked=True, app=None) Verify a user's session cookie.
get_user(name) Get a user by user ID or by email.
get_users() Get all Firebase users.
update_user(existing_user, data) Update a user.
delete_user(uid) Delete a user from Firebase.
generate_password_reset_link(email) Get a password reset link for a user given their email.

Secret Manager

For sensitive credentials, it is recommended to use Secret Manager. You can use the following functions to easily create, update, and access secrets.

Function Description
create_secret(project_id, secret_id, secret) Create a new secret with the given name. A secret is a logical wrapper around a collection of secret versions. Secret versions hold the actual secret material.
add_secret_version(project_id, secret_id, payload) Add a new secret version to the given secret with the provided payload. A secret version contains the actual contents of a secret. A secret version can be enabled, disabled, or destroyed. To change the contents of a secret, you create a new version. Adding a secret version requires the Secret Manager Admin role (roles/secretmanager.admin) on the secret, project, folder, or organization. Roles can't be granted on a secret version.
access_secret_version(project_id, secret_id, version_id) Access the payload for a given secret version if one exists. The version can be a version number as a string (e.g. "5") or an alias (e.g. "latest").

Storage

You can utilize Firebase Storage for file management.

You can upload files to storage.

# Upload a file to a Firebase Storage bucket.
firebase.upload_file(bucket_name, destination_blob_name, source_file_name)

# Upload all files in a folder to a Firebase Storage bucket.
firebase.upload_files(bucket_name, bucket_folder, local_folder)

You can then list files in a given bucket's folder.

# List all files in the Firebase Storage bucket folder.
files = firebase.list_files(bucket_name, bucket_folder)

You can download files.

# Download a file from Firebase Storage.
firebase.download_file(bucket_name, destination_blob_name, download_file_name)

# Download all files in a given Firebase Storage folder.
firebase.download_files(bucket_name, bucket_folder, download_folder)

Finally, you can rename and delete files if needed.

# Rename a file in the Firebase Storage bucket.
firebase.rename_file(bucket_name, bucket_folder, file_name, newfile_name)

# Delete a file from the Firebase Storage bucket.
firebase.delete_file(bucket_name, bucket_folder, file_copy)
Function Description
create_short_url(api_key, long_url, project_name) Create a short URL to a specified file.
download_file(source_blob_name, destination_file_name, bucket_name=None) Downloads a file from Firebase Storage.
download_files(bucket_folder, local_folder, bucket_name=None) Download all files in a given Firebase Storage folder.
get_file_url(ref, bucket_name=None, expiration=None) Return the storage URL of a given file reference.
upload_file(destination_blob_name, source_file_name=None, data_url=None, content_type='image/jpg', bucket_name=None) Upload file to Firebase Storage.
upload_files(bucket_folder, local_folder, bucket_name=None) Upload multiple files to Firebase Storage.
list_files(bucket_folder, bucket_name=None) List all files in GCP bucket folder.
delete_file(blob_name, bucket_name=None) Delete file from GCP bucket.
rename_file(bucket_folder, file_name, newfile_name, bucket_name=None) Rename file in GCP bucket.

Misc

If you need to create logs, then there is a method, create_log that provides a standardized way to save logs in your database.

Function Description
create_log(ref, claims, action, log_type, key, changes=None) Create an activity log.