This repository was archived by the owner on Jun 7, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathconfig.py
More file actions
162 lines (132 loc) · 6.17 KB
/
config.py
File metadata and controls
162 lines (132 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# ********************************************
# |docname| - Configuring Runestone BookServer
# ********************************************
# Many thing about Runestone BookServer are configurable. This is the place to change
# the configuration for most things. **Private** things should be configured in the
# environment so they are not accidentally committed to Github.
# Defaults provided here may be overridden by environment variables `Per <https://fastapi.tiangolo.com/advanced/settings/>`_.
#
#
# Imports
# =======
# These are listed in the order prescribed by `PEP 8`_.
#
# Standard library
# ----------------
from enum import Enum
from pathlib import Path
from functools import lru_cache
# Third-party imports
# -------------------
import pkg_resources
from pydantic import BaseSettings
# Local application imports
# -------------------------
from .applogger import rslogger
# Settings
# ========
# Define the possible bookserver configurations. The values assigned must be strings, since Pydantic will match these with environment variables.
class BookServerConfig(Enum):
development = "development"
test = "test"
production = "production"
# A enum for the type of database in use.
class DatabaseType(Enum):
SQLite = 0
PostgreSQL = 1
class Settings(BaseSettings):
# Pydantic provides a wonderful utility to handle settings. The beauty of it
# is that you can specify variables with or without default values, and Pydantic
# will check your environment variables in a case-insensitive way. So that
# if you have ``PROD_DBURL``` set in the environment it will be set as the value
# for ``prod_dburl``` in settings.
# This is a really nice way to keep from
# committing any data you want to keep private.
google_ga: str = ""
# Provide a path to the book server files. The leading underscore prevents environment variables from affecting this value. See the `docs <https://pydantic-docs.helpmanual.io/usage/models/#automatically-excluded-attributes>`_, which don't say this explicitly, but testing confirms it.
_book_server_path: Path = Path(
pkg_resources.resource_filename("bookserver", "")
).absolute()
# _`book_path`: specify the directory to serve books from.
book_path: Path = Path.home() / "Runestone/books"
# The path to store error logs.
error_path: Path = Path.home() / "Runestone/errors"
# The path to the Runeestone application inside web2py.
runestone_path: Path = (
Path(_book_server_path).parents[1] / "web2py/applications/runestone"
)
# Define the mode of operation for the webserver, taken from ``BookServerConfig```. This looks a bit odd, since the string value will be parsed by Pydantic into a Config.
#
# .. admonition:: warning
#
# When using an Enum for a configuration setting you cannot compare against
# a string. The value will actually be ``BookServerConfig.development``` or whatever.
# Our style will be to compare against the Enum not the ``.name`` attribute.
#
book_server_config: BookServerConfig = "development" # type: ignore
# Database setup: this must be an standard (synchronous) connection; for example:
#
# - ``sqlite:///./runestone.db``
# - ``postgresql://postgres:bully@localhost/runestone``
dburl: str = f"sqlite:///{_book_server_path}/runestone.db"
dev_dburl: str = f"sqlite:///{_book_server_path}/runestone_dev.db"
test_dburl: str = f"sqlite:///{_book_server_path}/runestone_test.db"
# Given a sync database URI, return its async equivalent.
@staticmethod
def _sync_to_async_uri(sync_uri: str) -> str:
return sync_uri.replace("sqlite://", "sqlite+aiosqlite://", 1).replace(
"postgresql://", "postgresql+asyncpg://", 1
)
# Determine the synchronous database URL based on the ``book_server_config`` and the dburls above.
@property
def _sync_database_url(self) -> str:
return {
"development": self.dev_dburl,
"test": self.test_dburl,
"production": self.dburl,
}[self.book_server_config.value]
# Return the async equivalent of the URI from ``sync_database_url``.
@property
def database_url(self) -> str:
return self._sync_to_async_uri(self._sync_database_url)
# Determine the database type from the URL.
@property
def database_type(self) -> DatabaseType:
dburl = self.database_url
if dburl.startswith("sqlite"):
return DatabaseType.SQLite
elif dburl.startswith("postgresql"):
return DatabaseType.PostgreSQL
else:
raise RuntimeError(f"Unknown database type; URL is {dburl}.")
# Setting db_echo to True makes for a LOT of sqlalchemy output - it gives you the SQL for every query!
db_echo = False
# The docker-compose.yml file will set the REDIS_URI environment variable
redis_uri = "redis://localhost:6379/0"
# Select normal mode or a high-stakes assessment mode (for administering a examination). In this mode, answers to supported question types are not shown.
is_exam: bool = False
# Configure ads. TODO: Link to the place in the Runestone Components where this is used.
adsenseid: str = ""
num_banners: int = 0
serve_ad: bool = False
# This is the secret key used for generating the JWT token
secret: str = "supersecret"
# This is the private key web2py uses for hashing passwords.
@property
def web2py_private_key(self) -> str:
# Put the cache here; above the def, it produces ``TypeError: unhashable type: 'Settings'``.
@lru_cache
def read_key():
key_file = self.runestone_path / "private/auth.key"
if key_file.exists():
with open(key_file, encoding="utf-8") as f:
return f.read().strip()
else:
rslogger.error("No Key file is found will default to settings.secret")
return self.secret
return read_key()
# if you want to reinitialize your database set this to ``Yes``.
# All data in the database will be lost! This will only work for
# development and test ``book_server_config`` settings.
drop_tables: str = "No"
settings = Settings()