Skip to content

Commit 2cd269a

Browse files
mylibrarSuqi Sun
andauthored
Allow user specified django project (#207)
* Allow user specified django project * Add comments * Improve prompts and add README for config Co-authored-by: Suqi Sun <[email protected]>
1 parent d42c381 commit 2cd269a

File tree

5 files changed

+390
-107
lines changed

5 files changed

+390
-107
lines changed

.github/workflows/pypi.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ jobs:
3535
yarn install
3636
yarn run build
3737
mv ./build ./simple-backend/stave_backend/lib/build
38+
rm ./simple-backend/stave_backend/settings.py
39+
rm ./simple-backend/stave_backend/wsgi.py
3840
- name: Build a binary wheel and a source tarball
3941
run: |
4042
python -m build --sdist --wheel --outdir dist/ .

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ pip install stave
2727
```
2828
#### Quick Start
2929
```bash
30-
stave start -l -o
30+
stave -s start -l -o
3131
```
32-
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.
32+
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.
3333

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

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

4141
At any time, you can still load the example projects:
4242
```bash
43-
stave load
43+
stave load-samples
4444
```
4545

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

58-
You can view or update the configuration by running the subcommand `config`. For more information, refer to:
58+
You can view or update the configuration by running the subcommand `config`.
59+
You may follow the prompts to interactively set up the configuration:
60+
```bash
61+
stave config -i
62+
```
63+
For more information, refer to:
5964
```bash
6065
stave config -h
6166
```

simple-backend/stave_backend/lib/stave_cli.py

Lines changed: 99 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,55 @@
1212
1313
"""
1414

15-
import os
1615
import sys
17-
import json
1816
import argparse
1917
import getpass
2018
import logging
19+
from typing import List, Union
2120
from stave_backend.lib.stave_viewer import StaveViewer
2221
from stave_backend.lib.stave_session import StaveSession
22+
from stave_backend.lib.stave_config import StaveConfig
2323

2424
START = "start"
2525
LOAD = "load-samples"
2626
IMPORT = "import"
2727
EXPORT = "export"
2828
CONFIG = "config"
2929

30+
CONFIG_ARGS = {
31+
"django_settings_module": {
32+
"name_or_flags": ["-s", "--django-settings-module"],
33+
"kwargs": {"help": (
34+
"Module path to settings.py of django project. "
35+
"If you have not set up any django project, you should leave this "
36+
"field empty and stave will use its default configuration. "
37+
"To set this field you should already have a django project and "
38+
"the 'settings.py' file under your project must be accessible "
39+
"from PYTHONPATH so that django can import it as a module. "
40+
"Example: 'myproject.settings'"
41+
)},
42+
},
43+
"db_file": {
44+
"name_or_flags": ["-d", "--db-file"],
45+
"kwargs": {"help": "Path to database file of Stave"}
46+
},
47+
"log_file": {
48+
"name_or_flags": ["-l", "--log-file"],
49+
"kwargs": {"help": "Path to log file for logging"}
50+
},
51+
"allowed_hosts": {
52+
"name_or_flags": ["-a", "--allowed-hosts"],
53+
"kwargs": {
54+
"default": ["localhost"],
55+
"nargs": '+',
56+
"help": (
57+
"A list of strings representing the host/domain names that "
58+
"stave can serve."
59+
)
60+
}
61+
}
62+
}
63+
3064
def get_args():
3165

3266
parser = argparse.ArgumentParser(
@@ -36,6 +70,9 @@ def get_args():
3670
)
3771
parser.add_argument("-v", "--verbose", action="store_true",
3872
help="Increase output verbosity")
73+
parser.add_argument("-s", "--simple", action="store_true",
74+
help=("Simple mode without initial setup step."
75+
" Stave will use the default configuration."))
3976
subparsers = parser.add_subparsers(dest="command",
4077
help="Valid commands")
4178
subparsers.required=True
@@ -68,57 +105,82 @@ def get_args():
68105

69106
parser_config = subparsers.add_parser(CONFIG,
70107
help="Show or change Stave configuration")
71-
parser_config.add_argument("-s", "--show-config", action="store_true",
72-
help="Display config info")
73-
parser_config.add_argument("-d", "--db-file",
74-
help="Path to database file of Stave")
75-
parser_config.add_argument("-l", "--log-file",
76-
help="Path to log file for logging")
108+
parser_config.add_argument("-i", "--interact-config", action="store_true",
109+
help="Interactively set up the configuration")
110+
for opt in CONFIG_ARGS.values():
111+
parser_config.add_argument(*opt["name_or_flags"], **opt["kwargs"])
77112

78113
return parser.parse_args()
79114

80-
def set_logger(verbose: bool, log_file: str):
115+
def set_logger_verbose(verbose: bool):
81116
"""
82-
Set up logging. The implementation is contingent on the LOGGING field
83-
in "stave_backend/settings.py", so this can only be called after
117+
Set up logging verbose at runtime. The implementation is contingent on
118+
static LOGGING setting in "stave_config.py". This can only be called after
84119
the server starts.
85120
"""
86121
root_logger = logging.getLogger()
87-
root_logger.setLevel(logging.NOTSET)
88-
89-
stream_handler = root_logger.handlers[0]
90-
stream_handler.setLevel(logging.INFO if verbose else logging.ERROR)
91122

92-
file_handler = logging.FileHandler(log_file)
93-
file_handler.setLevel(logging.NOTSET)
94-
file_handler.setFormatter(stream_handler.formatter)
95-
96-
root_logger.addHandler(file_handler)
97-
logging.getLogger("django").addHandler(file_handler)
123+
root_handlers = root_logger.handlers
124+
if root_handlers and isinstance(root_handlers[0], logging.StreamHandler):
125+
root_handlers[0].setLevel(logging.INFO if verbose else logging.ERROR)
126+
else:
127+
root_logger.setLevel(logging.INFO if verbose else logging.ERROR)
98128

99129
return root_logger
100130

131+
def interactive_config(config: StaveConfig):
132+
"""
133+
Interactively set up the configuration
134+
"""
135+
print(config.README)
136+
def set_val(name: str):
137+
"""
138+
Set attribute value based on user input
139+
"""
140+
val: Union[str, List] = input(
141+
f"\nname: {name}\n"
142+
f"default/current: {getattr(config, name)}\n"
143+
f"description: {CONFIG_ARGS[name]['kwargs']['help']}\n"
144+
f"Enter the config below or press ENTER to accept the "
145+
"default/current setting: \n> "
146+
)
147+
if val:
148+
if val in ("None", "null"):
149+
val = None
150+
if val in ("''", '""'):
151+
val = ""
152+
setattr(config, name, val)
153+
return val
154+
155+
dsm_name: str = "django_settings_module"
156+
if not set_val(dsm_name):
157+
for name in CONFIG_ARGS.keys():
158+
if name == dsm_name:
159+
continue
160+
set_val(name)
161+
config.show_config()
162+
101163
def main():
102164

103165
args = get_args()
166+
config = StaveConfig()
104167
in_viewer_mode = args.command == START and args.project_path is not None
105168
thread_daemon = not (args.command == START)
106169

107170
if args.command == CONFIG:
108-
change_config = (args.db_file or args.log_file) is not None
109-
if change_config:
110-
StaveViewer.set_config(
111-
db_file=args.db_file and os.path.abspath(args.db_file),
112-
log_file=args.log_file and os.path.abspath(args.log_file)
113-
)
114-
if args.show_config or (not change_config):
115-
print(json.dumps(
116-
StaveViewer.load_config(),
117-
sort_keys=True,
118-
indent=4
119-
))
171+
if args.interact_config:
172+
interactive_config(config=config)
173+
else:
174+
for field in CONFIG_ARGS.keys():
175+
if getattr(args, field) is not None:
176+
setattr(config, field, getattr(args, field))
177+
config.show_config()
120178
sys.exit()
121179

180+
# Interactively set up the configuration
181+
if not config.is_initialized() and not args.simple:
182+
interactive_config(config=config)
183+
122184
sv = StaveViewer(
123185
project_path=args.project_path if in_viewer_mode else '',
124186
port=args.port_number if args.command == START else 8888,
@@ -127,7 +189,7 @@ def main():
127189
)
128190
sv.run()
129191

130-
logger = set_logger(verbose=args.verbose, log_file=sv.log_file)
192+
logger = set_logger_verbose(verbose=args.verbose)
131193

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

0 commit comments

Comments
 (0)