Skip to content
This repository was archived by the owner on Jan 27, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/bloc/inference/inference_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class InferenceBloc extends Bloc<InferenceEvent, InferenceState> {
on<InferenceInput>(_onInput, transformer: concurrent());
on<InferenceOutput>(_onOutput, transformer: concurrent());
on<InferenceEnd>(_onEnd);
classification = false;
classification = true;
storageBacklog = 0;

//TODO dynamically load models through constructor
Expand Down Expand Up @@ -88,8 +88,9 @@ class InferenceBloc extends Bloc<InferenceEvent, InferenceState> {
_inferenceService.store(
event.input, classification, event.ratio, event.output!);
}
if (!isClosed && !emit.isDone)
if (!isClosed && !emit.isDone) {
emit(InferenceRunning(event.output, event.ratio));
}
} catch (e) {
log("Error: $e");
}
Expand Down
64 changes: 57 additions & 7 deletions lib/bloc/validation/validation_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'dart:developer';
import 'dart:ui';

import 'package:amplify_flutter/amplify_flutter.dart' as amplify;
import 'package:amplify_storage_s3/amplify_storage_s3.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:visualpt/service/storage_service.dart';
Expand All @@ -14,9 +13,14 @@ part 'validation_state.dart';

class ValidationBloc extends Bloc<ValidationEvent, ValidationState> {
final storage = StorageService();
// Internal list to keep track of processed IDs
final List<String> _processedKeys = [];
// Internal list to keep track of procesed labels
final List<int> _processedInputs = [];

ValidationBloc() : super(const ValidationLoad([])) {
on<ValidationFetch>(_onFetch);
on<ValidationPrev>(_onPrev);
on<ValidationNext>(_onNext);
on<ValidationInput>(_onInput);
on<ValidationEnd>(_onEnd);
Expand All @@ -28,6 +32,39 @@ class ValidationBloc extends Bloc<ValidationEvent, ValidationState> {
add(ValidationNext(keys: storedKeys));
}

FutureOr<void> _onPrev(
ValidationPrev event, Emitter<ValidationState> emit) async {
//Put the ID back into the list of unprocessed IDs
String id = _processedKeys.removeLast();
int classification = _processedInputs.removeLast();
event.keys.insert(0, event.currentKey);
event.keys.insert(0, id);
if (classification == 1) {
storage.move(
sourceKey: 'valid/$id.csv',
destinationKey: 'raw/$id.csv',
sourceAccessLevel: amplify.StorageAccessLevel.guest,
destinationAccessLevel: amplify.StorageAccessLevel.private);
storage.move(
sourceKey: 'valid/$id.csv',
destinationKey: 'raw/$id.csv',
sourceAccessLevel: amplify.StorageAccessLevel.guest,
destinationAccessLevel: amplify.StorageAccessLevel.private);
} else {
storage.move(
sourceKey: 'invalid/$id.csv',
destinationKey: 'raw/$id.csv',
sourceAccessLevel: amplify.StorageAccessLevel.guest,
destinationAccessLevel: amplify.StorageAccessLevel.private);
storage.move(
sourceKey: 'invalid/$id.csv',
destinationKey: 'raw/$id.csv',
sourceAccessLevel: amplify.StorageAccessLevel.guest,
destinationAccessLevel: amplify.StorageAccessLevel.private);
}
add(ValidationNext(keys: event.keys));
}

FutureOr<void> _onNext(
ValidationNext event, Emitter<ValidationState> emit) async {
String imageID, dataID;
Expand All @@ -51,7 +88,7 @@ class ValidationBloc extends Bloc<ValidationEvent, ValidationState> {
}
} else {
imageID = temp;
searchKey = temp.split('.').first;
searchKey = temp.split('.').first.split('/').last;
final dataIndex =
event.keys.indexWhere((element) => element.contains(searchKey));
if (dataIndex != -1) {
Expand All @@ -60,6 +97,7 @@ class ValidationBloc extends Bloc<ValidationEvent, ValidationState> {
throw Exception('No data file found');
}
}
//Get the image and data from the storage
final imageResult = await storage
.getUrl(imageID, amplify.StorageAccessLevel.private)
.result;
Expand All @@ -81,7 +119,7 @@ class ValidationBloc extends Bloc<ValidationEvent, ValidationState> {
emit(ValidationDisplay(
event.keys,
key: searchKey,
originalDataBytes: dataResult.bytes,
dataBytes: dataResult.bytes,
imageUrl: imageResult.url.toString(),
imageHeight: imageHeight,
imageWidth: imageWidth,
Expand All @@ -100,13 +138,25 @@ class ValidationBloc extends Bloc<ValidationEvent, ValidationState> {

FutureOr<void> _onInput(
ValidationInput event, Emitter<ValidationState> emit) {
_processedKeys.add(event.key);
_processedInputs.add(event.input ? 1 : 0);
//Store the validated data
if (event.input) {
storage.upload(S3DataPayload.bytes(event.originalDataBytes),
'valid/${event.key}.csv', amplify.StorageAccessLevel.guest);
//Store the ID of the image and data file in a list in case we need to go back
storage.move(
sourceKey: 'raw/${event.key}.csv',
destinationKey: 'valid/${event.key}.csv');
storage.move(
sourceKey: 'raw/${event.key}.png',
destinationKey: 'valid/${event.key}.png');
} else {
storage.move(
sourceKey: 'raw/${event.key}.csv',
destinationKey: 'invalid/${event.key}.csv');
storage.move(
sourceKey: 'raw/${event.key}.png',
destinationKey: 'invalid/${event.key}.png');
}
storage.remove('raw/${event.key}.csv', amplify.StorageAccessLevel.private);
storage.remove('raw/${event.key}.png', amplify.StorageAccessLevel.private);
//Start the next validation
add(ValidationNext(keys: event.keys));
}
Expand Down
17 changes: 13 additions & 4 deletions lib/bloc/validation/validation_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,36 @@ class ValidationFetch extends ValidationEvent {
List<Object> get props => [];
}

class ValidationNext extends ValidationEvent {
class ValidationPrev extends ValidationEvent {
const ValidationPrev({required this.currentKey, required this.keys});
final String currentKey;
final List<String> keys;

@override
List<Object> get props => [keys];
}

class ValidationNext extends ValidationEvent {
const ValidationNext({required this.keys});
final List<String> keys;

@override
List<Object> get props => [keys];
}

class ValidationInput extends ValidationEvent {
final List<int> originalDataBytes;
final List<int> dataBytes;
final String key;
final List<String> keys;
final bool input;
const ValidationInput(
{required this.originalDataBytes,
{required this.dataBytes,
required this.key,
required this.keys,
required this.input});

@override
List<Object> get props => [originalDataBytes, input];
List<Object> get props => [dataBytes, input];
}

class ValidationEnd extends ValidationEvent {
Expand Down
6 changes: 3 additions & 3 deletions lib/bloc/validation/validation_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ValidationLoad extends ValidationState {

class ValidationDisplay extends ValidationState {
final String key;
final List<int> originalDataBytes;
final List<int> dataBytes;
final String imageUrl;
final int imageHeight;
final int imageWidth;
Expand All @@ -26,7 +26,7 @@ class ValidationDisplay extends ValidationState {
final List<Offset> offsets;
const ValidationDisplay(super.keys,
{required this.key,
required this.originalDataBytes,
required this.dataBytes,
required this.imageUrl,
required this.imageHeight,
required this.imageWidth,
Expand All @@ -36,7 +36,7 @@ class ValidationDisplay extends ValidationState {

@override
List<Object> get props =>
[imageUrl, imageHeight, imageWidth, classification, offsets];
[dataBytes, imageUrl, imageHeight, imageWidth, classification, offsets];
}

class ValidationDispose extends ValidationState {
Expand Down
22 changes: 12 additions & 10 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,11 @@ class VisualPT extends StatelessWidget {
fontFamily: "Unbounded", color: CupertinoColors.black))),
routes: {
"/": (context) => const LandingView(),
"/home": (context) => const HomeView(),
"/video": (context) => VideoView(
"/home": (context) => HomeView(
(BlocProvider.of<AuthBloc>(context).state as AuthSuccess)
.user
.email !=
'gaitertech@gmail.com', //If the email is gaitertech@gmail.com, they are a guest, DO NOT let them store to database
(_) {}),
.email ==
'gaitertech@gmail.com'),
"/validate": (context) => const ValidationView(),
"/settings": (context) => const SettingsView(),
},
Expand All @@ -67,11 +65,15 @@ class VisualPT extends StatelessWidget {
builder: (context) =>
PatientView(assessmentType: assessmentType));
}
// if (route.name == "/video") {
// final args = route.arguments;
// return CupertinoPageRoute(
// builder: (context) => VideoView(onComplete: args.onComplete));
// }
if (route.name == "/video") {
final args = route.arguments as Map<String, dynamic>;
final storeData = args["storageAccess"] as bool;
final onComplete =
args["onComplete"] as dynamic Function(Segment);

return CupertinoPageRoute(
builder: (context) => VideoView(storeData, onComplete));
}
return null;
},
),
Expand Down
10 changes: 6 additions & 4 deletions lib/presenter/validation_presenter.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:visualpt/bloc/validation/validation_bloc.dart';
import 'package:visualpt/views/app_nav.dart';

/// Provided by ValidationBloc
class ValidationPresenter extends StatelessWidget {
final Widget Function(BuildContext context, ValidationDisplay state)
onDisplay;
final Widget Function(BuildContext context, ValidationLoad state) onLoad;
final Widget Function(BuildContext context, ValidationDispose state)
onDispose;
final Widget Function(BuildContext context, ValidationError state) onError;

//TODO: add error handling for...
/*
* "
* The following HttpException was thrown resolving an image codec:
* Connection closed before full header was received
* "
**/
const ValidationPresenter({
super.key,
required this.onDisplay,
required this.onLoad,
required this.onDispose,
required this.onError,
});

Expand All @@ -38,9 +42,7 @@ class ValidationPresenter extends StatelessWidget {
} else if (state is ValidationDisplay) {
return onDisplay(context, state);
} else if (state is ValidationDispose) {
AppNav(context).back();
//TODO: Fix this
return Container();
return onDispose(context, state);
} else {
throw Exception("Undefined Validation State");
}
Expand Down
14 changes: 14 additions & 0 deletions lib/service/storage_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ class StorageService {
}
}

void move(
{required String sourceKey,
required String destinationKey,
StorageAccessLevel sourceAccessLevel = StorageAccessLevel.private,
StorageAccessLevel destinationAccessLevel = StorageAccessLevel.private}) {
Amplify.Storage.move(
source: StorageItemWithAccessLevel(
storageItem: StorageItem(key: sourceKey),
accessLevel: sourceAccessLevel),
destination: StorageItemWithAccessLevel(
storageItem: StorageItem(key: destinationKey),
accessLevel: destinationAccessLevel));
}

void remove(String key, StorageAccessLevel accessLevel) {
Amplify.Storage.remove(
key: key, options: StorageRemoveOptions(accessLevel: accessLevel));
Expand Down
8 changes: 4 additions & 4 deletions lib/views/app_nav.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ class AppNav {
});
}

toPath(String path) {
void toPath({required String path, Object? args}) {
Future(() async {
Navigator.of(context).pushNamed(path);
Navigator.of(context).pushNamed(path, arguments: args);
});
}

toView(Widget view) {
void toView(Widget view) {
Future(() async {
Navigator.of(context).push(
CupertinoPageRoute<void>(
Expand All @@ -30,7 +30,7 @@ class AppNav {
});
}

reset() {
void reset() {
Future(() async {
Navigator.pushNamedAndRemoveUntil(context, '/', (route) => false);
});
Expand Down
2 changes: 1 addition & 1 deletion lib/views/components/video_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class VideoControls extends StatefulWidget {
}

class _VideoControlsState extends State<VideoControls> {
bool classification = false;
bool classification = true;
@override
Widget build(BuildContext context) {
final inf = BlocProvider.of<InferenceBloc>(context),
Expand Down
Loading