Skip to content

Commit

Permalink
Allow user specified django project (#207)
Browse files Browse the repository at this point in the history
* Allow user specified django project

* Add comments

* Improve prompts and add README for config

Co-authored-by: Suqi Sun <[email protected]>
  • Loading branch information
mylibrar and Suqi Sun authored Aug 22, 2021
1 parent d42c381 commit 2cd269a
Show file tree
Hide file tree
Showing 5 changed files with 390 additions and 107 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
yarn install
yarn run build
mv ./build ./simple-backend/stave_backend/lib/build
rm ./simple-backend/stave_backend/settings.py
rm ./simple-backend/stave_backend/wsgi.py
- name: Build a binary wheel and a source tarball
run: |
python -m build --sdist --wheel --outdir dist/ .
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ pip install stave
```
#### Quick Start
```bash
stave start -l -o
stave -s start -l -o
```
This will start the Stave server with example project loaded. `-l` will load example projects and `-o` will open a browser window. If you want to start Stave as a headless server, simply remove the `-o` flag. You can log in with default user name `admin` and default password `admin`. You can start viewing the projects and some annotations/applications that we have prepared.
This will start the Stave server with example project loaded. `-s` allows Stave to run with all the default configuration. `-l` will load example projects and `-o` will open a browser window. If you want to start Stave as a headless server, simply remove the `-o` flag. You can log in with default user name `admin` and default password `admin`. You can start viewing the projects and some annotations/applications that we have prepared.

Or if you just want to start Stave from scratch, you can:

Expand All @@ -40,7 +40,7 @@ You can still log in with default user name `admin` and default password `admin`

At any time, you can still load the example projects:
```bash
stave load
stave load-samples
```

#### Stave Configuration
Expand All @@ -55,7 +55,12 @@ After you start the Stave server, a `.stave/` folder is automatically created un
- `db.sqlite3` is the default database for Stave server.
- `log` is the default logging file.

You can view or update the configuration by running the subcommand `config`. For more information, refer to:
You can view or update the configuration by running the subcommand `config`.
You may follow the prompts to interactively set up the configuration:
```bash
stave config -i
```
For more information, refer to:
```bash
stave config -h
```
Expand Down
134 changes: 99 additions & 35 deletions simple-backend/stave_backend/lib/stave_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,55 @@
"""

import os
import sys
import json
import argparse
import getpass
import logging
from typing import List, Union
from stave_backend.lib.stave_viewer import StaveViewer
from stave_backend.lib.stave_session import StaveSession
from stave_backend.lib.stave_config import StaveConfig

START = "start"
LOAD = "load-samples"
IMPORT = "import"
EXPORT = "export"
CONFIG = "config"

CONFIG_ARGS = {
"django_settings_module": {
"name_or_flags": ["-s", "--django-settings-module"],
"kwargs": {"help": (
"Module path to settings.py of django project. "
"If you have not set up any django project, you should leave this "
"field empty and stave will use its default configuration. "
"To set this field you should already have a django project and "
"the 'settings.py' file under your project must be accessible "
"from PYTHONPATH so that django can import it as a module. "
"Example: 'myproject.settings'"
)},
},
"db_file": {
"name_or_flags": ["-d", "--db-file"],
"kwargs": {"help": "Path to database file of Stave"}
},
"log_file": {
"name_or_flags": ["-l", "--log-file"],
"kwargs": {"help": "Path to log file for logging"}
},
"allowed_hosts": {
"name_or_flags": ["-a", "--allowed-hosts"],
"kwargs": {
"default": ["localhost"],
"nargs": '+',
"help": (
"A list of strings representing the host/domain names that "
"stave can serve."
)
}
}
}

def get_args():

parser = argparse.ArgumentParser(
Expand All @@ -36,6 +70,9 @@ def get_args():
)
parser.add_argument("-v", "--verbose", action="store_true",
help="Increase output verbosity")
parser.add_argument("-s", "--simple", action="store_true",
help=("Simple mode without initial setup step."
" Stave will use the default configuration."))
subparsers = parser.add_subparsers(dest="command",
help="Valid commands")
subparsers.required=True
Expand Down Expand Up @@ -68,57 +105,82 @@ def get_args():

parser_config = subparsers.add_parser(CONFIG,
help="Show or change Stave configuration")
parser_config.add_argument("-s", "--show-config", action="store_true",
help="Display config info")
parser_config.add_argument("-d", "--db-file",
help="Path to database file of Stave")
parser_config.add_argument("-l", "--log-file",
help="Path to log file for logging")
parser_config.add_argument("-i", "--interact-config", action="store_true",
help="Interactively set up the configuration")
for opt in CONFIG_ARGS.values():
parser_config.add_argument(*opt["name_or_flags"], **opt["kwargs"])

return parser.parse_args()

def set_logger(verbose: bool, log_file: str):
def set_logger_verbose(verbose: bool):
"""
Set up logging. The implementation is contingent on the LOGGING field
in "stave_backend/settings.py", so this can only be called after
Set up logging verbose at runtime. The implementation is contingent on
static LOGGING setting in "stave_config.py". This can only be called after
the server starts.
"""
root_logger = logging.getLogger()
root_logger.setLevel(logging.NOTSET)

stream_handler = root_logger.handlers[0]
stream_handler.setLevel(logging.INFO if verbose else logging.ERROR)

file_handler = logging.FileHandler(log_file)
file_handler.setLevel(logging.NOTSET)
file_handler.setFormatter(stream_handler.formatter)

root_logger.addHandler(file_handler)
logging.getLogger("django").addHandler(file_handler)
root_handlers = root_logger.handlers
if root_handlers and isinstance(root_handlers[0], logging.StreamHandler):
root_handlers[0].setLevel(logging.INFO if verbose else logging.ERROR)
else:
root_logger.setLevel(logging.INFO if verbose else logging.ERROR)

return root_logger

def interactive_config(config: StaveConfig):
"""
Interactively set up the configuration
"""
print(config.README)
def set_val(name: str):
"""
Set attribute value based on user input
"""
val: Union[str, List] = input(
f"\nname: {name}\n"
f"default/current: {getattr(config, name)}\n"
f"description: {CONFIG_ARGS[name]['kwargs']['help']}\n"
f"Enter the config below or press ENTER to accept the "
"default/current setting: \n> "
)
if val:
if val in ("None", "null"):
val = None
if val in ("''", '""'):
val = ""
setattr(config, name, val)
return val

dsm_name: str = "django_settings_module"
if not set_val(dsm_name):
for name in CONFIG_ARGS.keys():
if name == dsm_name:
continue
set_val(name)
config.show_config()

def main():

args = get_args()
config = StaveConfig()
in_viewer_mode = args.command == START and args.project_path is not None
thread_daemon = not (args.command == START)

if args.command == CONFIG:
change_config = (args.db_file or args.log_file) is not None
if change_config:
StaveViewer.set_config(
db_file=args.db_file and os.path.abspath(args.db_file),
log_file=args.log_file and os.path.abspath(args.log_file)
)
if args.show_config or (not change_config):
print(json.dumps(
StaveViewer.load_config(),
sort_keys=True,
indent=4
))
if args.interact_config:
interactive_config(config=config)
else:
for field in CONFIG_ARGS.keys():
if getattr(args, field) is not None:
setattr(config, field, getattr(args, field))
config.show_config()
sys.exit()

# Interactively set up the configuration
if not config.is_initialized() and not args.simple:
interactive_config(config=config)

sv = StaveViewer(
project_path=args.project_path if in_viewer_mode else '',
port=args.port_number if args.command == START else 8888,
Expand All @@ -127,7 +189,7 @@ def main():
)
sv.run()

logger = set_logger(verbose=args.verbose, log_file=sv.log_file)
logger = set_logger_verbose(verbose=args.verbose)

try:
if args.command == START:
Expand Down Expand Up @@ -158,7 +220,9 @@ def main():
except Exception:
sys.exit()
finally:
print(f"For more details, check out log file at {sv.log_file}.")
# log_file only available when django_settings_module is not set
if not config.django_settings_module:
print(f"For more details, check out log file at {config.log_file}.")
# If the thread is not daemonic, user need to force stop the server
if not thread_daemon:
print(f"Starting Stave server at {sv.default_page}.\n"
Expand Down
Loading

0 comments on commit 2cd269a

Please sign in to comment.