0% found this document useful (0 votes)
9 views18 pages

Crit C

The document outlines the development of a teacher-student workflow system that includes features for assignment creation, submission, and grading, with a focus on OCR integration for automated grading. It details the architecture of the application, including distinct model classes for users, classes, assignments, and submissions, as well as the integration with Firebase for authentication and data management. The document also describes the processes for capturing student submissions, extracting text via OCR, and grading submissions based on recognized answers.

Uploaded by

ka20230019
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views18 pages

Crit C

The document outlines the development of a teacher-student workflow system that includes features for assignment creation, submission, and grading, with a focus on OCR integration for automated grading. It details the architecture of the application, including distinct model classes for users, classes, assignments, and submissions, as well as the integration with Firebase for authentication and data management. The document also describes the processes for capturing student submissions, extracting text via OCR, and grading submissions based on recognized answers.

Uploaded by

ka20230019
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

Crit C : Development

Overview

My application is a teacher–student workflow system that simplifies


assignment creation, submission, and grading. Its most innovative component
is the OCR integration, which automates part of the grading process by reading
and matching text from student submissions. Below, I detail how each
component is built and why I chose these particular structures and libraries.

1. Data Models and Structures


1.1 Rationale for Separate Model Classes
To keep the code organized and maintainable, I implemented four distinct
model classes—UserModel, ClassModel, AssignmentModel, and
SubmissionModel. Each model:
1. Defines entity-specific attributes (e.g., title, dueDate for
AssignmentModel).
2. Exposes a fromMap constructor, allowing direct conversion from Firestore
documents to Dart objects.
3. Provides a toMap method for easy uploading or updating data back to
Firestore.

This approach ensures loose coupling between the database schema and the
application logic. If I need to change or add new attributes in the future, I can
do so without breaking other parts of the code.

1.2 UserModel

class UserModel {
final String uid;
final String email;
final String role; // "teacher" or "student"
final String displayName;

UserModel({
required this.uid,
required this.email,

Crit C : Development 1
required this.role,
required this.displayName,
});

factory UserModel.fromMap(Map<String, dynamic> data, String documen


tId) {
return UserModel(
uid: documentId,
email: data['email'],
role: data['role'],
displayName: data['displayName'],
);
}

Map<String, dynamic> toMap() {


return {
'email': email,
'role': role,
'displayName': displayName,
};
}
}

Explanation

• uid: The unique user ID provided by FirebaseAuth. Using this ensures a


direct link to Firebase Authentication data.
• role: Can be either "teacher" or "student". This single field enables me to
gate certain app functionalities (e.g., only teachers can grade submissions).

• displayName: A user-friendly name that appears on the UI (e.g., the


teacher’s name displayed in a class list).

Why this matters


• Having a role field in UserModel (and Firestore) allows quick, role-based
UI redirection after login. This prevents needing multiple sign-in pages or
complicated conditional checks in code.

1.3 ClassModel

Crit C : Development 2
class ClassModel {
final String classId;
final String className;
final String teacherId;
final List<String> studentIds;

ClassModel({
required this.classId,
required this.className,
required this.teacherId,
required this.studentIds,
});

factory ClassModel.fromMap(Map<String, dynamic> data, String docume


ntId) {
return ClassModel(
classId: documentId,
className: data['className'],
teacherId: data['teacherId'],
studentIds: List<String>.from(data['studentIds'] ?? []),
);
}

Map<String, dynamic> toMap() {


return {
'className': className,
'teacherId': teacherId,
'studentIds': studentIds,
};
}
}

Explanation

• className: Human-readable name of the class (e.g., “Math 101”).


• teacherId: The uid of the teacher who owns or created the class.
• studentIds: A list of uids for all students enrolled in this class. The
List<String> type is straightforward for referencing which students belong to

Crit C : Development 3
which class.

Relavance

• This structure simplifies queries. For example, if I need to find all classes
for a student, I only need to check if the student’s ID is in studentIds. This is an
efficient way to store many-to-one relationships (many students to one class).

1.4 AssignmentModel

class AssignmentModel {
final String assignmentId;
final String classId;
final String title;
final String description;
final DateTime dueDate;

AssignmentModel({
required this.assignmentId,
required this.classId,
required this.title,
required this.description,
required this.dueDate,
});

factory AssignmentModel.fromMap(Map<String, dynamic> data, String do


cumentId) {
return AssignmentModel(
assignmentId: documentId,
classId: data['classId'],
title: data['title'],
description: data['description'],
dueDate: (data['dueDate'] as Timestamp).toDate(),
);
}

Map<String, dynamic> toMap() {


return {
'classId': classId,
'title': title,

Crit C : Development 4
'description': description,
'dueDate': dueDate,
};
}
}

Explanation

• classId: Ties the assignment to a specific class.

• dueDate: Stored as a DateTime in Dart, converted from a Firestore


Timestamp.

• title and description: Let teachers provide details about what students
must do.

Justification

• Assignments are a core object in the system. By separating them from the
class, teachers can reuse or reorder assignments, or even link them to multiple
classes if the app expands.

1.5 SubmissionModel

class SubmissionModel {
final String submissionId;
final String assignmentId;
final String studentId;
final String fileUrl;
final double grade; // -1 means ungraded
final String feedback; // For teacher feedback

SubmissionModel({
required this.submissionId,
required this.assignmentId,
required this.studentId,
required this.fileUrl,
required this.grade,
required this.feedback,
});

factory SubmissionModel.fromMap(Map<String, dynamic> data, String do

Crit C : Development 5
cId) {
return SubmissionModel(
submissionId: docId,
assignmentId: data['assignmentId'],
studentId: data['studentId'],
fileUrl: data['fileUrl'],
grade: data['grade'].toDouble(),
feedback: data['feedback'] ?? '',
);
}

Map<String, dynamic> toMap() {


return {
'assignmentId': assignmentId,
'studentId': studentId,
'fileUrl': fileUrl,
'grade': grade,
'feedback': feedback,
};
}
}

Explanation
• assignmentId: Links the submission to the correct assignment for grading.

• studentId: Identifies which student made the submission.

• fileUrl: Points to the location in Firebase Storage (or another storage


backend) where the submission file is stored.
• grade and feedback: Let teachers provide numerical and textual
feedback, updated as needed.

Why this matters


• Storing submissions in a separate collection (rather than inside
AssignmentModel) allows for multiple submissions per assignment, multiple
attempts, or future expansions like re-submissions or peer reviews.

2. Firebase Integration and Authentication


2.1 Firebase Initialization

Crit C : Development 6
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}

Explanation

• WidgetsFlutterBinding.ensureInitialized() ensures that Flutter has a fully


initialized widget system before running asynchronous tasks.
• Firebase.initializeApp() is critical for setting up Firebase services (Auth,
Firestore, Storage) in the app.
Justification
• This is a mandatory step in any Flutter–Firebase project. Without it, calls to
FirebaseAuth or FirebaseFirestore would fail because the Firebase SDK
wouldn’t be initialized.

2.2 Authentication Flow

Future<void> signInWithEmail(String email, String password) async {


UserCredential userCredential = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password);

String uid = userCredential.user!.uid;


// Retrieve user document from Firestore
DocumentSnapshot userDoc = await FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get();

if (userDoc.exists) {
String role = userDoc.get('role');
if (role == 'teacher') {
// Navigate to teacher dashboard
} else {
// Navigate to student dashboard
}

Crit C : Development 7
}
}

Explanation
1. signInWithEmailAndPassword(email, password): Authenticates the user
using Firebase’s built-in email/password method.
2. uid = userCredential.user!.uid: We retrieve a unique User ID from
FirebaseAuth.

3. We fetch the Firestore document for that uid to determine the user’s role.
4. Depending on the role, the app navigates to either the teacher or student
dashboard.

Justification
• Using email/password is a straightforward choice for educational
applications.

• Having the role in Firestore is more flexible than relying solely on Auth
claims. It allows dynamic updates to user roles and easy references in the app.

3. OCR Integration and Automated Grading


3.1 Capturing and Uploading Images (Student Side)

Future<void> pickImage() async {


final ImagePicker _picker = ImagePicker();
final XFile? image = await _picker.pickImage(source: ImageSource.camer
a);

if (image != null) {
// Upload the image to Firebase Storage
String url = await uploadToFirebaseStorage(image.path);

// Save submission data in Firestore


await createSubmission(url);
}
}

Explanation

Crit C : Development 8
• ImagePicker: A popular Flutter plugin to capture or pick images from the
device camera or gallery.

• If the student successfully captures an image,


uploadToFirebaseStorage(image.path) is called. This function (not shown)
handles the Storage API logic and returns a url.

• createSubmission(url) then writes a new SubmissionModel to Firestore,


referencing the uploaded image.
Justification

• Students often do homework on paper, so capturing images is an effective


first step. Using a uniform approach (Firebase Storage) ensures all submissions
can be accessed in a consistent manner for OCR.

3.2 Text Extraction (Teacher / Server Side)

Future<String> extractTextFromImage(String imagePath) async {


final GoogleVisionImage visionImage = GoogleVisionImage.fromFile(File(i
magePath));
final TextRecognizer textRecognizer = GoogleVision.instance.textRecogni
zer();

final VisionText visionText = await textRecognizer.processImage(visionIma


ge);
String extractedText = visionText.text; // All recognized text

textRecognizer.close();
return extractedText;
}

Explanation

• GoogleVisionImage.fromFile(File(imagePath)): Converts a local file path


into a format that the Vision API can process.
• textRecognizer.processImage(visionImage): Performs the OCR
detection.
• visionText.text: The raw text recognized from the image.
Justification

Crit C : Development 9
• Automating text extraction is a crucial feature. By hooking into a
recognized library (google_ml_vision), we ensure a decently accurate OCR
pipeline without implementing the low-level computer vision logic ourselves.

3.3 Automated Grading Logic

double gradeSubmission(String extractedText, Map<String, String> correct


Answers) {
// Example correctAnswers = {"Q1": "Answer1", "Q2": "Answer2"}
double score = 0;
final totalQuestions = correctAnswers.length;

correctAnswers.forEach((question, answer) {
// Simple matching logic
if (extractedText.contains(answer)) {
score += 1;
}
});

// Return percentage (0 to 100)


return (score / totalQuestions) * 100.0;
}

Explanation

• Iterates through each question’s “correct” answer.


• Checks if the recognized text (extractedText) contains that answer.

• Returns a percentage that represents the ratio of matched answers to total


questions.
Justification

• Using .contains(...) is a first iteration: it’s easy to implement but might not
account for variations or typos.
• This can be extended with fuzzy matching (e.g., Levenshtein distance) for
partial-credit logic or AI-based text similarity.

4. Teacher-Side Implementation

4.1 Creating and Managing Classes

Crit C : Development 10
Future<void> createClass(String className) async {
String classId = FirebaseFirestore.instance.collection('classes').doc().id;

ClassModel newClass = ClassModel(


classId: classId,
className: className,
teacherId: FirebaseAuth.instance.currentUser!.uid,
studentIds: [],
);

await FirebaseFirestore.instance
.collection('classes')
.doc(classId)
.set(newClass.toMap());
}

Explanation

• Uses FirebaseFirestore.instance.collection('classes') to create a new class


document.
• Generates a random classId (document ID) and sets the default studentIds
to an empty list.
• Saves the data using the toMap() method from ClassModel.
Justification

• This centralizes the creation logic, so if future changes are needed (e.g.,
auto-enrollment or generating class codes), it is straightforward to add them
here.

4.2 Creating and Managing Assignments

Future<void> createAssignment(String classId, String title, String descriptio


n, DateTime dueDate) async {
String assignmentId = FirebaseFirestore.instance.collection('assignment
s').doc().id;

AssignmentModel newAssignment = AssignmentModel(


assignmentId: assignmentId,

Crit C : Development 11
classId: classId,
title: title,
description: description,
dueDate: dueDate,
);

await FirebaseFirestore.instance
.collection('assignments')
.doc(assignmentId)
.set(newAssignment.toMap());
}

Explanation
• Teachers can directly link the newly created assignment to a specific
classId.

• dueDate allows the teacher to set a clear deadline, which can be displayed
in the UI or used in notifications.
Justification

• Separating assignments in their own collection provides flexibility.


Multiple classes could reuse the same assignment in a future iteration of the
application.

4.3 Grading Submissions and Providing Feedback

Future<void> gradeSubmission(SubmissionModel submission, double grad


e, String feedback) async {
await FirebaseFirestore.instance
.collection('submissions')
.doc(submission.submissionId)
.update({
'grade': grade,
'feedback': feedback,
});
}

Explanation

Crit C : Development 12
• This function updates an existing submission document with a final grade
and textual feedback.
• A teacher might call this after reviewing OCR results to confirm or override
the automatically generated score.

Justification
• Maintaining separate logic for grading vs. creating submissions keeps the
code modular. If the grading algorithm changes, only this function (and related
code) needs updating.

5. Student-Side Implementation
5.1 Viewing Assignments

Stream<List<AssignmentModel>> getStudentAssignments(List<String> stu


dentClassIds) {
return FirebaseFirestore.instance
.collection('assignments')
.where('classId', whereIn: studentClassIds)
.snapshots()
.map((snapshot) {
return snapshot.docs.map((doc) {
return AssignmentModel.fromMap(doc.data(), doc.id);
}).toList();
});
}

Explanation
• A Firebase query that uses .where('classId', whereIn: studentClassIds),
returns only assignments matching any of the classes the student is enrolled in.
• snapshots() creates a real-time data stream, so the UI updates whenever
an assignment is added or changed.

Justification
• Keeping the UI in sync with Firestore is a major strength of
Flutter+Firebase. Students see updates instantly, which improves engagement
and transparency.

5.2 Submitting Assignments

Crit C : Development 13
Future<void> createSubmission(String fileUrl, String assignmentId) async {
String submissionId = FirebaseFirestore.instance.collection('submission
s').doc().id;

SubmissionModel newSubmission = SubmissionModel(


submissionId: submissionId,
assignmentId: assignmentId,
studentId: FirebaseAuth.instance.currentUser!.uid,
fileUrl: fileUrl,
grade: -1, // Ungraded by default
feedback: '',
);

await FirebaseFirestore.instance
.collection('submissions')
.doc(submissionId)
.set(newSubmission.toMap());
}

Explanation
• The student picks an assignment (by assignmentId) and attaches a file
reference (fileUrl).
• A fresh submissionId is generated, and the record is written to the
submissions collection.

Justification
• Defining grade = -1 as a default indicates to both the teacher and the
system that the submission has not been reviewed yet. This prevents confusion
over whether a submission is missing or just ungraded.

5.3 Viewing Grades and Feedback

Stream<SubmissionModel> getSubmission(String submissionId) {


return FirebaseFirestore.instance
.collection('submissions')
.doc(submissionId)
.snapshots()
.map((doc) {

Crit C : Development 14
return SubmissionModel.fromMap(doc.data() as Map<String, dynamic
>, doc.id);
});
}

Explanation

• Similar to assignments, students can stream a single submission


document to immediately see any new feedback or updated grades.
• Ideal for giving real-time updates without refreshing.
Justification
• In a standard learning management system, immediate feedback
encourages better student engagement. This design pattern ensures updates
are automatically pushed to the student’s device.

6. State Management and Architecture


6.1 Using Provider

class UserProvider extends ChangeNotifier {


UserModel? _currentUser;

UserModel? get currentUser => _currentUser;

Future<void> loadUserData(String uid) async {


DocumentSnapshot userDoc = await FirebaseFirestore.instance
.collection('users')
.doc(uid)
.get();

if (userDoc.exists) {
_currentUser = UserModel.fromMap(userDoc.data() as Map<String,dyn
amic>, userDoc.id);
notifyListeners();
}
}
}

Explanation

Crit C : Development 15
• The UserProvider is a class that extends ChangeNotifier, meaning it can
notify any widgets listening to it when user data changes.
• The loadUserData method populates _currentUser from Firestore, then
calls notifyListeners() to force a UI rebuild if needed.
Justification

• This approach centralizes user logic, avoiding repeated calls to Firestore


every time a widget needs user data.
• Clean separation of data (provider) and UI (widgets) fosters simpler
testing and debugging.

7. Testing, Debugging, and Iterative Refinements

7.1 Unit Testing

test('gradeSubmission returns 100 when text contains all correct answers',


() {
Map<String, String> correctAnswers = {'Q1': 'Answer1', 'Q2': 'Answer2'};
String extractedText = 'This is Answer1 and here is Answer2';
double result = gradeSubmission(extractedText, correctAnswers);
expect(result, 100.0);
});

Explanation
• Unit tests focus on isolated functions, such as gradeSubmission.
• Verifies that 100% of answers matched returns a score of 100.0.

Justification
• By verifying the function’s correctness, I catch logical errors before
shipping the product.
7.2 Integration Testing
• Simulated teacher and student flows in a staging environment:
1. Teacher logs in → creates a class → creates an assignment.

2. Student logs in → sees the assignment in their feed → submits an image.


3. Teacher uses OCR to auto-grade → teacher finalizes grade and feedback.
4. Student sees updated grade in real-time.

Crit C : Development 16
Why this matters
• Ensures all components work together. A single unit test passing doesn’t
guarantee that multiple pieces integrated together function correctly.
7.3 Debugging OCR Inaccuracies
• Issue: In low light or with poor handwriting, the OCR results were
incomplete.

• Solution: Perform simple image preprocessing (e.g., convert to grayscale,


enhance contrast) before sending the image to the OCR engine, and prompt the
teacher if confidence is below a certain threshold.
7.4 Performance Optimization
• Indexed certain fields (e.g., classId) in Firestore to handle large queries.
• Tested with ~500 submissions to confirm adequate performance under
typical usage scenarios.

8. Tools and Technologies


• Flutter (Dart): Chosen for cross-platform (Windows, macOS) compatibility
and rapid development.
• Firebase:

• Authentication for login.


• Firestore for real-time, NoSQL data storage.
• Storage for managing large media files (images, PDFs).
• google_ml_vision / Tesseract: Used for optical character recognition to
enable automated grading.

• Provider: A widely accepted state management solution in Flutter,


facilitating clean architecture.
• Git/GitHub: Version control for collaboration and continuous integration.

9. Code Quality, Complexity, and Innovation


1. Modular Design

• Splitting models, providers, and UI ensures readable, scalable code.


2. Real-Time Firestore Streams
• Students and teachers see immediate updates, which is user-friendly and
interactive.

Crit C : Development 17
3. Role-Based Security

• Simple logic (if role == 'teacher' ... else) ensures the same codebase can
handle multiple user types.
4. OCR Integration
• Incorporating text recognition into a student submission process is an
innovative feature for an educational app, streamlining grading and providing a
unique aspect to the project.

10. Conclusion of Development


In summary:
• I built a multi-tier system for teachers and students, covering
class/assignment creation, submission handling, and a specialized OCR-based
grading workflow.

• I iterated on each feature, from the initial prototypes to more polished


versions, guided by unit tests, integration tests, and user feedback.
• The final product addresses both functional needs (automatic grading,
real-time feedback) and non-functional requirements (responsive, role-based
access, scalable with Firebase).
• Future enhancements could incorporate fuzzy matching for grading
partial answers or additional AI-based analytics to measure class performance
trends.
This development journey demonstrates a robust and educational application
of Flutter and Firebase technologies, with OCR providing added complexity and
a valuable automation feature for end users.

Crit C : Development 18

You might also like