## 1. Prerequisites * Environment requirements in [README.md](../README.md). * Clone the branch `4.1-SolutionViews` of this repository to get the full project with all the view code, and install the dependencies. ```bash git clone --branch 4.1-SolutionViews cd flutter pub get ``` * Init Amplify Configuration If you have followed the previous step (Step3), execute the `amplify pull` command and use the existed environment. You can get this command from the aws console. When you executing this command, it will open your default web browser and ask you to verify. Click login to continue. ```bash $ amplify pull --appId --envName dev ✔ Successfully received Amplify Studio tokens. Amplify AppID found: . Amplify App name is: PowerToolkit Backend environment dev found in Amplify Console app: PowerToolkit ? Choose your default editor: Visual Studio Code ? Choose the type of app that you're building flutter Please tell us about your project ? Where do you want to store your configuration file? ./lib/ ? Do you plan on modifying this backend? Yes Added backend environment config object to your project. Run 'amplify pull' to sync future upstream changes. ``` ## 2. OCRRecord Schema ### 2.1 Update schema.graphql Add OCRRecord schama in `schema.graphql`, and the relationship between User and OCRRecord is one to many. ``` type OCRRecord @model @auth(rules: [{allow: private}]) { id: ID! fullKey: String privateKey: String content: String userID: ID! @index(name: "byUser") } type User @model @auth(rules: [{allow: private}]) { id: ID! username: String email: String avatarKey: String description: String deviceId: String OCRRecords: [OCRRecord] @hasMany(indexName: "byUser", fields: ["id"]) } ``` Using `amplify push` command to push the changes, and you will see a new DynamoDB table created which agaist the best practices of single-table design. Here is the issue for this question: https://github.com/aws-amplify/amplify-cli/issues/431#issuecomment-444303514. Then using `amplify codegen models` command to generate models. As codegen created models don't respect Dart linting rules, you need to replace the User.dart and OCRRecord.dart with below code. **User.dart** ```dart ``` **OCRRecord.dart** ```dart ``` ### 2.2 Update OCRRecordRepository #### 2.2.1 Query with Pagination Query the records for specific user. ```dart Future?> queryOCRRecordsOfUser({ required String userId, GraphQLRequest>? requestForNextResult, }) async { try { var request = requestForNextResult ?? ModelQueries.list( OCRRecord.classType, limit: 30, where: OCRRecord.USERID.eq(userId), ); final response = await Amplify.API.query(request: request).response; return response.data; } catch (e) { rethrow; } } ``` #### 2.2.2 Query by Id ```dart Future getOCRRecordById(String id) async { try { final request = ModelQueries.get(OCRRecord.classType, id); final response = await Amplify.API.query(request: request).response; return response.data; } catch (e) { rethrow; } } ``` #### 2.2.3 Create New Record ```dart Future saveOCRRecord(OCRRecord ocrRecord) async { try { final request = ModelMutations.create(ocrRecord); final response = await Amplify.API.mutate(request: request).response; return response.data!; } catch (e) { rethrow; } } ``` ### 2.3 Update ocr_list_bloc.dart for Pagination we use the `list_bloc.dart` (lib/solution/ocr/list_bloc.dart) to deal with the data displayed in ocr_list view. Firstly, add a parameter to save the quert result object. ```dart PaginatedResult? _paginatedResult; ``` Then, finish the refresh and loadingMore function with below code: ```dart void _refresh(OCRListRefreshEvent event, Emitter emit) async { emit(state.copyWith(loadingState: LoadingState.inProgress)); try { _paginatedResult = await ocrRecordRepo.queryOCRRecordsOfUser(userId: sessionCubit.currentUserId); emit(state.copyWith( loadingState: LoadingState.success, records: await _parse(_paginatedResult!.items), canLoadNextPage: false, )); } on Exception catch (e) { emit(state.copyWith(loadingState: LoadingState.failed, error: e)); } } void _loadingMore(OCRListLoadingMoreEvent event, Emitter emit) async { if (_paginatedResult != null && _paginatedResult!.hasNextResult) { _paginatedResult = await ocrRecordRepo.queryOCRRecordsOfUser( userId: sessionCubit.currentUserId, requestForNextResult: _paginatedResult!.requestForNextResult, ); emit(state.appendRecords(await _parse(_paginatedResult!.items))); } } ``` ## 3. Image Uploading and Processing ### 3.1 Upload Image and Create OCRRecord We need to upload the image and save the new record to OCRRecord table. Below is the code and nothing to be changed here. ```dart void _openImagePicker(OpenImagePicker event, Emitter emit) async { print('_openImagePicker, imageSource: ${event.imageSource}'); emit(state.copyWith(imageSourceActionSheetIsVisible: false)); final pickedImage = await _picker.pickImage(source: event.imageSource); if (pickedImage == null) return; emit(state.copyWith(uploading: true)); // upload image then update table. final uuid = UUID.getUUID(); final imageKey = await storageRepo.uploadPrivateFile( category: StorageCategory.ocrImage, file: File(pickedImage.path), uuid: uuid, ); await ocrRecordRepo.saveOCRRecord(OCRRecord( id: uuid, privateKey: imageKey, // fullKey: 'private/${sessionCubit.identityId}/$imageKey}', userID: sessionCubit.currentUserId, )); emit(state.copyWith(uploading: false)); add(OCRListRefreshEvent()); } ``` ### 3.2 Add S3 Trigger and TriggerFunction in Lambda When the image is uploaded, the S3 Notification will trigger a lambda funtion which will invoke the Solution Kit Api the recognition the text in the image. Firstly, using `Amplify update storage` command to update the storage configuration with a trigger lambda. ```bash $ amplify update storage ? Select from one of the below mentioned services: Content (Images, audio, video, etc.) ✔ Who should have access: · Auth and guest users ✔ What kind of access do you want for Authenticated users? · create/update, read, delete ✔ What kind of access do you want for Guest users? · create/update, read ✔ Do you want to add a Lambda Trigger for your S3 Bucket? (y/N) · yes ✅ Successfully added resource S3Trigger2a9347b5 locally ✔ Do you want to edit the local S3Trigger2a9347b5 lambda function now? (y/N) · no ✅ Successfully updated resource ``` Then you will find a new directory with S3Trigger preffix direction under the `amplify/backend/function` directory. Secondly, push the changes to the cloud with `amplify push` command. You can also open the AWS console to see the lambda funtion with a S3 trigger on it after the deployment is finished. Upload an image through the app, and see the logs printed in the lambda monitor console. Copy the event info to local for testing and you will be able to use the event.json as input to mock function locally. ```bash amplify mock function S3Trigger2a9347b5 --timeout 60 --event src/event.json ``` ### 3.3 Implement Trigger Function #### 3.3.1 Update Amplify Configuration Update resource access permission with `amplify update function` command, and select the previously created function. ```bash $ amplify update function ? Select the Lambda function you want to update S3Trigger2a9347b5 ... ? Which setting do you want to update? Resource access permissions ? Select the categories you want this function to have access to. storage ? Storage has 3 resources in this project. Select the one you would like your Lambda to access OCRRecord:@model(appsync) ? Select the operations you want to permit on OCRRecord:@model(appsync) read, update You can access the following resource attributes as environment variables from your Lambda function API_POWERTOOLKIT_GRAPHQLAPIIDOUTPUT API_POWERTOOLKIT_OCRRECORDTABLE_ARN API_POWERTOOLKIT_OCRRECORDTABLE_NAME ? Do you want to edit the local lambda function now? No ``` Update environment variable: ```shell amplify update function ? Select the Lambda function you want to update S3Trigger2a9347b5 ... ? Which setting do you want to update? Environment variables configuration ? Enter the environment variable name: AIKitsInvokeURL ? Enter the environment variable value: ? Select what you want to do with environment variables: I'm done ? Do you want to edit the local lambda function now? No ``` ## 4. Implement OCR Result Page