summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md29
-rw-r--r--gui-project/cards.py78
-rw-r--r--gui-project/main.py10
-rw-r--r--gui-project/main_ui.py123
-rw-r--r--requirements.txt1
5 files changed, 241 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1404c25
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+## Overview
+
+Repository containing implementation for flashcard-based applications.
+
+### Set up
+
+- Need to have `python3.12` preinstalled
+- Run `./bootstrap-venv.sh` to create the environment and install the packages
+- Need to have flashcard files (can clone the schwiizertuutsch repo for this): `git clone git.0xf7.com:schwiizertuutsch`
+
+### Run applications
+
+#### Running cli-based flashcards
+
+Call `./venv/bin/python cli-project/flashcard_cli.py practice <STATE_FILE> <PATHS_TO_FLASHCARDS>` to start practicing
+
+> STATE_FILE - is a txt file where the progression state is kept - for a more efficient learning with the flashcards, based on how often you got them right or wrong
+
+> PATH_TO_FLASHCARDS - paths to the `.fcard` files containing the flashcards contents
+
+
+#### Running GUI application
+
+To run the GUI application, you need to update paths in `gui-project/main_ui.py`:
+ - `FLASHCARDS_ROOT` = path to folder containing `.fcard` files
+ - `STATE_FILE` = path to file for storing and using the state
+
+ Then run:
+ `./venv/bin/python gui-project/main.py`
diff --git a/gui-project/cards.py b/gui-project/cards.py
new file mode 100644
index 0000000..ededff9
--- /dev/null
+++ b/gui-project/cards.py
@@ -0,0 +1,78 @@
+import asyncio
+
+from nicegui import ui
+
+class CardComponent():
+ def __init__(self, front : str, back : str):
+ if front is None or front == "":
+ raise Exception("Cannot have empty front on the card")
+
+ if back is None or back == "":
+ raise Exception("Cannot have empty back on the card")
+
+ self.front = front
+ self.back = back
+
+ def get_front(self):
+ return self.front
+
+ def get_back(self):
+ return self.back
+
+class CardUI():
+ def __init__(self, parent_ui, card : CardComponent):
+ self.parent_ui = parent_ui
+ self.card = card
+ self.is_resolved = asyncio.Event()
+ self.init_card()
+ self.correctly_answered = False
+
+ def init_card(self):
+ with self.parent_ui:
+ self.row_parent = ui.row()
+ self.init_front()
+ self.init_back()
+
+ def init_front(self):
+ with self.row_parent:
+ with ui.card().classes('bg-frontc w-[600px] mx-auto') as self.front:
+ ui.markdown("**Front**")
+ ui.separator()
+ ui.markdown(self.card.get_front()).style('white-space: pre-wrap')
+ ui.button("Revert", on_click=lambda: self.revert_card())
+ self.front.set_visibility(False)
+
+ def init_back(self):
+ with self.row_parent:
+ with ui.card().classes('bg-back w-[600px] mx-auto') as self.back:
+ ui.markdown("_Back_")
+ ui.separator()
+ ui.markdown(self.card.get_back()).style('white-space: pre-wrap')
+ with ui.button_group():
+ ui.button("Got it", on_click=lambda: self.user_clicked_correct())
+ ui.button("Ain't got it", on_click=lambda: self.user_clicked_incorrect())
+ self.back.set_visibility(False)
+
+ def show_front(self):
+ self.back.set_visibility(False)
+ self.front.set_visibility(True)
+
+ def show_back(self):
+ self.front.set_visibility(False)
+ self.back.set_visibility(True)
+
+ def revert_card(self):
+ self.show_back()
+
+ def user_clicked_correct(self):
+ self.correctly_answered = True
+ self.is_resolved.set()
+
+ def user_clicked_incorrect(self):
+ self.is_resolved.set()
+
+ async def is_answered(self):
+ await self.is_resolved.wait()
+
+ def hide_card(self):
+ self.row_parent.delete()
diff --git a/gui-project/main.py b/gui-project/main.py
new file mode 100644
index 0000000..d98b2c7
--- /dev/null
+++ b/gui-project/main.py
@@ -0,0 +1,10 @@
+from nicegui import ui
+from main_ui import MainUI
+
+FLASHCARDS_ROOT="/home/andreear/git/schwiizertuutsch/flashcards"
+
+@ui.page("/")
+def main_page():
+ MainUI()
+
+ui.run(port=3011) \ No newline at end of file
diff --git a/gui-project/main_ui.py b/gui-project/main_ui.py
new file mode 100644
index 0000000..fcb1f5a
--- /dev/null
+++ b/gui-project/main_ui.py
@@ -0,0 +1,123 @@
+from nicegui import ui
+import os
+import random
+import sys
+from cards import CardUI, CardComponent
+from flashcards import Session
+
+FLASHCARDS_ROOT="/home/andreear/git/schwiizertuutsch/flashcards"
+STATE_FILE="/home/andreear/git/flashcards/state.txt"
+
+class MainUI():
+ def __init__(self):
+ # ===== default value for a session ========
+ self.nb_questions = 1
+ self.prompt_type = "Front"
+ self.fcards = ["saetze-allgemein.fcard"]
+ self.run_mode = "practice"
+
+ # ========= start initializing UI ===========
+ with ui.header() as self.header:
+ self.header_element = ui.markdown("Session Configuration")
+ self.root_ui = ui.row().classes("fixed-center")
+ with self.root_ui:
+ with ui.card(align_items="baseline") as self.session_configuration:
+ self.initialize_session_ui()
+
+ def initialize_session_ui(self):
+ all_fcards = os.listdir(FLASHCARDS_ROOT)
+
+ with ui.row(align_items="center"):
+ ui.label("Number of questions:")
+ self.nb_questions_ui = ui.number(placeholder='e.g.: 20', min=1, step=1, precision=0, value=15).props(
+ 'rounded outlined dense')
+ with ui.row(align_items="center"):
+ ui.label("Prompt type:")
+ self.prompt_sel_ui = ui.select(["Front", "Back", "Random"], value="Back")
+ with ui.row(align_items="center"):
+ ui.label("Select cards to play from:")
+ self.cards_sel_ui = ui.select(all_fcards, multiple=True, value=all_fcards[:2]) \
+ .classes('w-64').props('use-chips')
+ with ui.button_group():
+ ui.button("Practice mode", color="positive",
+ on_click=lambda: self.prepare_session("practice"))
+ ui.button("Test mode", color="negative",
+ on_click=lambda: self.prepare_session("test"))
+
+ async def prepare_session(self, run_mode : str):
+ self.header.clear()
+ self.run_mode = run_mode
+ if run_mode == "practice":
+ self.header.classes("bg-positive")
+ else:
+ self.header.classes("bg-negative")
+
+ with self.header:
+ self.header_element = ui.markdown(f"Currently in **{run_mode}** mode\nQuestion 1/{self.nb_questions_ui.value}")
+ self.run_mode = run_mode
+ # read values from UI and set session configuration
+ self.nb_questions = int(self.nb_questions_ui.value)
+ self.prompt_type = self.prompt_sel_ui.value
+ self.fcards = self.cards_sel_ui.value
+
+ assert type(self.fcards) is list
+ assert type(self.nb_questions) is int
+ assert type(self.prompt_type) is str
+
+ self.session_configuration.set_visibility(False)
+ await self.start_session()
+
+ async def start_session(self):
+ ui.notify("Started session")
+ # initialize cards paths
+ cards_paths = [os.path.join(FLASHCARDS_ROOT, x) for x in self.fcards]
+ # initialize session
+ session = Session("brutal", cards_paths, STATE_FILE)
+ # initialize final score
+ final_score = 0
+ # set ui.colors to be used
+ ui.colors(frontc='#bcdbc6', back="#c4bcdb", final="#9cfcfc")
+
+ # Start session
+ if self.run_mode == "practice":
+ for i, card in enumerate(session.practice(self.nb_questions)):
+ correctly_answered = await self.showCard(i, card)
+ final_score += 1 if correctly_answered else 0
+ elif self.run_mode == "test":
+ for i, (card, correct) in enumerate(session.test(self.nb_questions)):
+ correctly_answered = await self.showCard(i, card)
+ correct(correctly_answered)
+ final_score += 1 if correctly_answered else 0
+
+ # Wrap up session
+ with self.root_ui:
+ with ui.card(align_items="center").classes("bg-final"):
+ ui.markdown("## Finished")
+ ui.label(f"Your final score is: {final_score}/{self.nb_questions}")
+ ui.label("To start again, refresh the page")
+
+ async def showCard(self, idx : int, card):
+ self.header_element.set_content(
+ f"Currently in **{self.run_mode}** mode\n\nQuestion {idx + 1}/{int(self.nb_questions_ui.value)}")
+ prompt_type_binary = self.prompt_type.lower()
+ if self.prompt_type.lower() == "random":
+ random_nb = random.randint(0, sys.maxsize)
+ if random_nb % 2 == 0:
+ prompt_type_binary = "front"
+ else:
+ prompt_type_binary = "back"
+
+ if prompt_type_binary == "front":
+ card_component = CardComponent(card.front, card.back)
+ else:
+ card_component = CardComponent(card.back, card.front)
+
+ card_ui = CardUI(self.root_ui, card_component)
+
+ card_ui.show_front()
+ await card_ui.is_answered()
+ card_ui.hide_card()
+ return card_ui.correctly_answered
+
+
+
diff --git a/requirements.txt b/requirements.txt
index 17a9a3d..efd1b01 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,5 @@
click
pytest
+nicegui
-e flashcards-project
-e cli-project