diff --git a/assets/lessons/cells.json b/assets/lessons/cells.json new file mode 100644 index 0000000..5d172ae --- /dev/null +++ b/assets/lessons/cells.json @@ -0,0 +1,102 @@ +[ + { + "question": "Which part of the cell is known as the 'powerhouse'?", + "options": [ + "Nucleus", + "Ribosome", + "Mitochondria", + "Golgi Apparatus" + ], + "correctAnswer": 2 + }, + { + "question": "What is the main function of ribosomes?", + "options": [ + "DNA replication", + "Protein synthesis", + "Energy production", + "Waste removal" + ], + "correctAnswer": 1 + }, + { + "question": "Which organelle is responsible for packaging and transporting proteins?", + "options": [ + "Endoplasmic Reticulum", + "Nucleus", + "Golgi Apparatus", + "Lysosome" + ], + "correctAnswer": 2 + }, + { + "question": "Which structure in the cell controls its activities and contains genetic material?", + "options": [ + "Cytoplasm", + "Nucleus", + "Mitochondria", + "Cell Membrane" + ], + "correctAnswer": 1 + }, + { + "question": "What is the semi-fluid substance inside the cell that contains the organelles?", + "options": [ + "Cytoplasm", + "Nucleoplasm", + "Plasma", + "Protoplasm" + ], + "correctAnswer": 0 + }, + { + "question": "Which organelle is responsible for breaking down waste materials and cellular debris?", + "options": [ + "Ribosome", + "Lysosome", + "Mitochondria", + "Endoplasmic Reticulum" + ], + "correctAnswer": 1 + }, + { + "question": "What is the function of the cell membrane?", + "options": [ + "Protects the cell and controls what enters and exits", + "Stores genetic information", + "Produces energy", + "Synthesizes proteins" + ], + "correctAnswer": 0 + }, + { + "question": "Which type of cell contains a nucleus?", + "options": [ + "Prokaryotic", + "Eukaryotic", + "Both", + "Neither" + ], + "correctAnswer": 1 + }, + { + "question": "Which organelle is involved in the synthesis of lipids and detoxification?", + "options": [ + "Rough Endoplasmic Reticulum", + "Smooth Endoplasmic Reticulum", + "Golgi Apparatus", + "Ribosome" + ], + "correctAnswer": 1 + }, + { + "question": "What is transported by vesicles that bud off from the Golgi apparatus?", + "options": [ + "DNA", + "Proteins", + "RNA", + "Lipids" + ], + "correctAnswer": 1 + } +] diff --git a/assets/lessons/cells.md b/assets/lessons/cells.md new file mode 100644 index 0000000..aa336d3 --- /dev/null +++ b/assets/lessons/cells.md @@ -0,0 +1,47 @@ +Topic: Basics of Cell Structure and Function +1. Introduction (5 Minutes) + + Objective: Provide an overview of what the student will learn in the session. + Engagement Question: "Have you ever wondered what the smallest unit of life looks like and how it functions?" + +2. Overview of Cells (10 Minutes) + + Definition: Explain what a cell is and its importance. + Types of Cells: Differentiate between prokaryotic and eukaryotic cells with simple examples (e.g., bacteria vs. human cells). + Scale and Visualization: Show images/diagrams of cells under a microscope. + +3. Cell Structure (15 Minutes) + + Cell Membrane: Describe its function as a protective barrier and its role in regulating what enters and leaves the cell. + Cytoplasm: Explain it as the 'factory floor' where most cellular activities occur. + Nucleus: Introduce it as the 'control center' of the cell, containing DNA. + Mitochondria: Explain its role as the 'powerhouse' of the cell, generating energy. + Ribosomes: Describe their function in protein synthesis. + Other Organelles: + Endoplasmic Reticulum (ER): Differentiate between rough ER and smooth ER. + Golgi Apparatus: Explain its role in packaging and transporting proteins. + Lysosomes: Describe their function in waste removal. + +4. Interactive Activity (10 Minutes) + + Virtual Tour: Use an interactive 3D model of a cell to explore different organelles. + Quiz Questions: + "What is the function of the mitochondria?" + "Where are proteins synthesized in the cell?" + Q&A: Allow the student to ask questions and clarify any doubts. + +5. Real-World Application (5 Minutes) + + Relate to Everyday Life: Discuss how understanding cells can help us learn about diseases, develop medicines, and comprehend how our bodies work. + Current Research: Briefly mention exciting advancements in cell biology (e.g., stem cell research or cancer treatment). + +6. Conclusion and Review (5 Minutes) + + Summary: Recap the key points covered in the session. + Take-Home Message: Emphasize the importance of cells as the building blocks of life. + Homework/Next Steps: Suggest a short reading or video to reinforce the concepts learned. + +7. Feedback (5 Minutes) + + Student Reflection: Ask the student what they found most interesting or challenging. + Tutor Feedback: Provide positive feedback and areas for improvement. diff --git a/lib/bloc/eeg_state.dart b/lib/bloc/eeg_state.dart index 54c3cc2..e69de29 100644 --- a/lib/bloc/eeg_state.dart +++ b/lib/bloc/eeg_state.dart @@ -1,103 +0,0 @@ -import 'dart:async'; -import 'package:gemini_app/config.dart'; -import 'package:http/http.dart' as http; - -import 'package:flutter_bloc/flutter_bloc.dart'; - -class EegState { - final double mind_wandering; - final double focus; - - EegState({ - required this.mind_wandering, - required this.focus, - }); - - String getJsonString() { - return '{"mind_wandering": $mind_wandering, "focus": $focus}'; - } -} - -class EegCubit extends Cubit { - EegCubit() : super(EegState(mind_wandering: 0.9, focus: 0.1)) { - // Start the timer when the cubit is created - if (!isDebug) { - startPolling(); - } - } - - Timer? _timer; - - void startPolling() { - // Poll every 1 second (adjust the duration as needed) - _timer = Timer.periodic(Duration(seconds: 1), (timer) { - // Simulate getting new EEG data - // In a real application, you would fetch this data from your EEG device or API - // double newMindWandering = (DateTime.now().millisecondsSinceEpoch % 100) / 100; - // double newFocus = 1 - newMindWandering; - - fetchEegData().then((data) { - double newMindWandering = data[0]; - double newFocus = data[1]; - // Update the state with the new EEG data - updateEegData(newMindWandering, newFocus); - }); - - // updateEegData(newMindWandering, newFocus); - }); - } - - Future> fetchEegData() async { - if (isDebug) { - return [0.9, 0.1]; // Placeholder ret - } - - final url = Uri.parse('http://192.168.83.153:1234'); - - try { - final response = await http.get(url); - - if (response.statusCode == 200) { - // Split the response body by newline and parse as floats - List values = response.body.trim().split('\n'); - if (values.length == 2) { - return [ - double.parse(values[0]), - double.parse(values[1]), - ]; - } else { - throw Exception('Unexpected response format'); - } - } else { - throw Exception('Failed to load EEG data: ${response.statusCode}'); - } - } catch (e) { - throw Exception('Error fetching EEG data: $e'); - } - } - - void stopPolling() { - _timer?.cancel(); - _timer = null; - } - - void updateEegData(double mindWandering, double focus) { - emit(EegState(mind_wandering: mindWandering, focus: focus)); - print('Mind Wandering: $mindWandering, Focus: $focus'); - } - - @override - Future close() { - stopPolling(); - return super.close(); - } - - void toggleState() { - // Toggle the state between mind_wandering and focus - if (state.mind_wandering > state.focus) { - updateEegData(state.focus, state.mind_wandering); - } else { - updateEegData(state.mind_wandering, state.focus); - } - } -} diff --git a/lib/bloc/gemini_state.dart b/lib/bloc/gemini_state.dart index e3042f8..e944bd9 100644 --- a/lib/bloc/gemini_state.dart +++ b/lib/bloc/gemini_state.dart @@ -4,13 +4,14 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/services.dart'; import 'package:gemini_app/config.dart'; import 'package:gemini_app/bloc/eeg_state.dart'; +import 'package:gemini_app/eeg/eeg_service.dart'; +import 'package:get_it/get_it.dart'; import 'package:google_generative_ai/google_generative_ai.dart'; const String systemPrmpt = """You are an AI tutor helping students understand topics with help of biometric data. You will be supplied with a json containing data extracted from an EEG device, use that data to modify your approach and help the student learn more effectively. At the start you will be provided a script with a lesson to cover. Keep the analysis and responses short. -Use language: POLISH After completing the theoretical part there's a quiz, you can start it yourself at the appropriate time or react to users' request by including at the start of your response @@ -42,18 +43,6 @@ class QuizMessage { }); } -// class Message { -// final String text; -// final MessageType type; -// final MessageSource source; - -// Message({ -// required this.text, -// required this.type, -// required this.source, -// }); -// } - enum MessageType { text, lessonScript, quizQuestion, quizAnswer } class Message { @@ -148,11 +137,7 @@ class GeminiState { static GeminiState get initialState => GeminiState( status: GeminiStatus.initial, - // messages: [Message(text: "Hello, I'm Gemini Pro. How can I help you?", type: MessageType.text, source: MessageSource.agent)], - messages: [ - // Message.fromGeminiContent(Content.model( - // [TextPart("Hello, I'm Gemini Pro. How can I help you?")])) - ], + messages: [], error: '', ); } @@ -160,11 +145,13 @@ class GeminiState { class GeminiCubit extends Cubit { GeminiCubit() : super(GeminiState.initialState); - void startLesson(EegState eegState) async { + void startLesson() async { final quizQuestions = await loadQuizQuestions(); - final String rjp = await rootBundle.loadString('assets/lessons/rjp.md'); - final String prompt = - "Jesteś nauczycielem/chatbotem prowadzącym zajęcia z jednym uczniem. Uczeń ma możliwość zadawania pytań w trakcie, natomiast jesteś odpowiedzialny za prowadzenie lekcji i przedstawienie tematu. Zacznij prowadzić lekcje dla jednego ucznia na podstawie poniszego skryptu:\n$rjp"; + final String lessonScript = await rootBundle.loadString('assets/lessons/cells.md'); + // final String prompt = + // "Jesteś nauczycielem/chatbotem prowadzącym zajęcia z jednym uczniem. Uczeń ma możliwość zadawania pytań w trakcie, natomiast jesteś odpowiedzialny za prowadzenie lekcji i przedstawienie tematu. Zacznij prowadzić lekcje dla jednego ucznia na podstawie poniszego skryptu:\n$rjp"; + final String prompt = + "You are a teacher/chatbot conducting a class with one student. The student has the ability to ask questions during the lesson, while you are responsible for leading the class and presenting the topic. Start conducting the lesson for one student based on the script below:\n$lessonScript"; final safetySettings = [ SafetySetting(HarmCategory.harassment, HarmBlockThreshold.none), @@ -197,7 +184,7 @@ class GeminiCubit extends Cubit { try { final chat = state.model!.startChat(history: [Content.text(prompt)]); final stream = chat.sendMessageStream(Content.text( - "EEG DATA:\n${eegState.getJsonString()}\nPytanie:\n$prompt")); + "EEG DATA:\n${GetIt.instance().state.getJsonString()}\nMessage:\n$prompt")); String responseText = ''; @@ -221,13 +208,9 @@ class GeminiCubit extends Cubit { error: e.toString(), )); } - - // enterQuizMode(); - - // sendMessage(prompt, eegState); } - void sendMessage(String prompt, EegState eegState) async { + void sendMessage(String prompt) async { List messagesWithoutPrompt = state.messages; var messagesWithPrompt = state.messages + [ @@ -246,7 +229,7 @@ class GeminiCubit extends Cubit { .map((mess) => mess.toGeminiContent()) .toList()); final stream = chat.sendMessageStream(Content.text( - "EEG DATA:\n${eegState.getJsonString()}\nWiadomość od ucznia:\n$prompt")); + "EEG DATA:\n${GetIt.instance().state.getJsonString()}\nUser message:\n$prompt")); String responseText = ''; @@ -298,7 +281,7 @@ class GeminiCubit extends Cubit { Future> loadQuizQuestions() async { final String quizJson = - await rootBundle.loadString('assets/lessons/rjp.json'); + await rootBundle.loadString('assets/lessons/cells.json'); final List quizData = json.decode(quizJson); return quizData diff --git a/lib/eeg/eeg_service.dart b/lib/eeg/eeg_service.dart new file mode 100644 index 0000000..e3b6227 --- /dev/null +++ b/lib/eeg/eeg_service.dart @@ -0,0 +1,99 @@ +import 'dart:async'; +import 'package:gemini_app/config.dart'; +import 'package:http/http.dart' as http; + +import 'package:flutter_bloc/flutter_bloc.dart'; + +class EegState { + final double mind_wandering; + final double focus; + + EegState({ + required this.mind_wandering, + required this.focus, + }); + + String getJsonString() { + return '{"mind_wandering": $mind_wandering, "focus": $focus}'; + } +} + +class EegService { + EegState state; + + EegService() : state = EegState(mind_wandering: 0.9, focus: 0.1) { + // Start the timer when the cubit is created + if (!isDebug) { + startPolling(); + } + } + + Timer? _timer; + + void startPolling() { + // Poll every 1 second (adjust the duration as needed) + _timer = Timer.periodic(Duration(seconds: 1), (timer) { + // Simulate getting new EEG data + // In a real application, you would fetch this data from your EEG device or API + // double newMindWandering = (DateTime.now().millisecondsSinceEpoch % 100) / 100; + // double newFocus = 1 - newMindWandering; + + fetchEegData().then((data) { + double newMindWandering = data[0]; + double newFocus = data[1]; + // Update the state with the new EEG data + updateEegData(newMindWandering, newFocus); + }); + + // updateEegData(newMindWandering, newFocus); + }); + } + + Future> fetchEegData() async { + if (isDebug) { + return [0.9, 0.1]; // Placeholder ret + } + + final url = Uri.parse('http://192.168.83.153:1234'); + + try { + final response = await http.get(url); + + if (response.statusCode == 200) { + // Split the response body by newline and parse as floats + List values = response.body.trim().split('\n'); + if (values.length == 2) { + return [ + double.parse(values[0]), + double.parse(values[1]), + ]; + } else { + throw Exception('Unexpected response format'); + } + } else { + throw Exception('Failed to load EEG data: ${response.statusCode}'); + } + } catch (e) { + throw Exception('Error fetching EEG data: $e'); + } + } + + void stopPolling() { + _timer?.cancel(); + _timer = null; + } + + void updateEegData(double mindWandering, double focus) { + state = EegState(mind_wandering: mindWandering, focus: focus); + print('Mind Wandering: $mindWandering, Focus: $focus'); + } + + void toggleState() { + // Toggle the state between mind_wandering and focus + if (state.mind_wandering > state.focus) { + updateEegData(state.focus, state.mind_wandering); + } else { + updateEegData(state.mind_wandering, state.focus); + } + } +} diff --git a/lib/main.dart b/lib/main.dart index f555958..5ad8dee 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:gemini_app/eeg/eeg_service.dart'; import 'package:gemini_app/screens/gemini_chat_screen.dart'; +import 'package:get_it/get_it.dart'; void main() { + GetIt.I.registerSingleton(EegService()); runApp(const MyApp()); } diff --git a/lib/screens/gemini_chat_screen.dart b/lib/screens/gemini_chat_screen.dart index 3449ef4..e48b16e 100644 --- a/lib/screens/gemini_chat_screen.dart +++ b/lib/screens/gemini_chat_screen.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:gemini_app/bloc/eeg_state.dart'; import 'package:gemini_app/bloc/gemini_state.dart'; -import 'package:google_generative_ai/google_generative_ai.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:gemini_app/eeg/eeg_service.dart'; +import 'package:get_it/get_it.dart'; class GeminiScreen extends StatelessWidget { const GeminiScreen({super.key}); @@ -14,7 +13,6 @@ class GeminiScreen extends StatelessWidget { return MultiBlocProvider( providers: [ BlocProvider(create: (context) => GeminiCubit()), - BlocProvider(create: (context) => EegCubit()), ], child: const GeminiChat(), ); @@ -31,6 +29,7 @@ class GeminiChat extends StatefulWidget { class GeminiChatState extends State { final _textController = TextEditingController(); bool _quizMode = false; // Add this line + final EegService _eegService = GetIt.instance(); @override void initState() { @@ -50,23 +49,23 @@ class GeminiChatState extends State { @override void dispose() { - context.read().stopPolling(); + _eegService.stopPolling(); super.dispose(); } void _startConversation() async { - context.read().startLesson(context.read().state); + context.read().startLesson(); } void _sendMessage() async { context .read() - .sendMessage(_textController.text, context.read().state); + .sendMessage(_textController.text); _textController.clear(); } void _toggleEegState() { - context.read().toggleState(); + _eegService.toggleState(); } void _resetConversation() { @@ -93,23 +92,19 @@ class GeminiChatState extends State { padding: const EdgeInsets.all(16.0), child: Column( children: [ - BlocBuilder( - builder: (context, eegState) { - return Card( + Card( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Mind Wandering: ${eegState.mind_wandering.toStringAsFixed(2)}'), - Text('Focus: ${eegState.focus.toStringAsFixed(2)}'), + 'Mind Wandering: ${_eegService.state.mind_wandering.toStringAsFixed(2)}'), + Text('Focus: ${_eegService.state.focus.toStringAsFixed(2)}'), ], ), ), - ); - }, - ), + ), Expanded( child: BlocBuilder( builder: (context, state) { diff --git a/pubspec.lock b/pubspec.lock index 356f398..de052a6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -107,6 +107,14 @@ packages: description: flutter source: sdk version: "0.0.0" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 + url: "https://pub.dev" + source: hosted + version: "7.7.0" google_generative_ai: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 801eacb..304ccaf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: flutter_lints: ^3.0.0 flutter_markdown: ^0.7.3 flutter_bloc: ^8.0.1 + get_it: ^7.7.0 dev_dependencies: flutter_test: @@ -69,8 +70,7 @@ flutter: # - images/a_dot_ham.jpeg assets: - - assets/lessons/rjp.md - - assets/lessons/rjp.json + - assets/lessons/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware