import os import sys import otree from .base import BaseCommand import shutil from pathlib import Path from tempfile import TemporaryDirectory from io import BytesIO import random print_function = print def prompt_about_sample_games(): '''for easy patching''' return input("Include sample games? (y or n): ") class Command(BaseCommand): help = "Creates a new oTree project." def add_arguments(self, parser): parser.add_argument('name') # we need a CLI arg rather than a small function we can patch, # because our test launches this in a subprocess. parser.add_argument( '--noinput', action='store_false', dest='interactive', default=True, ) def handle(self, name, interactive): dest = Path(name) if Path('settings.py').exists(): msg = ( 'You are trying to create a project but it seems you are ' 'already in a project folder (found settings.py).' ) sys.exit(msg) if dest.exists(): msg = ( f'There is already a project called "{name}" ' 'in this folder. Either delete that folder first, or use a different name.' ) sys.exit(msg) if interactive and prompt_about_sample_games().lower() == "y": download_from_github(dest) else: copy_project_template(dest) settings_path = dest.joinpath('settings.py') settings_path.write_text( settings_path.read_text().replace( "{{ secret_key }}", str(random.randint(10 ** 12, 10 ** 13)) ) ) msg = ( 'Created project folder.\n' f'Enter "cd {name}" to move inside the project folder, ' 'then start the server with "otree devserver".' # ) print_function(msg) def download_from_github(dest: Path): # expensive import from urllib.request import urlopen import zipfile branch_name = 'lite' resp = urlopen(f"https://github.com/oTree-org/oTree/archive/{branch_name}.zip") f = BytesIO() f.write(resp.read()) f.seek(0) with TemporaryDirectory() as tmpdir: with zipfile.ZipFile(f, 'r') as zip_ref: # omit tests.py because it is jarring/distracting with __init__.py format. zip_ref.extractall( tmpdir, members=[f for f in zip_ref.namelist() if not f.endswith('tests.py')], ) shutil.move(Path(tmpdir, f'oTree-{branch_name}'), dest) def copy_project_template(dest: Path): src = Path(otree.__file__).parent / 'project_template' shutil.copytree(src, dest)