AdamyaG commited on
Commit
d291776
·
verified ·
1 Parent(s): 59823a6

Upload 6 files

Browse files
Files changed (6) hide show
  1. app.py +55 -0
  2. constants.py +25 -0
  3. flashcard.py +242 -0
  4. import_export_page.py +48 -0
  5. requirements.txt +4 -0
  6. show_generator_page.py +125 -0
app.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ Flashcards generator using LLM as a backend """
2
+
3
+ import streamlit as st
4
+ # from dotenv import find_dotenv, load_dotenv
5
+
6
+ from flashcard import Flashcards
7
+ # Importing page modules
8
+ from import_export_page import show_import_export_page
9
+ from show_generator_page import show_generator_page
10
+
11
+
12
+ def main():
13
+ """
14
+ Main function to run the Streamlit app for flashcard generation and management.
15
+
16
+ This function initializes the app, setting up the page configuration and session state.
17
+ It provides navigation between the flashcard generator and import/export pages.
18
+ """
19
+
20
+ # Load environment variables from .env file
21
+ # load_dotenv(find_dotenv())
22
+
23
+ # Set Streamlit page configuration
24
+ st.set_page_config(page_title="FG", layout="centered", initial_sidebar_state="auto")
25
+
26
+ # Initialize flashcards in session state if not already present
27
+ if "flashcards" not in st.session_state:
28
+ st.session_state.flashcards = Flashcards([])
29
+
30
+ # Initialize expand_all toggle state in session state
31
+ if "expand_all" not in st.session_state:
32
+ st.session_state.expand_all = False
33
+
34
+ # Define navigation options
35
+ generator_choice = "🤖 Generator"
36
+ import_export_choice = "📂 Import/Export"
37
+
38
+ # Sidebar for navigation
39
+ with st.sidebar:
40
+ # Display application logo
41
+ st.image(
42
+ "https://github.com/mikkac/flashcards_generator/blob/main/resources/logo.png?raw=true"
43
+ )
44
+ # Radio buttons for page selection
45
+ choice = st.radio("Select Page", (generator_choice, import_export_choice))
46
+
47
+ # Conditional rendering of pages based on user choice
48
+ if choice == generator_choice:
49
+ show_generator_page()
50
+ elif choice == import_export_choice:
51
+ show_import_export_page()
52
+
53
+
54
+ if __name__ == "__main__":
55
+ main()
constants.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ File with constants """
2
+
3
+ languages = [
4
+ "English",
5
+ "Spanish",
6
+ "Polish",
7
+ "Chinese",
8
+ "Hindi",
9
+ "Arabic",
10
+ "Portuguese",
11
+ "Bengali",
12
+ "Russian",
13
+ "Japanese",
14
+ ]
15
+
16
+ language_to_flag = {
17
+ "English": "🇬🇧", # Flag of the United Kingdom
18
+ "Spanish": "🇪🇸", # Flag of Spain
19
+ "Polish": "🇵🇱", # Flag of Poland
20
+ "Chinese": "🇨🇳", # Flag of China
21
+ "Hindi": "🇮🇳", # Flag of India
22
+ "Arabic": "🇸🇦", # Flag of Saudi Arabia (Arabic is widely spoken in many countries)
23
+ "Portuguese": "🇵🇹", # Flag of Portugal
24
+ "Japanese": "🇯,,🇵" # Flag of Japan
25
+ }
flashcard.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ File that contains flashcards' backend """
2
+ import json
3
+ import os
4
+ from dataclasses import asdict, dataclass
5
+
6
+ # from dotenv import find_dotenv, load_dotenv
7
+ from langchain.chat_models import ChatOpenAI
8
+ from langchain.output_parsers import ResponseSchema, StructuredOutputParser
9
+ from langchain.prompts import ChatPromptTemplate
10
+
11
+
12
+ @dataclass
13
+ class Flashcard:
14
+ """
15
+ Represents a flashcard containing language translation information.
16
+
17
+ Attributes:
18
+ input_expression (str): The expression in the input language.
19
+ input_language (str): The language of the input expression.
20
+ output_expression (str): The translated expression in the output language.
21
+ output_language (str): The language of the output expression.
22
+ example_usage (str): An example usage of the input expression in a sentence.
23
+ """
24
+
25
+ input_expression: str
26
+ input_language: str
27
+ output_expression: str
28
+ output_language: str
29
+ example_usage: str
30
+
31
+ @classmethod
32
+ def from_dict(cls, data: dict) -> "Flashcard":
33
+ """
34
+ Creates a Flashcard instance from a dictionary of attributes.
35
+
36
+ Args:
37
+ data (dict): A dictionary containing flashcard attributes.
38
+
39
+ Returns:
40
+ Flashcard: An instance of Flashcard.
41
+ """
42
+ return cls(
43
+ input_expression=data.get("input_expression", None),
44
+ input_language=data.get("input_language", None),
45
+ output_expression=data.get("output_expression", None),
46
+ output_language=data.get("output_language", None),
47
+ example_usage=data.get("example_usage", None),
48
+ )
49
+
50
+
51
+ @dataclass
52
+ class Flashcards:
53
+ """
54
+ Represents a collection of Flashcard instances.
55
+
56
+ Attributes:
57
+ data (list[Flashcard]): A list of Flashcard instances.
58
+ """
59
+
60
+ data: list[Flashcard]
61
+
62
+ def as_json(self) -> dict:
63
+ """
64
+ Converts the collection of Flashcard instances to a JSON format.
65
+
66
+ Returns:
67
+ dict: A dictionary representing the flashcards in JSON format.
68
+ """
69
+ return {"flashcards": [asdict(card) for card in self.data]}
70
+
71
+ @classmethod
72
+ def import_from_json(cls, data: dict) -> "Flashcards":
73
+ """
74
+ Creates a Flashcards instance from a JSON file.
75
+
76
+ Args:
77
+ data (file): A JSON file containing flashcard data.
78
+
79
+ Returns:
80
+ Flashcards: An instance of Flashcards containing the imported data.
81
+ """
82
+ data = json.load(data)
83
+ flashcard_objects = [Flashcard(**card) for card in data["flashcards"]]
84
+ return cls(data=flashcard_objects)
85
+
86
+ def __len__(self) -> int:
87
+ """
88
+ Returns the number of Flashcard instances in the collection.
89
+
90
+ Returns:
91
+ int: The number of Flashcard instances.
92
+ """
93
+ return len(self.data)
94
+
95
+
96
+ class FlashcardGeneratorOpenAI: # pylint: disable=R0903
97
+ """
98
+ A class to generate language learning flashcards using OpenAI's language model.
99
+
100
+ Attributes:
101
+ chat (ChatOpenAI): An instance of ChatOpenAI for generating flashcards.
102
+ response_schemas (list): A list of ResponseSchema objects for structuring the response.
103
+ output_parser (StructuredOutputParser): Parser to structure the output
104
+ from the language model.
105
+ flashcard_generator_template (str): A template for generating flashcard data.
106
+ prompt (ChatPromptTemplate): A prompt template for the language model.
107
+ """
108
+
109
+ def __init__(self, api_key: str, llm_model: str = "gpt-3.5-turbo") -> None:
110
+ """
111
+ Initializes the FlashcardGeneratorOpenAI class with
112
+ the specified API key and language model.
113
+
114
+ Args:
115
+ api_key (str): The API key for OpenAI.
116
+ llm_model (str): The name of the language model to use.
117
+ """
118
+ self.chat = ChatOpenAI(temperature=0.0, model=llm_model, api_key=api_key)
119
+
120
+ input_expression_schema = ResponseSchema(
121
+ name="input_expression",
122
+ type="str",
123
+ description="Original expression entered by the user, refined"
124
+ " to create translated_expression.",
125
+ )
126
+ input_language_schema = ResponseSchema(
127
+ name="input_language",
128
+ type="str",
129
+ description="Language of the input expression.",
130
+ )
131
+ output_expression_schema = ResponseSchema(
132
+ name="output_expression",
133
+ type="str",
134
+ description="Translation of refined expression entered by the user.",
135
+ )
136
+ output_language_schema = ResponseSchema(
137
+ name="output_language",
138
+ type="str",
139
+ description="Language of the output expression.",
140
+ )
141
+ example_usage_schema = ResponseSchema(
142
+ name="example_usage",
143
+ type="str",
144
+ description="Example usage of input expression, used to give the user some "
145
+ "example context where it could be used. Limited to one sentence.",
146
+ )
147
+
148
+ response_schemas = [
149
+ input_expression_schema,
150
+ input_language_schema,
151
+ output_expression_schema,
152
+ output_language_schema,
153
+ example_usage_schema,
154
+ ]
155
+
156
+ self.output_parser = StructuredOutputParser.from_response_schemas(
157
+ response_schemas
158
+ )
159
+ self.format_instructions = self.output_parser.get_format_instructions()
160
+
161
+ self.flashcard_generator_template = """\
162
+ For the following expression, extract the following information:
163
+
164
+ input_expression: Original expression entered by the user, but refined to create translated_expression (for flashcard for language learning). If the expression is too long (more than 10 words), it should be shortened while keeping the sense.
165
+
166
+ input_language: Language of the input expression
167
+
168
+ output_expression: Refined input expression translated to {output_language} language. Provide 2 alternatives, separated with 'slash' sign (and space before & after the sign).
169
+
170
+ example_usage: Example usage of input expression, used to give the user some example context where it could be used. Limited to one sentence.
171
+
172
+ input_expression: {input_expression}
173
+ input_language: {input_language}
174
+
175
+ {format_instructions}
176
+ """
177
+
178
+ self.prompt = ChatPromptTemplate.from_template(
179
+ template=self.flashcard_generator_template
180
+ )
181
+
182
+ def generate_flashcard(
183
+ self, input_exp: str, input_lang: str, output_lang: str
184
+ ) -> Flashcard:
185
+ """
186
+ Generates a flashcard by translating an input expression from one language to another.
187
+
188
+ This method takes an expression in a specified input language, translates it into
189
+ a specified output language, and then creates a flashcard containing both the original
190
+ and translated expressions. It uses the ChatOpenAI model to generate the translation
191
+ and example usage.
192
+
193
+ Args:
194
+ input_expression (str): The expression to be translated.
195
+ input_language (str): The language of the input expression.
196
+ output_language (str): The language into which the input expression is to be translated.
197
+
198
+ Returns:
199
+ Flashcard: An instance of the Flashcard class containing the original expression,
200
+ its translation, and an example usage in the output language.
201
+ """
202
+ messages = self.prompt.format_messages(
203
+ input_expression=input_exp,
204
+ input_language=input_lang,
205
+ output_language=output_lang,
206
+ format_instructions=self.format_instructions,
207
+ )
208
+ response = self.chat(messages)
209
+ flashcard_dict = self.output_parser.parse(response.content)
210
+ return Flashcard.from_dict(flashcard_dict)
211
+
212
+
213
+ def main():
214
+ """
215
+ For debugging purposes only
216
+ """
217
+ # _ = load_dotenv(find_dotenv()) # Read local .env file
218
+
219
+ generator = FlashcardGeneratorOpenAI(api_key=os.environ["OPENAI_API_KEY"])
220
+
221
+ input_expressions = [
222
+ "cruel",
223
+ "let someone off the hook",
224
+ "it absorbed me",
225
+ "get my thoughts in order",
226
+ "crude",
227
+ "pore over",
228
+ ]
229
+ input_language = "English"
230
+ output_language = "Polish"
231
+
232
+ flashcards = Flashcards([])
233
+
234
+ for input_expression in input_expressions:
235
+ flashcard = generator.generate_flashcard(
236
+ input_expression, input_language, output_language
237
+ )
238
+ print(flashcard)
239
+ flashcards.data.append(flashcard)
240
+
241
+ if __name__ == "__main__":
242
+ main()
import_export_page.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ Import/export page definition """
2
+ import json
3
+
4
+ import streamlit as st
5
+
6
+ from flashcard import Flashcards
7
+
8
+
9
+ def show_import_export_page():
10
+ """
11
+ Defines a Streamlit page for importing and exporting flashcards.
12
+
13
+ This function creates an interface where users can upload a JSON file
14
+ to import flashcards, and download a JSON file containing the current
15
+ flashcards stored in the session state. It handles file upload, file validation,
16
+ and displays success or error messages accordingly.
17
+ """
18
+
19
+ # Displaying a header for the import section
20
+ st.header("Import file with flashcards")
21
+
22
+ # File uploader widget allowing the user to upload a JSON file
23
+ flashcards_file = st.file_uploader("Select a file", type="json")
24
+
25
+ # Handling the uploaded file
26
+ if flashcards_file is not None:
27
+ try:
28
+ # Attempt to import flashcards from the uploaded file
29
+ st.session_state.flashcards = Flashcards.import_from_json(flashcards_file)
30
+ print(st.session_state.flashcards.as_json()) # Debug print statement
31
+ st.success(f"Imported {len(st.session_state.flashcards)} flashcards!")
32
+ except json.JSONDecodeError:
33
+ # Handling invalid JSON files
34
+ st.error("Invalid JSON file. Please upload a valid JSON file.")
35
+
36
+ # Divider to separate import and export sections
37
+ st.divider()
38
+
39
+ # Displaying a header for the export section
40
+ st.header("Export generated flashcards")
41
+
42
+ # Download button to export the flashcards as a JSON file
43
+ st.download_button(
44
+ "Download flashcards",
45
+ data=json.dumps(st.session_state.flashcards.as_json(), indent=4),
46
+ file_name="flashcards_export.json",
47
+ mime="application/json",
48
+ )
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ streamlit
2
+ langchain
3
+ json
4
+ dataclasses
show_generator_page.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ Flashcards generator page definition """
2
+ import os
3
+
4
+ import streamlit as st
5
+
6
+ from constants import language_to_flag, languages
7
+ from flashcard import Flashcard, FlashcardGeneratorOpenAI
8
+
9
+
10
+ def create_flashcard(
11
+ expression: str, input_language: str, output_language: str
12
+ ) -> Flashcard:
13
+ """
14
+ Creates a Flashcard instance with given expression and languages.
15
+
16
+ Args:
17
+ expression (str): The expression to be included in the flashcard.
18
+ input_language (str): The language of the input expression.
19
+ output_language (str): The target language for translation.
20
+
21
+ Returns:
22
+ Flashcard: A new Flashcard instance.
23
+ """
24
+ return Flashcard(
25
+ input_expression=expression,
26
+ input_language=input_language,
27
+ output_expression=None,
28
+ output_language=output_language,
29
+ example_usage=None,
30
+ )
31
+
32
+
33
+ def create_toggle(col, original: str, translation: str, example: str):
34
+ """
35
+ Creates a toggle (expandable section) in the Streamlit app.
36
+
37
+ Args:
38
+ col: The Streamlit column where the toggle will be placed.
39
+ original (str): The original expression to be displayed.
40
+ translation (str): The translated expression.
41
+ example (str): An example usage of the expression.
42
+ id (str): A unique identifier for the toggle.
43
+ """
44
+ with col:
45
+ with st.expander(original, expanded=st.session_state.expand_all):
46
+ st.write(f"**{translation}**\n\n{example}")
47
+
48
+
49
+ def show_generator(generator: FlashcardGeneratorOpenAI):
50
+ """
51
+ Displays the flashcard generator interface in the Streamlit app.
52
+
53
+ Args:
54
+ generator (FlashcardGeneratorOpenAI): The flashcard generator object.
55
+ """
56
+ col1, col2 = st.columns(2)
57
+ with col1:
58
+ input_language = st.selectbox(
59
+ "Select an input language:", languages, index=languages.index("English")
60
+ )
61
+ with col2:
62
+ output_language = st.selectbox(
63
+ "Select an output language:", languages, index=languages.index("Polish")
64
+ )
65
+
66
+ if "input_language" not in st.session_state:
67
+ st.session_state.input_language = input_language
68
+ st.session_state.input_language = input_language
69
+
70
+ if "output_language" not in st.session_state:
71
+ st.session_state.output_language = output_language
72
+ st.session_state.output_language = output_language
73
+
74
+ expression = st.text_input(
75
+ "Expression",
76
+ placeholder="Enter an expression and press Enter to generate a flashcard",
77
+ )
78
+
79
+ if expression and not any(
80
+ flashcard.input_expression == expression
81
+ for flashcard in st.session_state.flashcards.data
82
+ ):
83
+ new_flashcard = generator.generate_flashcard(
84
+ expression, input_language, output_language
85
+ )
86
+ st.session_state.flashcards.data.append(new_flashcard)
87
+
88
+
89
+ def show_expand_button():
90
+ """
91
+ Displays a button to expand or collapse all flashcards in the Streamlit app.
92
+ """
93
+ if st.button("Expand/Collapse All"):
94
+ st.session_state.expand_all = not st.session_state.expand_all
95
+
96
+
97
+ def show_flashcards():
98
+ """
99
+ Displays the generated flashcards in the Streamlit app.
100
+ """
101
+ if len(st.session_state.flashcards) == 0:
102
+ st.info("Generate a flashcard or import a file with previously generated ones")
103
+ else:
104
+ col1, col2 = st.columns(2)
105
+ for idx, flashcard in enumerate(st.session_state.flashcards.data):
106
+ create_toggle(
107
+ col1 if idx % 2 == 0 else col2,
108
+ f"{language_to_flag[flashcard.input_language]} {flashcard.input_expression}",
109
+ f"{language_to_flag[flashcard.output_language]} {flashcard.output_expression}",
110
+ f"{flashcard.example_usage}",
111
+ )
112
+
113
+
114
+ def show_generator_page():
115
+ """
116
+ Sets up the main page of the Streamlit app for the flashcard generator.
117
+ """
118
+ generator = FlashcardGeneratorOpenAI(api_key=os.environ["OPENAI_API_KEY"])
119
+
120
+ st.title("Flashcards generator")
121
+ show_generator(generator)
122
+
123
+ st.divider()
124
+ show_expand_button()
125
+ show_flashcards()