Skip to content

Commit

Permalink
Merge pull request #536 from LuteOrg/issue_534_change_initial_demo_da…
Browse files Browse the repository at this point in the history
…ta_load

Issue 534 change initial demo data load
  • Loading branch information
jzohrab authored Dec 10, 2024
2 parents 0f69c23 + c716f03 commit 03c5da9
Show file tree
Hide file tree
Showing 25 changed files with 464 additions and 620 deletions.
3 changes: 1 addition & 2 deletions .pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,4 @@ markers =
# Rather than sorting out how to add a flask cli command
# that has access to the configured app and context,
# I'm just using some markers to reset/wipe the dev db.
dbdemoload: cli hack to load the dev db with demo data
dbwipe: cli hack to wipe the dev db
dbreset: cli hack to wipe the dev db and set the LoadDemoData flag
4 changes: 4 additions & 0 deletions devstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
from lute import __version__
from lute.app_factory import create_app
from lute.config.app_config import AppConfig
from lute.db import db
from lute.db.demo import load_demo_data

log = logging.getLogger("werkzeug")
log.setLevel(logging.ERROR)
Expand All @@ -46,6 +48,8 @@ def dev_print(s):
config_file = AppConfig.default_config_filename()
dev_print("")
app = create_app(config_file, output_func=dev_print)
with app.app_context():
load_demo_data(db.session)

ac = AppConfig(config_file)
dev_print(f"\nversion {__version__}")
Expand Down
18 changes: 11 additions & 7 deletions lute/app_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from lute.db.management import add_default_user_settings
from lute.db.data_cleanup import clean_data
from lute.backup.service import Service as BackupService
import lute.db.demo
from lute.db.demo import Service as DemoService
import lute.utils.formutils

from lute.parse.registry import init_parser_plugins, supported_parsers
Expand Down Expand Up @@ -128,7 +128,8 @@ def inject_menu_bar_vars():

@app.route("/")
def index():
is_production = not lute.db.demo.contains_demo_data(db.session)
demosvc = DemoService(db.session)
is_production = not demosvc.contains_demo_data()
us_repo = UserSettingRepository(db.session)
bkp_settings = us_repo.get_backup_settings()

Expand All @@ -153,13 +154,14 @@ def index():
and warning_msg != ""
)

demosvc = DemoService(db.session)
response = make_response(
render_template(
"index.html",
hide_homelink=True,
dbname=app_config.dbname,
datapath=app_config.datapath,
tutorial_book_id=lute.db.demo.tutorial_book_id(db.session),
tutorial_book_id=demosvc.tutorial_book_id(),
have_books=have_books,
have_languages=have_languages,
language_choices=language_choices,
Expand All @@ -181,8 +183,9 @@ def refresh_all_stats():

@app.route("/wipe_database")
def wipe_db():
if lute.db.demo.contains_demo_data(db.session):
lute.db.demo.delete_demo_data(db.session)
demosvc = DemoService(db.session)
if demosvc.contains_demo_data():
demosvc.delete_demo_data()
msg = """
The database has been wiped clean. Have fun! <br /><br />
<i>(Lute has automatically enabled backups --
Expand All @@ -193,8 +196,9 @@ def wipe_db():

@app.route("/remove_demo_flag")
def remove_demo():
if lute.db.demo.contains_demo_data(db.session):
lute.db.demo.remove_flag(db.session)
demosvc = DemoService(db.session)
if demosvc.contains_demo_data():
demosvc.remove_flag()
msg = """
Demo mode deactivated. Have fun! <br /><br />
<i>(Lute has automatically enabled backups --
Expand Down
3 changes: 3 additions & 0 deletions lute/book/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ def _build_db_book(self, book):
lang = lang_repo.find(book.language_id)
elif book.language_name:
lang = lang_repo.find_by_name(book.language_name)
if lang is None:
msg = f"No language matching id={book.language_id} or name={book.language_name}"
raise RuntimeError(msg)

b = None
if book.id is None:
Expand Down
280 changes: 159 additions & 121 deletions lute/db/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,129 +9,167 @@
"""

from sqlalchemy import text
from lute.language.service import Service
from lute.language.service import Service as LanguageService
from lute.book.model import Repository
from lute.book.stats import Service as StatsService
from lute.models.repositories import SystemSettingRepository
from lute.models.repositories import SystemSettingRepository, LanguageRepository
import lute.db.management


def _demo_languages():
"""
Demo languages to be loaded for new users.
Also loaded during tests.
"""
return [
"Arabic",
"Classical Chinese",
"Czech",
"English",
"French",
"German",
"Greek",
"Hindi",
"Japanese",
"Russian",
"Sanskrit",
"Spanish",
"Turkish",
]


def contains_demo_data(session):
"""
True if IsDemoData setting is present.
"""
repo = SystemSettingRepository(session)
ss = repo.get_value("IsDemoData")
if ss is None:
return False
return True


def remove_flag(session):
"""
Remove IsDemoData setting.
"""
if not contains_demo_data(session):
raise RuntimeError("Can't delete non-demo data.")

repo = SystemSettingRepository(session)
repo.delete_key("IsDemoData")
session.commit()


def tutorial_book_id(session):
"""
Return the book id of the tutorial.
"""
if not contains_demo_data(session):
return None
sql = """select BkID from books
inner join languages on LgID = BkLgID
where LgName = 'English' and BkTitle = 'Tutorial'
"""
r = session.execute(text(sql)).first()
if r is None:
return None
return int(r[0])


def delete_demo_data(session):
"""
If this is a demo, wipe everything.
"""
if not contains_demo_data(session):
raise RuntimeError("Can't delete non-demo data.")
remove_flag(session)
lute.db.management.delete_all_data(session)


# Loading demo data.


def load_demo_languages(session):
"""
Load selected predefined languages. Assume everything is supported.
This method will also be called during acceptance tests, so it's public.
"""
demo_langs = _demo_languages()
service = Service(session)
langs = [service.get_language_def(langname)["language"] for langname in demo_langs]
supported = [lang for lang in langs if lang.is_supported]
for lang in supported:
session.add(lang)
session.commit()


def load_demo_stories(session):
"Load the stories."
demo_langs = _demo_languages()
service = Service(session)
langdefs = [service.get_language_def(langname) for langname in demo_langs]
langdefs = [d for d in langdefs if d["language"].is_supported]

r = Repository(session)
for d in langdefs:
for b in d["books"]:
r.add(b)
r.commit()

repo = SystemSettingRepository(session)
repo.set_value("IsDemoData", True)
session.commit()

svc = StatsService(session)
svc.refresh_stats()


def load_demo_data(session):
"""
Load the data.
"""
load_demo_languages(session)
load_demo_stories(session)
repo = SystemSettingRepository(session)
repo.set_value("IsDemoData", True)
session.commit()
class Service:
"Demo database service."

def __init__(self, session):
self.session = session

def _demo_languages(self):
"""
Demo languages to be loaded for new users.
Also loaded during tests.
"""
return [
"Arabic",
"Classical Chinese",
"Czech",
"English",
"French",
"German",
"Greek",
"Hindi",
"Japanese",
"Russian",
"Sanskrit",
"Spanish",
"Turkish",
]

def set_load_demo_flag(self):
"Set the flag."
repo = SystemSettingRepository(self.session)
repo.set_value("LoadDemoData", True)
self.session.commit()

def remove_load_demo_flag(self):
"Set the flag."
repo = SystemSettingRepository(self.session)
repo.delete_key("LoadDemoData")
self.session.commit()

def _flag_exists(self, flagname):
"True if flag exists, else false."
repo = SystemSettingRepository(self.session)
return repo.key_exists(flagname)

def should_load_demo_data(self):
return self._flag_exists("LoadDemoData")

def contains_demo_data(self):
return self._flag_exists("IsDemoData")

def remove_flag(self):
"""
Remove IsDemoData setting.
"""
if not self.contains_demo_data():
raise RuntimeError("Can't delete non-demo data.")

repo = SystemSettingRepository(self.session)
repo.delete_key("IsDemoData")
self.session.commit()

def tutorial_book_id(self):
"""
Return the book id of the tutorial.
"""
if not self.contains_demo_data():
return None
sql = """select BkID from books
inner join languages on LgID = BkLgID
where LgName = 'English' and BkTitle = 'Tutorial'
"""
r = self.session.execute(text(sql)).first()
if r is None:
return None
return int(r[0])

def delete_demo_data(self):
"""
If this is a demo, wipe everything.
"""
if not self.contains_demo_data():
raise RuntimeError("Can't delete non-demo data.")
self.remove_flag()
lute.db.management.delete_all_data(self.session)

# Loading demo data.

def load_demo_languages(self):
"""
Load selected predefined languages, if they're supported.
This method will also be called during acceptance tests, so it's public.
"""
demo_langs = self._demo_languages()
service = LanguageService(self.session)
langs = [service.get_language_def(langname).language for langname in demo_langs]
supported = [lang for lang in langs if lang.is_supported]
for lang in supported:
self.session.add(lang)
self.session.commit()

def load_demo_stories(self):
"Load the stories for any languages already loaded."
demo_langs = self._demo_languages()
service = LanguageService(self.session)
langdefs = [service.get_language_def(langname) for langname in demo_langs]

langrepo = LanguageRepository(self.session)
langdefs = [
d
for d in langdefs
if d.language.is_supported
and langrepo.find_by_name(d.language.name) is not None
]

r = Repository(self.session)
for d in langdefs:
for b in d.books:
r.add(b)
r.commit()

repo = SystemSettingRepository(self.session)
repo.set_value("IsDemoData", True)
self.session.commit()

svc = StatsService(self.session)
svc.refresh_stats()

def _db_has_data(self):
"True of the db contains any language data."
sql = "select LgID from languages limit 1"
r = self.session.execute(text(sql)).first()
return r is not None

def load_demo_data(self):
"""
Load the data.
"""
if self._db_has_data():
self.remove_load_demo_flag()
return

repo = SystemSettingRepository(self.session)
do_load = repo.get_value("LoadDemoData")
if do_load is None:
# Only load if flag is explicitly set.
return

do_load = bool(int(do_load))
if not do_load:
return

self.load_demo_languages()
self.load_demo_stories()
self.remove_load_demo_flag()
repo.set_value("IsDemoData", True)
self.session.commit()
Loading

0 comments on commit 03c5da9

Please sign in to comment.