forked from enviPath/enviPy
## Summary This PR improves the local development setup experience by adding Docker Compose and Makefile for streamlined setup. ## Changes - **Added `docker-compose.yml`**: for one-command PostgreSQL database setup - **Added `Makefile`**: Convenient shortcuts for common dev tasks (\`make setup\`, \`make dev\`, etc.) - **Updated `README.md`**: Quick development setup instructions using Make - - **Added**: RDkit installation pain point documentation - **Fixed**: Made Java feature properly dependent ## Why these changes? The application uses PostgreSQL-specific features (\`ArrayField\`) and requires an anonymous user created by the bootstrap command. This PR makes the setup process trivial for new developers: ```bash cp .env.local.example .env make setup # Starts DB, runs migrations, bootstraps data make dev # Starts development server ``` Java fix: Moved global Java import to inline to avoid everyone having to configure the Java path. Numerous changes to view and settings. - Applied ruff-formatting ## Testing Verified complete setup from scratch works with: - PostgreSQL running in Docker - All migrations applied - Bootstrap data loaded successfully - Anonymous user created - The development server starts correctly. Co-authored-by: Tobias O <tobias.olenyi@tum.de> Co-authored-by: Tobias O <tobias.olenyi@envipath.com> Co-authored-by: Liam <62733830+limmooo@users.noreply.github.com> Reviewed-on: enviPath/enviPy#143 Reviewed-by: jebus <lorsbach@envipath.com> Reviewed-by: liambrydon <lbry121@aucklanduni.ac.nz> Co-authored-by: t03i <mail+envipath@t03i.net> Co-committed-by: t03i <mail+envipath@t03i.net>
197 lines
7.1 KiB
Python
197 lines
7.1 KiB
Python
import json
|
|
|
|
from django.conf import settings as s
|
|
from django.core.management.base import BaseCommand
|
|
from django.db import transaction
|
|
|
|
from epdb.logic import UserManager, GroupManager, PackageManager, SettingManager
|
|
from epdb.models import UserSettingPermission, MLRelativeReasoning, EnviFormer, Permission, User, ExternalDatabase
|
|
|
|
|
|
class Command(BaseCommand):
|
|
|
|
def create_users(self):
|
|
|
|
# Anonymous User
|
|
if not User.objects.filter(email='anon@envipath.com').exists():
|
|
anon = UserManager.create_user("anonymous", "anon@envipath.com", "SuperSafe",
|
|
is_active=True, add_to_group=False, set_setting=False)
|
|
else:
|
|
anon = User.objects.get(email='anon@envipath.com')
|
|
|
|
# Admin User
|
|
if not User.objects.filter(email='admin@envipath.com').exists():
|
|
admin = UserManager.create_user("admin", "admin@envipath.com", "SuperSafe",
|
|
is_active=True, add_to_group=False, set_setting=False)
|
|
admin.is_staff = True
|
|
admin.is_superuser = True
|
|
admin.save()
|
|
else:
|
|
admin = User.objects.get(email='admin@envipath.com')
|
|
|
|
# System Group
|
|
g = GroupManager.create_group(admin, 'enviPath Users', 'All enviPath Users')
|
|
g.public = True
|
|
g.save()
|
|
|
|
g.user_member.add(anon)
|
|
g.save()
|
|
|
|
anon.default_group = g
|
|
anon.save()
|
|
|
|
admin.default_group = g
|
|
admin.save()
|
|
|
|
if not User.objects.filter(email='user0@envipath.com').exists():
|
|
user0 = UserManager.create_user("user0", "user0@envipath.com", "SuperSafe",
|
|
is_active=True, add_to_group=False, set_setting=False)
|
|
user0.is_staff = True
|
|
user0.is_superuser = True
|
|
user0.save()
|
|
else:
|
|
user0 = User.objects.get(email='user0@envipath.com')
|
|
|
|
g.user_member.add(user0)
|
|
g.save()
|
|
|
|
user0.default_group = g
|
|
user0.save()
|
|
|
|
return anon, admin, g, user0
|
|
|
|
def import_package(self, data, owner):
|
|
return PackageManager.import_legacy_package(data, owner, keep_ids=True, add_import_timestamp=False, trust_reviewed=True)
|
|
|
|
def create_default_setting(self, owner, packages):
|
|
s = SettingManager.create_setting(
|
|
owner,
|
|
name='Global Default Setting',
|
|
description='Global Default Setting containing BBD Rules and Max 30 Nodes and Max Depth of 8',
|
|
max_nodes=30,
|
|
max_depth=5,
|
|
rule_packages=packages,
|
|
model=None,
|
|
model_threshold=None
|
|
)
|
|
|
|
return s
|
|
|
|
def populate_common_external_databases(self):
|
|
"""
|
|
Helper function to populate common external databases.
|
|
This can be called from a Django management command.
|
|
"""
|
|
databases = [
|
|
{
|
|
'name': 'PubChem Compound',
|
|
'full_name': 'PubChem Compound Database',
|
|
'description': 'Chemical database of small organic molecules',
|
|
'base_url': 'https://pubchem.ncbi.nlm.nih.gov',
|
|
'url_pattern': 'https://pubchem.ncbi.nlm.nih.gov/compound/{id}'
|
|
},
|
|
{
|
|
'name': 'PubChem Substance',
|
|
'full_name': 'PubChem Substance Database',
|
|
'description': 'Database of chemical substances',
|
|
'base_url': 'https://pubchem.ncbi.nlm.nih.gov',
|
|
'url_pattern': 'https://pubchem.ncbi.nlm.nih.gov/substance/{id}'
|
|
},
|
|
{
|
|
'name': 'ChEBI',
|
|
'full_name': 'Chemical Entities of Biological Interest',
|
|
'description': 'Dictionary of molecular entities',
|
|
'base_url': 'https://www.ebi.ac.uk/chebi',
|
|
'url_pattern': 'https://www.ebi.ac.uk/chebi/searchId.do?chebiId=CHEBI:{id}'
|
|
},
|
|
{
|
|
'name': 'RHEA',
|
|
'full_name': 'RHEA Reaction Database',
|
|
'description': 'Comprehensive resource of biochemical reactions',
|
|
'base_url': 'https://www.rhea-db.org',
|
|
'url_pattern': 'https://www.rhea-db.org/rhea/{id}'
|
|
},
|
|
{
|
|
'name': 'KEGG Reaction',
|
|
'full_name': 'KEGG Reaction Database',
|
|
'description': 'Database of biochemical reactions',
|
|
'base_url': 'https://www.genome.jp',
|
|
'url_pattern': 'https://www.genome.jp/entry/{id}'
|
|
},
|
|
{
|
|
'name': 'UniProt',
|
|
'full_name': 'MetaCyc Metabolic Pathway Database',
|
|
'description': 'UniProt is a freely accessible database of protein sequence and functional information',
|
|
'base_url': 'https://www.uniprot.org',
|
|
'url_pattern': 'https://www.uniprot.org/uniprotkb?query="{id}"'
|
|
}
|
|
]
|
|
|
|
for db_info in databases:
|
|
ExternalDatabase.objects.get_or_create(
|
|
name=db_info['name'],
|
|
defaults=db_info
|
|
)
|
|
|
|
@transaction.atomic
|
|
def handle(self, *args, **options):
|
|
# Create users
|
|
anon, admin, g, user0 = self.create_users()
|
|
|
|
self.populate_common_external_databases()
|
|
|
|
# Import Packages
|
|
packages = [
|
|
'EAWAG-BBD.json',
|
|
'EAWAG-SOIL.json',
|
|
'EAWAG-SLUDGE.json',
|
|
'EAWAG-SEDIMENT.json',
|
|
]
|
|
|
|
mapping = {}
|
|
for p in packages:
|
|
print(f"Importing {p}...")
|
|
package_data = json.loads(open(s.BASE_DIR / 'fixtures' / 'packages' / '2025-07-18' / p, encoding='utf-8').read())
|
|
imported_package = self.import_package(package_data, admin)
|
|
mapping[p.replace('.json', '')] = imported_package
|
|
|
|
setting = self.create_default_setting(admin, [mapping['EAWAG-BBD']])
|
|
setting.public = True
|
|
setting.save()
|
|
setting.make_global_default()
|
|
|
|
for u in [anon, user0]:
|
|
u.default_setting = setting
|
|
u.save()
|
|
|
|
usp = UserSettingPermission()
|
|
usp.user = u
|
|
usp.setting = setting
|
|
usp.permission = Permission.READ[0]
|
|
usp.save()
|
|
|
|
# Create Model Package
|
|
pack = PackageManager.create_package(admin, "Public Prediction Models",
|
|
"Package to make Prediction Models publicly available")
|
|
pack.reviewed = True
|
|
pack.save()
|
|
|
|
# Create RR
|
|
ml_model = MLRelativeReasoning.create(
|
|
package=pack,
|
|
rule_packages=[mapping['EAWAG-BBD']],
|
|
data_packages=[mapping['EAWAG-BBD']],
|
|
eval_packages=[],
|
|
threshold=0.5,
|
|
name='ECC - BBD - T0.5',
|
|
description='ML Relative Reasoning',
|
|
)
|
|
|
|
ml_model.build_dataset()
|
|
ml_model.build_model()
|
|
# ml_model.evaluate_model()
|
|
|
|
# If available, create EnviFormerModel
|
|
if s.ENVIFORMER_PRESENT:
|
|
enviFormer_model = EnviFormer.create(pack, 'EnviFormer - T0.5', 'EnviFormer Model with Threshold 0.5', 0.5)
|