From 755e0762c736ba71104c35acdd9d21531dca2c64 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 11 Jan 2023 22:02:12 -0500 Subject: [PATCH 01/43] SAMPLE CAMERA APP --- ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + ios/Podfile | 41 +++ ios/Podfile.lock | 68 ++++ ios/Runner.xcodeproj/project.pbxproj | 68 ++++ .../contents.xcworkspacedata | 3 + ios/Runner/Info.plist | 100 +++--- lib/UI/editor_page.dart | 161 ++++++++++ lib/UI/video_page.dart | 139 +++++++++ lib/main.dart | 129 +------- lib/state/camera_cubit.dart | 211 +++++++++++++ pubspec.lock | 293 ++++++++++++++++++ pubspec.yaml | 12 +- 13 files changed, 1052 insertions(+), 175 deletions(-) create mode 100644 ios/Podfile create mode 100644 ios/Podfile.lock create mode 100644 lib/UI/editor_page.dart create mode 100644 lib/UI/video_page.dart create mode 100644 lib/state/camera_cubit.dart diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee8..ec97fc6f 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee8..c4855bfe 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 00000000..2e0382ea --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project + platform :ios, '14.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 00000000..8243c9e0 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,68 @@ +PODS: + - camera_avfoundation (0.0.1): + - Flutter + - ffmpeg-kit-ios-min-gpl (5.1) + - ffmpeg_kit_flutter_min_gpl (5.1.0): + - ffmpeg_kit_flutter_min_gpl/min-gpl (= 5.1.0) + - Flutter + - ffmpeg_kit_flutter_min_gpl/min-gpl (5.1.0): + - ffmpeg-kit-ios-min-gpl (= 5.1) + - Flutter + - Flutter (1.0.0) + - libwebp (1.2.4): + - libwebp/demux (= 1.2.4) + - libwebp/mux (= 1.2.4) + - libwebp/webp (= 1.2.4) + - libwebp/demux (1.2.4): + - libwebp/webp + - libwebp/mux (1.2.4): + - libwebp/demux + - libwebp/webp (1.2.4) + - path_provider_ios (0.0.1): + - Flutter + - video_player_avfoundation (0.0.1): + - Flutter + - video_thumbnail (0.0.1): + - Flutter + - libwebp + +DEPENDENCIES: + - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) + - ffmpeg_kit_flutter_min_gpl (from `.symlinks/plugins/ffmpeg_kit_flutter_min_gpl/ios`) + - Flutter (from `Flutter`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) + - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`) + +SPEC REPOS: + trunk: + - ffmpeg-kit-ios-min-gpl + - libwebp + +EXTERNAL SOURCES: + camera_avfoundation: + :path: ".symlinks/plugins/camera_avfoundation/ios" + ffmpeg_kit_flutter_min_gpl: + :path: ".symlinks/plugins/ffmpeg_kit_flutter_min_gpl/ios" + Flutter: + :path: Flutter + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" + video_player_avfoundation: + :path: ".symlinks/plugins/video_player_avfoundation/ios" + video_thumbnail: + :path: ".symlinks/plugins/video_thumbnail/ios" + +SPEC CHECKSUMS: + camera_avfoundation: 07c77549ea54ad95d8581be86617c094a46280d9 + ffmpeg-kit-ios-min-gpl: ac5eb47f98b27e26d14c009de3ce9181007ce688 + ffmpeg_kit_flutter_min_gpl: f657651b493b7608fad9dda3ad606eb0946c9faa + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef + path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff + video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1 + +PODFILE CHECKSUM: fe5e039928e08cc3aa55730590fe5577354d32d2 + +COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c50b5b53..93d18374 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + DF9913B344CB67476283E499 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F24DF8C1B03080865541129 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -35,6 +36,7 @@ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 8344BFF53557D3E087D322A4 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -42,6 +44,9 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9F24DF8C1B03080865541129 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EA0411E3AF748213EFDFBDA0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + F8BB7CD19972C49A7D9CC042 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,12 +54,32 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + DF9913B344CB67476283E499 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 295C2E2AB62C11FDFA17AF2A /* Pods */ = { + isa = PBXGroup; + children = ( + EA0411E3AF748213EFDFBDA0 /* Pods-Runner.debug.xcconfig */, + 8344BFF53557D3E087D322A4 /* Pods-Runner.release.xcconfig */, + F8BB7CD19972C49A7D9CC042 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 3DEF271E828DB41DE7BDD265 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9F24DF8C1B03080865541129 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +97,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 295C2E2AB62C11FDFA17AF2A /* Pods */, + 3DEF271E828DB41DE7BDD265 /* Frameworks */, ); sourceTree = ""; }; @@ -105,12 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 067F723BD754716381856C27 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 9F4EC215FE88CD9D17F4CA0F /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -169,6 +198,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 067F723BD754716381856C27 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -197,6 +248,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + 9F4EC215FE88CD9D17F4CA0F /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 4c6faa11..dcb1e9c3 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,51 +1,57 @@ - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Visualpt - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - visualpt - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Visualpt + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + visualpt + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + NSCameraUsageDescription + VisualPT needs access to the camera to take photos and videos + NSBonjourServices + + _dartobservatory._tcp + + diff --git a/lib/UI/editor_page.dart b/lib/UI/editor_page.dart new file mode 100644 index 00000000..eb7a63b1 --- /dev/null +++ b/lib/UI/editor_page.dart @@ -0,0 +1,161 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; +import 'package:video_editor/video_editor.dart'; +import 'package:helpers/helpers.dart' show OpacityTransition; + +class EditorPage extends StatefulWidget { + const EditorPage({Key? key, required this.file}) : super(key: key); + + final File file; + + @override + State createState() => _EditorPageState(); +} + +class _EditorPageState extends State { + final double height = 60; + late VideoEditorController _c; + late Key trimmerKey = const Key("0"); + + @override + void initState() { + super.initState(); + _c = VideoEditorController.file(widget.file, + maxDuration: const Duration(seconds: 60)) + ..initialize().then((_) => setState(() {})); + } + + @override + void dispose() { + _c.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + leading: ElevatedButton( + onPressed: () => Navigator.pop(context), + child: const Icon(CupertinoIcons.back))), + child: _c.initialized + ? Stack( + alignment: Alignment.center, + children: [ + Stack( + alignment: Alignment.bottomCenter, + children: [ + FractionallySizedBox( + widthFactor: 1, + child: GestureDetector( + onTap: () { + if (_c.isPlaying) { + _c.video.pause(); + } else { + _c.video.play(); + } + }, + child: VideoPlayer(_c.video), + ), + ), + Positioned( + bottom: 20, + child: Row( + children: [ + SizedBox( + height: height, + width: MediaQuery.of(context).size.width / 3, + child: TrimSlider( + key: trimmerKey, + controller: _c, + ), + ), + const SizedBox(width: 10), + CupertinoButton( + padding: const EdgeInsets.only( + top: 1, bottom: 1, right: 20, left: 20), + color: CupertinoColors.link, + child: SizedBox( + height: height, + child: const Center( + child: Text( + "TODO EXPORT", + ), + )), + onPressed: () { + final _duration = _c.endTrim - _c.startTrim; + //TODO Export the video here + final v = VideoPlayer(_c.video); + _c.video.pause().then((_) {}); + }), + ], + ), + ), + ], + ), + AnimatedBuilder( + animation: _c.video, + builder: (_, __) => OpacityTransition( + visible: !_c.isPlaying, + child: GestureDetector( + onTap: _c.video.play, + child: Container( + alignment: Alignment.center, + width: 40, + height: 40, + decoration: const BoxDecoration( + color: CupertinoColors.white, + shape: BoxShape.circle, + ), + child: const Icon(CupertinoIcons.play_arrow, + color: CupertinoColors.black), + ), + ), + ), + ), + ], + ) + : const Center(child: CupertinoActivityIndicator()), + ); + } + + CupertinoButton trimIndexButton(bool isForward, bool isMax) { + final IconData icon = + isForward ? CupertinoIcons.right_chevron : CupertinoIcons.left_chevron; + + return CupertinoButton( + padding: const EdgeInsets.only(top: 1, bottom: 1, right: 10, left: 10), + child: Icon(icon), + onPressed: () { + _c.video.pause(); + setState(() { + if (isMax && isForward && _c.maxTrim < 0.95) { + _c.updateTrim(_c.minTrim, _c.maxTrim + 0.05); + _c.video + .seekTo(Duration(milliseconds: _c.endTrim.inMilliseconds - 50)); + } else if (isMax && !isForward && (_c.maxTrim - _c.minTrim > 0.1)) { + _c.updateTrim(_c.minTrim, _c.maxTrim - 0.05); + _c.video.seekTo( + Duration(milliseconds: _c.endTrim.inMilliseconds - 100)); + } else if (!isMax && isForward && (_c.maxTrim - _c.minTrim > 0.1)) { + _c.updateTrim(_c.minTrim + 0.05, _c.maxTrim); + _c.video.seekTo(_c.startTrim); + } else if (!isMax && !isForward && _c.minTrim > 0.05) { + _c.updateTrim(_c.minTrim - 0.05, _c.maxTrim); + _c.video.seekTo(_c.startTrim); + } + + trimmerKey = Key((_c.maxTrim - _c.minTrim).toString()); + }); + }, + ); + } + + String formatter(Duration duration) => [ + duration.inSeconds.remainder(99).toString().padLeft(2, '0'), + duration.inMilliseconds.remainder(1000).toString().padLeft(3, '0')[0] + ].join("."); +} diff --git a/lib/UI/video_page.dart b/lib/UI/video_page.dart new file mode 100644 index 00000000..7e11396f --- /dev/null +++ b/lib/UI/video_page.dart @@ -0,0 +1,139 @@ +import 'package:camera/camera.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/state/camera_cubit.dart'; + +class VideoPage extends StatefulWidget { + const VideoPage({Key? key}) : super(key: key); + + @override + _VideoPageState createState() => _VideoPageState(); +} + +class _VideoPageState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => CameraCubit()..initCamera(), + child: BlocBuilder( + builder: (context, state) { + return CupertinoPageScaffold( + child: state is CameraError + ? Center( + child: Column( + children: [ + Text(state.exception.toString()), + CupertinoButton( + onPressed: () { + BlocProvider.of(context) + .resetCamera(); + }, + child: const Text("Retry")) + ], + ), + ) + : cameraWidget(context, state)); + }, + ), + ); + } + + Widget stopwatchWidget(BuildContext context, CameraState state) { + return SizedBox( + height: MediaQuery.of(context).size.height / 8, + width: MediaQuery.of(context).size.width / 2, + child: DecoratedBox( + decoration: BoxDecoration( + color: CupertinoColors.systemRed, + borderRadius: BorderRadius.circular(4), + ), + child: Center( + child: Text( + (() { + if (state is CameraRecording) { + return formatter(state.duration); + } else { + return formatter(const Duration(seconds: 0)); + } + }()), + style: const TextStyle(color: Color(0xFF000000))), + ), + ), + ); + } + + Widget cameraWidget(BuildContext context, CameraState state) { + return SizedBox( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Stack( + alignment: Alignment.center, + children: (() { + if (state is CameraRecording) { + return [ + CameraPreview(state.controller), + Positioned( + bottom: 10, + child: GestureDetector( + onTap: () => BlocProvider.of(context) + .triggerState(context, state), + child: const Icon( + CupertinoIcons.stop, + color: CupertinoColors.systemRed, + size: 80, + ), + ), + ), + Positioned( + top: 20, + right: 20, + child: Row( + children: [ + stopwatchWidget(context, state), + ], + ), + ), + ]; + } else if (state is CameraStandby) { + return [ + CameraPreview(state.controller), + Positioned( + bottom: 10, + child: GestureDetector( + onTap: () => BlocProvider.of(context) + .triggerState(context, state), + child: const Icon( + CupertinoIcons.circle, + color: CupertinoColors.black, + size: 80, + ), + ), + ), + Positioned( + top: 20, + right: 20, + child: Row( + children: [ + stopwatchWidget(context, state), + ], + ), + ), + ]; + } else { + return [const Center(child: CupertinoActivityIndicator())]; + } + }()), + ), + ); + } + + String formatter(Duration duration) => [ + duration.inSeconds.remainder(60).toString().padLeft(2, '0'), + duration.inMilliseconds.remainder(1000).toString().padLeft(3, '0')[0] + ].join("."); +} diff --git a/lib/main.dart b/lib/main.dart index c077dda7..94b0078b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,134 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:visualpt/UI/video_page.dart'; -void main() { - runApp(const MyApp()); -} - -//TODO VisualPT -//Install Amplify Backend Services -//Enable Auth (email/password combo, no third-party auth services) -//Enable GraphQL data services -//Enable HIPAA compliant storage -//Establish therapist data model -//Establish patient data model -//Establish assessment data model (for each assessment?) -//Create UI for login/signup -//Create UI for test list -//Create UI flow of Assessment -//Train AI for CT-SIB Assessment (all 6 parts) -//Create UI for result page -//Connect PDF to email (until EMR integration is available) -//Create UI for asseessment tutorial -//Create marketing content -//Market to physical therapists -//Contact EMR services for connection ($2k investment) +void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + return const MaterialApp( + title: 'Video Recorder App', + home: VideoPage(), ); } } diff --git a/lib/state/camera_cubit.dart b/lib/state/camera_cubit.dart new file mode 100644 index 00000000..87e9e836 --- /dev/null +++ b/lib/state/camera_cubit.dart @@ -0,0 +1,211 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/services.dart'; +import 'package:camera/camera.dart'; +import 'package:visualpt/UI/editor_page.dart'; + +@immutable +abstract class CameraState {} + +class CameraLoading extends CameraState {} + +class CameraRecording extends CameraState { + final IconData icon = CupertinoIcons.square; + final CameraController controller; + final Duration duration; + CameraRecording({required this.controller, required this.duration}); +} + +class CameraStandby extends CameraState { + final IconData icon = CupertinoIcons.circle; + final CameraController controller; + CameraStandby({required this.controller}); +} + +class CameraError extends CameraState { + final IconData icon = CupertinoIcons.restart; + final Exception exception; + CameraError({required this.exception}); +} + +class CameraCubit extends Cubit { + Timer? timer; + late Duration duration = const Duration(seconds: 0); + + CameraCubit() : super(CameraLoading()); + void startTimer(CameraController controller) { + timer = Timer.periodic(const Duration(milliseconds: 10), (_) { + duration = Duration(milliseconds: duration.inMilliseconds + 10); + + emit(CameraRecording(controller: controller, duration: duration)); + }); + } + + void resetTimer() { + timer?.cancel(); + duration = const Duration(seconds: 0); + } + + void initCamera() async { + try { + final camera = await availableCameras().then((_cameras) => + _cameras.firstWhere( + (camera) => camera.lensDirection == CameraLensDirection.back)); + final cameraController = CameraController( + camera, + ResolutionPreset.medium, + enableAudio: false, + imageFormatGroup: ImageFormatGroup.bgra8888, + ); + await cameraController.initialize(); + emit(CameraStandby(controller: cameraController)); + } catch (e) { + emit(CameraError(exception: Exception(e.toString()))); + } + //TODO Handle exception when no camera available + } + + void triggerState(BuildContext context, CameraState state) async { + try { + if (state is CameraRecording) { + resetTimer(); + XFile file = await state.controller.stopVideoRecording(); + CupertinoPageRoute route = CupertinoPageRoute( + fullscreenDialog: true, + builder: (_) => EditorPage(file: File(file.path))); + Navigator.push(context, route); + initCamera(); + } else if (state is CameraStandby) { + startTimer(state.controller); + state.controller.startVideoRecording(); + } + } on Exception catch (e) { + resetTimer(); + emit(CameraError(exception: e)); + } + } + + void resetCamera() async { + emit(CameraLoading()); + initCamera(); + } + //TODO build robust videocamera widget using methods below +} + + // @override + // void initState() { + // super.initState(); + // WidgetsBinding.instance.addObserver(this); + + // } + + // @override + // void dispose() { + // WidgetsBinding.instance.removeObserver(this); + // super.dispose(); + // } + + // // #docregion AppLifecycle + // @override + // void didChangeAppLifecycleState(AppLifecycleState state) { + // final CameraController? cameraController = widget.controller; + + // // App state changed before we got the chance to initialize. + // if (cameraController == null || !cameraController.value.isInitialized) { + // return; + // } + + // if (state == AppLifecycleState.inactive) { + // cameraController.dispose(); + // } else if (state == AppLifecycleState.resumed) { + // onNewCameraSelected(cameraController.description); + // } + // } + // void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + // if (widget.controller == null) { + // return; + // } + + // final CameraController cameraController = widget.controller!; + + // final Offset offset = Offset( + // details.localPosition.dx / constraints.maxWidth, + // details.localPosition.dy / constraints.maxHeight, + // ); + // cameraController.setExposurePoint(offset); + // cameraController.setFocusPoint(offset); + // } + // Future onNewCameraSelected(CameraDescription cameraDescription) async { + // final CameraController? oldController = widget.controller; + // if (oldController != null) { + // // `controller` needs to be set to null before getting disposed, + // // to avoid a race condition when we use the controller that is being + // // disposed. This happens when camera permission dialog shows up, + // // which triggers `didChangeAppLifecycleState`, which disposes and + // // re-creates the controller. + // widget.controller = null; + // await oldController.dispose(); + // } + + // final CameraController cameraController = CameraController( + // cameraDescription, + // kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, + // enableAudio: enableAudio, + // imageFormatGroup: ImageFormatGroup.jpeg, + // ); + + // widget.controller = cameraController; + + // // If the controller is updated then update the UI. + // cameraController.addListener(() { + // if (mounted) { + // setState(() {}); + // } + // if (cameraController.value.hasError) { + // showInSnackBar( + // 'Camera error ${cameraController.value.errorDescription}'); + // } + // }); + + // try { + // await cameraController.initialize(); + // } on CameraException catch (e) { + // switch (e.code) { + // case 'CameraAccessDenied': + // showInSnackBar('You have denied camera access.'); + // break; + // case 'CameraAccessDeniedWithoutPrompt': + // // iOS only + // showInSnackBar('Please go to Settings app to enable camera access.'); + // break; + // case 'CameraAccessRestricted': + // // iOS only + // showInSnackBar('Camera access is restricted.'); + // break; + // case 'AudioAccessDenied': + // showInSnackBar('You have denied audio access.'); + // break; + // case 'AudioAccessDeniedWithoutPrompt': + // // iOS only + // showInSnackBar('Please go to Settings app to enable audio access.'); + // break; + // case 'AudioAccessRestricted': + // // iOS only + // showInSnackBar('Audio access is restricted.'); + // break; + // default: + // _showCameraException(e); + // break; + // } + // } + + // if (mounted) { + // setState(() {}); + // } + // } + // void showInSnackBar(String message) { + // ScaffoldMessenger.of(context) + // .showSnackBar(SnackBar(content: Text(message))); + // } diff --git a/pubspec.lock b/pubspec.lock index 56c9d413..319adde3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.9.0" + bloc: + dependency: transitive + description: + name: bloc + url: "https://pub.dartlang.org" + source: hosted + version: "8.1.0" boolean_selector: dependency: transitive description: @@ -15,6 +22,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + camera: + dependency: "direct main" + description: + name: camera + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.1" + camera_android: + dependency: transitive + description: + name: camera_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.2+1" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.10+1" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.3" + camera_web: + dependency: transitive + description: + name: camera_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.1" characters: dependency: transitive description: @@ -36,6 +78,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.16.0" + cross_file: + dependency: transitive + description: + name: cross_file + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3+2" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.2" cupertino_icons: dependency: "direct main" description: @@ -50,11 +106,46 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + ffmpeg_kit_flutter_min_gpl: + dependency: transitive + description: + name: ffmpeg_kit_flutter_min_gpl + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + ffmpeg_kit_flutter_platform_interface: + dependency: transitive + description: + name: ffmpeg_kit_flutter_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.4" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + url: "https://pub.dartlang.org" + source: hosted + version: "8.1.1" flutter_lints: dependency: "direct dev" description: @@ -62,11 +153,44 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + helpers: + dependency: "direct main" + description: + name: helpers + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.1" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" lints: dependency: transitive description: @@ -95,6 +219,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -102,6 +233,90 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.2" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.22" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + provider: + dependency: transitive + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.5" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" sky_engine: dependency: transitive description: flutter @@ -128,6 +343,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -149,6 +371,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.12" + transparent_image: + dependency: transitive + description: + name: transparent_image + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" vector_math: dependency: transitive description: @@ -156,5 +385,69 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + video_editor: + dependency: "direct main" + description: + name: video_editor + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + video_player: + dependency: "direct main" + description: + name: video_player + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.10" + video_player_android: + dependency: transitive + description: + name: video_player_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.10" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.8" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" + video_player_web: + dependency: transitive + description: + name: video_player_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + video_thumbnail: + dependency: transitive + description: + name: video_thumbnail + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.3" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+3" sdks: dart: ">=2.18.5 <3.0.0" + flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index b680bad5..3073750e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A new Flutter project. # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -20,7 +20,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=2.18.5 <3.0.0' + sdk: ">=2.18.5 <3.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -32,10 +32,15 @@ dependencies: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + camera: ^0.10.1 + path_provider: ^2.0.11 + video_player: ^2.4.10 + flutter_bloc: ^8.1.1 + video_editor: ^2.2.0 + helpers: ^1.2.0 dev_dependencies: flutter_test: @@ -53,7 +58,6 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. From 415a3ff24de41be2fa47e4f187d83346f787702d Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 21 Jan 2023 22:05:21 -0500 Subject: [PATCH 02/43] Base Views --- lib/views/analysis_view.dart | 10 +++++ lib/views/assessment_view.dart | 11 +++++ lib/views/auth_view.dart | 13 ++++++ lib/views/components/view_background.dart | 49 +++++++++++++++++++++++ lib/views/home_view.dart | 11 +++++ lib/views/styles.dart | 13 ++++++ lib/views/view.dart | 22 ++++++++++ lib/views/views.dart | 6 +++ 8 files changed, 135 insertions(+) create mode 100644 lib/views/analysis_view.dart create mode 100644 lib/views/assessment_view.dart create mode 100644 lib/views/auth_view.dart create mode 100644 lib/views/components/view_background.dart create mode 100644 lib/views/home_view.dart create mode 100644 lib/views/styles.dart create mode 100644 lib/views/view.dart create mode 100644 lib/views/views.dart diff --git a/lib/views/analysis_view.dart b/lib/views/analysis_view.dart new file mode 100644 index 00000000..486daf2a --- /dev/null +++ b/lib/views/analysis_view.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class AnalysisView extends StatelessWidget { + const AnalysisView({super.key}); + + @override + Widget build(BuildContext context) { + return Container(); + } +} \ No newline at end of file diff --git a/lib/views/assessment_view.dart b/lib/views/assessment_view.dart new file mode 100644 index 00000000..3bdec12b --- /dev/null +++ b/lib/views/assessment_view.dart @@ -0,0 +1,11 @@ + +import 'package:flutter/material.dart'; + +class AssessmentView extends StatelessWidget { + const AssessmentView({super.key}); + + @override + Widget build(BuildContext context) { + return Container(); + } +} \ No newline at end of file diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart new file mode 100644 index 00000000..bcffe3e4 --- /dev/null +++ b/lib/views/auth_view.dart @@ -0,0 +1,13 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/view.dart'; + +class AuthView extends StatelessWidget { + const AuthView({super.key}); + + @override + Widget build(BuildContext context) { + return const View( + child: Text("TODO Implement"), + ); + } +} diff --git a/lib/views/components/view_background.dart b/lib/views/components/view_background.dart new file mode 100644 index 00000000..57ae43d8 --- /dev/null +++ b/lib/views/components/view_background.dart @@ -0,0 +1,49 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/styles.dart'; + +class ViewBackground extends StatelessWidget { + const ViewBackground({super.key}); + + @override + Widget build(BuildContext context) { + final screenSize = MediaQuery.of(context).size; + double decorSize() => screenSize.height * (Random().nextInt(10) + 50) / 100; + double decorX() => + Random().nextInt(screenSize.width ~/ 3) - (decorSize() / 2); + double decorY() => + Random().nextInt(screenSize.height ~/ 3) - (decorSize() / 2); + + return Stack( + alignment: Alignment.center, + children: [ + SizedBox( + width: screenSize.width, + height: screenSize.height, + child: Container( + decoration: const BoxDecoration(color: Styles.backgroundPrimary), + ), + ), + Positioned( + top: decorY(), + left: decorX(), + child: Container( + height: decorSize(), + width: decorSize(), + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.backgroundAccent)), + ), + Positioned( + bottom: decorY(), + right: decorX(), + child: Container( + height: decorSize(), + width: decorSize(), + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.backgroundAccent)), + ), + ], + ); + } +} diff --git a/lib/views/home_view.dart b/lib/views/home_view.dart new file mode 100644 index 00000000..e87196f5 --- /dev/null +++ b/lib/views/home_view.dart @@ -0,0 +1,11 @@ +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; + +class HomeView extends StatelessWidget { + const HomeView({super.key}); + + @override + Widget build(BuildContext context) { + return Container(); + } +} \ No newline at end of file diff --git a/lib/views/styles.dart b/lib/views/styles.dart new file mode 100644 index 00000000..68f70231 --- /dev/null +++ b/lib/views/styles.dart @@ -0,0 +1,13 @@ +import 'package:flutter/cupertino.dart'; + +///Singleton class for consistency of styles across the app. Access styles through Styles. +class Styles { + ///SINGLETON CONFIG DO NOT MODIFY + static final Styles _instance = Styles._internal(); + factory Styles() => _instance; + Styles._internal(); + + ///Background-related styles + static const Color backgroundPrimary = Color(0xFF91DAFF); + static const Color backgroundAccent = Color(0xFF4EC3FF); +} diff --git a/lib/views/view.dart b/lib/views/view.dart new file mode 100644 index 00000000..5eca9acd --- /dev/null +++ b/lib/views/view.dart @@ -0,0 +1,22 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/components/view_background.dart'; + +class View extends StatelessWidget { + const View({super.key, required this.child}); + final Widget child; + + @override + Widget build(BuildContext context) { + final screenSize = MediaQuery.of(context).size; + + return Stack( + children: [ + const ViewBackground(), + SizedBox( + height: screenSize.height, + width: screenSize.width, + child: Container(child: child)) + ], + ); + } +} diff --git a/lib/views/views.dart b/lib/views/views.dart new file mode 100644 index 00000000..440ae8ce --- /dev/null +++ b/lib/views/views.dart @@ -0,0 +1,6 @@ +///Barrel File for ALL views. Use this when importing more than 1 view into a file. + +export 'analysis_view.dart'; +export 'assessment_view.dart'; +export 'auth_view.dart'; +export 'home_view.dart'; From f98bb814adcca14e300bfe75d92b0269ec782592 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 21 Jan 2023 22:06:22 -0500 Subject: [PATCH 03/43] Debuggable on personal device --- ios/Podfile.lock | 118 ++++++++++++++++++ ios/Runner.xcodeproj/project.pbxproj | 68 ++++++++++ .../contents.xcworkspacedata | 3 + 3 files changed, 189 insertions(+) create mode 100644 ios/Podfile.lock diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 00000000..f07f65a9 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,118 @@ +PODS: + - Amplify (1.28.3): + - Amplify/Default (= 1.28.3) + - Amplify/Default (1.28.3) + - amplify_auth_cognito_ios (0.0.1): + - Amplify (= 1.28.3) + - amplify_core + - AmplifyPlugins/AWSCognitoAuthPlugin (= 1.28.3) + - Flutter + - ObjectMapper + - amplify_core (0.0.1): + - Flutter + - SwiftFormat/CLI + - SwiftLint (= 0.48.0) + - amplify_datastore (0.0.1): + - Amplify (= 1.28.3) + - amplify_core + - AmplifyPlugins/AWSDataStorePlugin (= 1.28.3) + - Flutter + - amplify_flutter_ios (0.0.1): + - Amplify (= 1.28.3) + - amplify_core + - AmplifyPlugins/AWSCognitoAuthPlugin (= 1.28.3) + - AWSPluginsCore (= 1.28.3) + - Flutter + - SwiftFormat/CLI + - SwiftLint (= 0.48.0) + - AmplifyPlugins/AWSCognitoAuthPlugin (1.28.3): + - AWSAuthCore (~> 2.28.0) + - AWSCognitoIdentityProvider (~> 2.28.0) + - AWSCognitoIdentityProviderASF (~> 2.28.0) + - AWSCore (~> 2.28.0) + - AWSMobileClient (~> 2.28.0) + - AWSPluginsCore (= 1.28.3) + - AmplifyPlugins/AWSDataStorePlugin (1.28.3): + - AWSCore (~> 2.28.0) + - AWSPluginsCore (= 1.28.3) + - SQLite.swift (= 0.13.2) + - AWSAuthCore (2.28.5): + - AWSCore (= 2.28.5) + - AWSCognitoIdentityProvider (2.28.5): + - AWSCognitoIdentityProviderASF (= 2.28.5) + - AWSCore (= 2.28.5) + - AWSCognitoIdentityProviderASF (2.28.5): + - AWSCore (= 2.28.5) + - AWSCore (2.28.5) + - AWSMobileClient (2.28.5): + - AWSAuthCore (= 2.28.5) + - AWSCognitoIdentityProvider (= 2.28.5) + - AWSCognitoIdentityProviderASF (= 2.28.5) + - AWSCore (= 2.28.5) + - AWSPluginsCore (1.28.3): + - Amplify (= 1.28.3) + - AWSCore (~> 2.28.0) + - Flutter (1.0.0) + - ObjectMapper (4.2.0) + - SQLite.swift (0.13.2): + - SQLite.swift/standard (= 0.13.2) + - SQLite.swift/standard (0.13.2) + - SwiftFormat/CLI (0.50.5) + - SwiftLint (0.48.0) + +DEPENDENCIES: + - amplify_auth_cognito_ios (from `.symlinks/plugins/amplify_auth_cognito_ios/ios`) + - amplify_core (from `.symlinks/plugins/amplify_core/ios`) + - amplify_datastore (from `.symlinks/plugins/amplify_datastore/ios`) + - amplify_flutter_ios (from `.symlinks/plugins/amplify_flutter_ios/ios`) + - Flutter (from `Flutter`) + +SPEC REPOS: + trunk: + - Amplify + - AmplifyPlugins + - AWSAuthCore + - AWSCognitoIdentityProvider + - AWSCognitoIdentityProviderASF + - AWSCore + - AWSMobileClient + - AWSPluginsCore + - ObjectMapper + - SQLite.swift + - SwiftFormat + - SwiftLint + +EXTERNAL SOURCES: + amplify_auth_cognito_ios: + :path: ".symlinks/plugins/amplify_auth_cognito_ios/ios" + amplify_core: + :path: ".symlinks/plugins/amplify_core/ios" + amplify_datastore: + :path: ".symlinks/plugins/amplify_datastore/ios" + amplify_flutter_ios: + :path: ".symlinks/plugins/amplify_flutter_ios/ios" + Flutter: + :path: Flutter + +SPEC CHECKSUMS: + Amplify: e89b493fc2fe840ac044bd4ddf71ad9828f8c364 + amplify_auth_cognito_ios: e532d70387aa0fe6f456855afe78ea275d58fe4b + amplify_core: 0876b2fbbcec7619f5b58b20c59ecc0118f65a47 + amplify_datastore: 4aa847727e99901625642db923deb86ee4c33817 + amplify_flutter_ios: 8943b5e5fb995a7aa3eb5c5238a6822756703584 + AmplifyPlugins: 98bcd23f2c13179f75c733b887e80dd06243be5e + AWSAuthCore: e728d73a2b4eed9e826506a1f0fcc58171537267 + AWSCognitoIdentityProvider: 261fac98d461e9e398bf3c1e18c3cd90578b33ca + AWSCognitoIdentityProviderASF: 8e35d6414d9ee2443e9167414ef5e839a58e1e72 + AWSCore: 4bad3f0eb17393b4926a9491e32b835991c56382 + AWSMobileClient: c898658a4d15e64e07d39c7dc6354fd3ea3055a9 + AWSPluginsCore: 93f813579479fa4c5bfff58817df89e0e10f9f3b + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81 + SQLite.swift: 4fc2be46c36392e3b87afe6fe7f1801c1daa07ef + SwiftFormat: fc64190719f3655079b7b4c246182383bd89026f + SwiftLint: 284cea64b6187c5d6bd83e9a548a64104d546447 + +PODFILE CHECKSUM: d2243213672c3c48aae53c36642ba411a6be7309 + +COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c50b5b53..558ba264 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 374774678789BA8A8A5A3E14 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C613CAEA31C604B21DC511E /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -29,12 +30,15 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0269A0F4EB1031AC6BF050C0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 05CDDC1470545B2B5B0EDD62 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7C613CAEA31C604B21DC511E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -42,6 +46,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DA543580F25D8B34350CB87F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,12 +54,21 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 374774678789BA8A8A5A3E14 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 02CAF58E4D1358DBC9A19049 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 7C613CAEA31C604B21DC511E /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +86,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + A86C41238E6C57A4EB294284 /* Pods */, + 02CAF58E4D1358DBC9A19049 /* Frameworks */, ); sourceTree = ""; }; @@ -98,6 +114,17 @@ path = Runner; sourceTree = ""; }; + A86C41238E6C57A4EB294284 /* Pods */ = { + isa = PBXGroup; + children = ( + 05CDDC1470545B2B5B0EDD62 /* Pods-Runner.debug.xcconfig */, + DA543580F25D8B34350CB87F /* Pods-Runner.release.xcconfig */, + 0269A0F4EB1031AC6BF050C0 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -105,12 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 2EDB72B56FAED939D9E87ACE /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 17E36E41424B2149C1F1E4F5 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -169,6 +198,45 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 17E36E41424B2149C1F1E4F5 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 2EDB72B56FAED939D9E87ACE /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + From 6d0b31b9d279c9a24466bedadd1d1b9cb19bdf4f Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 21 Jan 2023 22:06:58 -0500 Subject: [PATCH 04/43] neccesary packages --- pubspec.lock | 28 ++++++++++++++++++++++++++++ pubspec.yaml | 15 +++++---------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 3d2f9b45..f5ff1fb9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,27 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + amplify_auth_cognito: + dependency: "direct main" + description: + name: amplify_auth_cognito + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.11" + amplify_auth_cognito_android: + dependency: transitive + description: + name: amplify_auth_cognito_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.11" + amplify_auth_cognito_ios: + dependency: transitive + description: + name: amplify_auth_cognito_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.11" amplify_core: dependency: transitive description: @@ -57,6 +78,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.1" + bloc: + dependency: "direct main" + description: + name: bloc + url: "https://pub.dartlang.org" + source: hosted + version: "8.1.0" boolean_selector: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b128fc7e..b125cab4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,24 +3,20 @@ description: A new Flutter project. # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -# In Windows, build-name is used as the major, minor, and patch parts -# of the product and file versions while build-number is used as the build suffix. version: 1.0.0+1 environment: - sdk: '>=2.18.5 <3.0.0' + sdk: ">=2.18.5 <3.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -32,13 +28,13 @@ dependencies: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + bloc: ^8.1.0 amplify_flutter: ^0.6.0 amplify_datastore: ^0.6.0 - + amplify_auth_cognito: ^0.6.0 dev_dependencies: flutter_test: @@ -56,11 +52,10 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. - uses-material-design: true + # uses-material-design: true #Uncomment this line if we decide to use Material instead of Cupertino # To add assets to your application, add an assets section, like this: # assets: From c1f8dc332839f970c60c98ff3b429c94ac21944a Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 21 Jan 2023 22:07:11 -0500 Subject: [PATCH 05/43] Base Views --- lib/main.dart | 111 +++++--------------------------------------------- 1 file changed, 10 insertions(+), 101 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index bed52dfa..68a4c865 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,15 +1,11 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/views.dart'; void main() { - runApp(const MyApp()); + runApp(const VisualPT()); } //TODO VisualPT -//Enable Auth (email/password combo, no third-party auth services) -//Enable GraphQL data services -//Establish therapist data model -//Establish patient data model -//Establish assessment data model (for each assessment?) //Follow Figma Model //Connect PDF to email (until EMR integration is available) //Create UI for asseessment tutorial @@ -17,103 +13,16 @@ void main() { //Market to physical therapists //Contact EMR services for connection ($2k investment) -class MyApp extends StatelessWidget { - const MyApp({super.key}); +class VisualPT extends StatelessWidget { + const VisualPT({super.key}); - // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; + return CupertinoApp(routes: { + "auth": (context) => const AuthView(), + "home": (context) => const HomeView(), + "assessment": (context) => const AssessmentView(), + "analysis": (context) => const AnalysisView(), }); } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } } From 3414244b4042d16a8d1aa55916f8b6d940971b4f Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 25 Jan 2023 00:15:34 -0500 Subject: [PATCH 06/43] routing, suboptimal background --- lib/main.dart | 15 +++-- lib/views/components/view_background.dart | 80 +++++++++++++---------- lib/views/launch_view.dart | 14 ++++ lib/views/views.dart | 5 +- 4 files changed, 69 insertions(+), 45 deletions(-) create mode 100644 lib/views/launch_view.dart diff --git a/lib/main.dart b/lib/main.dart index 68a4c865..72d69705 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,9 +8,9 @@ void main() { //TODO VisualPT //Follow Figma Model //Connect PDF to email (until EMR integration is available) -//Create UI for asseessment tutorial -//Create marketing content -//Market to physical therapists +//Asseessment tutorial +//Marketing content +//Pitch to physical therapists //Contact EMR services for connection ($2k investment) class VisualPT extends StatelessWidget { @@ -19,10 +19,11 @@ class VisualPT extends StatelessWidget { @override Widget build(BuildContext context) { return CupertinoApp(routes: { - "auth": (context) => const AuthView(), - "home": (context) => const HomeView(), - "assessment": (context) => const AssessmentView(), - "analysis": (context) => const AnalysisView(), + "/": (context) => const LaunchView(), + "/auth": (context) => const AuthView(), + "/home": (context) => const HomeView(), + "/assessment": (context) => const AssessmentView(), + "/analysis": (context) => const AnalysisView(), }); } } diff --git a/lib/views/components/view_background.dart b/lib/views/components/view_background.dart index 57ae43d8..4c009f3a 100644 --- a/lib/views/components/view_background.dart +++ b/lib/views/components/view_background.dart @@ -1,49 +1,57 @@ import 'dart:math'; - import 'package:flutter/cupertino.dart'; import 'package:visualpt/views/styles.dart'; class ViewBackground extends StatelessWidget { - const ViewBackground({super.key}); + final int seed; + const ViewBackground({super.key, this.seed = 0}); + + Offset _generateRandomPosition(BuildContext context, bool topHalf) { + final Random random = seed == 0 ? Random() : Random(seed); + + double y = topHalf + ? random.nextDouble() * (MediaQuery.of(context).size.height / 3) + : random.nextDouble() * (MediaQuery.of(context).size.height / 3) + + (MediaQuery.of(context).size.height / 2); + return Offset( + random.nextDouble() * (MediaQuery.of(context).size.width / 3), y); + } @override Widget build(BuildContext context) { - final screenSize = MediaQuery.of(context).size; - double decorSize() => screenSize.height * (Random().nextInt(10) + 50) / 100; - double decorX() => - Random().nextInt(screenSize.width ~/ 3) - (decorSize() / 2); - double decorY() => - Random().nextInt(screenSize.height ~/ 3) - (decorSize() / 2); - - return Stack( - alignment: Alignment.center, - children: [ - SizedBox( - width: screenSize.width, - height: screenSize.height, - child: Container( - decoration: const BoxDecoration(color: Styles.backgroundPrimary), - ), - ), - Positioned( - top: decorY(), - left: decorX(), - child: Container( - height: decorSize(), - width: decorSize(), + final containerSize = MediaQuery.of(context).size.height / 2; + final container1Pos = _generateRandomPosition(context, true); + final container2Pos = _generateRandomPosition(context, false); + return Container( + color: Styles.backgroundPrimary, + child: Stack( + children: [ + Positioned( + left: container1Pos.dx, + top: container1Pos.dy, + child: Container( + width: containerSize, + height: containerSize, decoration: const BoxDecoration( - shape: BoxShape.circle, color: Styles.backgroundAccent)), - ), - Positioned( - bottom: decorY(), - right: decorX(), - child: Container( - height: decorSize(), - width: decorSize(), + shape: BoxShape.circle, + color: Styles.backgroundAccent, + ), + ), + ), + Positioned( + left: container2Pos.dx, + top: container2Pos.dy, + child: Container( + width: containerSize, + height: containerSize, decoration: const BoxDecoration( - shape: BoxShape.circle, color: Styles.backgroundAccent)), - ), - ], + shape: BoxShape.circle, + color: Styles.backgroundAccent, + ), + ), + ), + ], + ), ); } } diff --git a/lib/views/launch_view.dart b/lib/views/launch_view.dart new file mode 100644 index 00000000..e3bbdc2c --- /dev/null +++ b/lib/views/launch_view.dart @@ -0,0 +1,14 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/components/view_background.dart'; + +class LaunchView extends StatelessWidget { + const LaunchView({super.key}); + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + child: Stack( + children: const [ViewBackground(seed: 0)], + )); + } +} diff --git a/lib/views/views.dart b/lib/views/views.dart index 440ae8ce..a8e37c02 100644 --- a/lib/views/views.dart +++ b/lib/views/views.dart @@ -1,6 +1,7 @@ ///Barrel File for ALL views. Use this when importing more than 1 view into a file. -export 'analysis_view.dart'; -export 'assessment_view.dart'; export 'auth_view.dart'; export 'home_view.dart'; +export 'assessment_view.dart'; +export 'launch_view.dart'; +export 'analysis_view.dart'; From 83d935d2b23baf0da7e7e6eb7b67b36e1740e425 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 28 Jan 2023 20:15:17 -0500 Subject: [PATCH 07/43] Base push --- lib/views/base_view.dart | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 lib/views/base_view.dart diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart new file mode 100644 index 00000000..2be42456 --- /dev/null +++ b/lib/views/base_view.dart @@ -0,0 +1,27 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/components/view_background.dart'; + +class BaseView extends StatelessWidget { + const BaseView({super.key, required this.child}); + final Widget child; + + @override + Widget build(BuildContext context) { + final screenSize = MediaQuery.of(context).size; + + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + + ), + child: Stack( + children: [ + const ViewBackground(), + SizedBox( + height: screenSize.height, + width: screenSize.width, + child: Container(child: child)) + ], + ), + ); + } +} From 2cf3e3f8b1755bc73a38379cda317613bec575f7 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:34:33 -0500 Subject: [PATCH 08/43] beginning assets --- assets/unbounded.ttf | Bin 0 -> 771528 bytes assets/vpt_mainlogo.png | Bin 0 -> 23373 bytes pubspec.lock | 170 ++++++++++++++++++++++++++-------------- pubspec.yaml | 22 ++---- 4 files changed, 116 insertions(+), 76 deletions(-) create mode 100644 assets/unbounded.ttf create mode 100644 assets/vpt_mainlogo.png diff --git a/assets/unbounded.ttf b/assets/unbounded.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6550010326a1f4d7c31cdbece68d35bd8d911cf7 GIT binary patch literal 771528 zcmc${2XtJ;^*{dRy_ICE*{W+vwz{o{bN+CwGJ0?3wz+fL+?ALl zNrCuckVK~F6)?3rExP92|H|~<`-6DCkAFF9!Qr>yu$|7m*ia+3` zH*1%cwe7g}m8+$>+kYrY^L!V#r)LdZcXEluz88sESGISQbsQbu{H`?j;Lq^;wX1fn z+B$OIN+!)6dj`KJt=_O|^P!?Y{uIyemn844gZTSRZ;jl9zjxs814FB}ZN~Tcfafpx zK4)nCp23iwPv4a!)+n*Dl|y)b>o;$XP!|$=f8@BH@-T9UxvFi+y6!fq4n^v!4f4u9c#17$kPj$np-J9L6mHvj`AHnmH z8&_>u^VBoH@sik+c;D^#=1tqSm+cyQOk&U8B1zWP&0E)O9(KR#5dIE&Ngkjf;FkX; zpOqB+<|WOOqyV#D45NQhK~aAA*Ky#0yzsyQHCA3=`;7lPYx9@+ziqaEZNuw$Q&RTv zIlQonB`Qc^m;(Qvk-Z;h!$;L;&FZs9+3+J9Hpu08`2}00e0E|&yr;Y@$4kUh4>yHX zOOk#375lii^ee}>VVQm0S9(*7Ph=_k6#aRVVVo76djj*#^q!CY9>Hz6%0AzGm+>9; z@p&}PDTL>hm+_OA6yS=fHO6E%##qghFg||$v0bCNqdQ$NFS=!T*p8(Gdw1y-CoD7V ztIv_%aKci5-ht&z?|JC&aV(eH=ks(K-)GU=T#Yt~jqlunC3OJdvaxB@z zy`aEg_RhsC*SzDZ)Y=<6I~$vpE^SH@{~2z)=iIZ-iY}wrl2crqV<|2RT@Vr)8nPgi zOOblU1?u;id~Oe5nlo`&xmX$2paW#g1u}VXy}EAw0#^`};d9|E-g!-joSv}b@6Y$Q zEX{#1U94+s78GR(;JVt(=qJC?#cor5)Hkf^D@R$2Qe=CglYMe7js1s}zOhw)%a$&m-TKDH zV;eSrpL{m1{bTqpRpepLH!)6p7UKbo&Qe**2izvC9v~%vW7cq)3tUuy62zC2wP!>RnL4&J1r!UZHlF*$u^2t&7#?S+1oz->M!pTiEtREW68E z*lS@2*oIY0QkN8{4^`z^a*GEGtcjK?^%+3SAy7yk#dC-y7Q`XW$N2-u^l|8;M4Ug0 z)G^VcWO9fXN^yW$2RSIlPlMS6$pZPZa+nUXJXY8go4cu1J$`__GrTz@$C96`j`&OMR7dxPKV)}KU zUW@u;mSd>_O|JpdJ^ROnqAjww9?lkeHMTy|)f}FABf7YFlHBP_xht zLhCB)JU2YP<&qcIu6^;6Est;TJmgiky|L>+b@hR+#_e@pEV1`!v(RtNS1m7GnqGcU zd;3M@X`O{c555lH5vgKFuof=)+BnuvoCO4$C2|2s-qI|SnIV#8VW*@{1QGU-ZUL2% z1#+q9<929GZhV|uae#UnY}_>~PGCd3NX#d(8eqvL{yotn{{vjd$;>R80U5;10Wkq) zb{}iGM*XK%{pM=6SpCJdf6x8;YBsOU_8<0LwOckcv5(Y)>|%{mr3`61^ZkvAsC9AjFswVonS8pBt=R#Oa$;GWx99L2&$O73ij?5WNMi0DFt-BCwKJV|XDT%OIq|~6S^NyR*S^qX>B73O^py_K*OT-Es3Q*SD8B&Gn{>uO6pf~MOmzn@i2b!&P zja-wmalrPY)Qw6q#F$_W1^zprrQM|hz^LfI<0oHO0bI+>1Z60DRBdCAPD>j0&;Wa2 z8tPzPPH~M25i`>u?)-SO!1G|bPVCVR_20x>>?zI%!&#QY_h83FlFRrh>DhBXnoJA9 zN2Plf@koq!1|64K{+;w0%| zDGo9FZ>pV_-%pQC3ActWtWRj_uuGhoOB~J&XH5B-Auw?J#370Dhsr)(65T#gBK|i? zRL;*4`4{ZDR@=YjJhi4;X768h&8E@Z1Dox@JM9r$X&*ZlInbxBj~tM!7!WuQCa~Df}hqrb4z4?(I~l>@bmk zD^?Gb$o|$>A^&!sv}igha`thjuE%b5adRgvy;^g0?dy~MFV+u`uCzCgKjLu0J z_(k~murSQ#AacFwr>q>SlWk!Pa&pw=KOOSgva)}9$MPdbYL>>WU$<`O+(U-mdp2x3 z-d1y{XZhaZ=4)1MJl-XHl-CqmGK%^`Gh>UY8>$<(mJ|$E6t*WC0y5gOi+jvvaCc=1 z`dGpHa0E1-M>HXkF zJXX)roUQ0InIj^C5!}iFZ#-g(Wp-lt_L1!2=k~3-WyC6BUu*mvlC4 ztnxiHZ|y_!x#A5+7jJvCzhv9-pcJpF}#UCaEihDuwzXhf^ zK_D5^3%S|s5oeL#wEZBQH4Zf`PuMiHeuw8Fx8A!qZaUstb!4D6P<=r@yRl|M>JQJ2 zudLw`UA&>XvOhcZKLf%}m4Ct|8Ib^zWLnKH#_yOMX8Tl5w!Kw5#=g6qy|BH(R)EK# zF<@uOKjGF3A_&k3TwhA^#G#2MZKhx`Q$Qd^s7w~Y7K(0L`K*;2)<%_v1*9*^&6U?- z0nD#{GqlaiYuLl9s6>8tdjsGizhx_=E0)`{zDf*_QE%$fvDISl;dU#IE5F0LEJIGy}r+*|q`{;ufHbC}H#*V#d> z8{)HuUR8BFVHRk};lg^DjElSUuD}JhEa;-17yTW4z5=Nn z;XMaaX6^kMeH8@%h+XUrom0E6%Z!@r1o|5;^Qzre@8DelF*l9#&sS$%Thml$FgsLm zvvY5G!TK66;xr2rJjK590TMreTp`+}`yU=2zH(4d48?S<)?!GRh#o^{ZQF3P%9xv3 z8f}_Cwje8^B0kw~aM`L&-ec~a$K`W*{Ur%S`EeN`=>^IAueCN8FUndJ7rP)gx2C*m zQ<+_U=xh}<43Iw3mSyN?xbxvi)7j;iMG)UG^KsXkD>rzb0`XHRv_MJp>}V=Go(6t&`bQQ@6^^=or; z*S@#Ucv;BrZT}g*eK5;x-dmJac}Yx0*S?0vy=2u^6M`D5dod`>*Pj$u=fruDGgq?~ChcPi}c| zWd4<&)f;QO4%Sv5TDg3zdXajDxes?U->y$eTMqV_S3J6#Hf8O_E$Q`1<$IU5Us9d5 zvMMINRy{=8M>_#zcWDi$C3$`MTz^qU%_X1KF*YXmkBvc%b=$rxa%eVx_QUP4UKZf3 z8|-i440g`Lk!yhWvk&0UMmb80AL z;R={k=>@)<#W?L|J`Rr)yO-mTeDriL*@UsCz%F_4P2FU7?XpiIoUZa5}R`ZD~czEkWKR^ZjzFAVu(olnGGXACeymdBQQ{>cN_$v{Wd& zV=QAZA)+cs>O%8E@G@p>OpTL6Y=4r^s;%rX z!1v^KXamqidXK-w-yp64cG7dDur&V6{{pB9f1_ELm=-^s2=SKEu`xE)?^9>MMspF=2s@3#`C#$IhF$O0(o8Z z#&4Ic@2lxr*LNp>1J2bcyrN8;#T&)SNl3ks$9SCbI%cPzJurhM(qxLnzk4D~rU-n0 z88hXXqG&YAqokm0B{Dm^E;M z`2HeBH-JhC@tx}imwYAWVIeNg8=o@Ko|}|4I^T&I5Q(5n8d21rwR{dD)^v6*K+~+1)VgK4*0g?IYmG zn)p5ftO0f3o>gIWE{`75&PyCP&%95$ zPOIF@sSyJ0K)E@t-KXk>kkS5)LMaT~x`YiyjqKPu0H?QM{q=SA$2Nj0cwfopdoc`{ zqn3g8BAZ6T$hq3)R9l_r?H_ zZY_I~I7>Jz;+!Qp$C>Fi4g72!`+?f5KI#lS`*VNf6v#!yycF|s{bYugHNmM3u(Ftr zu>|(dYiu`uxtt~43;{b3A`e1=@99xf^s}&(YuDqnOHtQSjPD{IjB!36_~bmJ9&vP; z`MVCC>9hjZo^(>eP6l+?%X}5s$tCs^)OmcIWs#1clSL`xwi9+A0~S#nBW^Nj=@Adc z9`0cUx7r>VOJa8iBw6DYrp`A;_{Appsj2KhHx4Y0Z1jS-pt)|tZidJp+jqsOJ?H|< zY3V1(L3D5u|h1+v99ifNkiw&M@{#Q)mu4*VD|-AmMxZjnFcc;JkZ z-{w@LaqW!4NiE5L!P5%mkI4ei=JjWvtHGb}$=P^68`I_o-Z+bt0ldNewg3uMlVz=C z^hx_zVP!*cczEbqC{BQSQ&v@6ZcbEaSbS!nDL6A{OR0HRL26K@v14T|GYUMSlbf=) zk66+c1O`R88|L{Iri{cC%=0q1HJ0Vpj|(6ON5aK{qnjJEXuL|Iaqy7ij>zT@cw`g2 zbs0a)(#7+x^BBtIpX)q#d48NlpL=3rV&X+aNZ;l)6Er_XNHGpt2HYtq-=jRO1Zk8v z$zJk@!~w&&{E;^6pv$bMrN_WknMaCjb{@n9xoJFzWxrD+ek*uz$Np6Z*poUJxFi@z z??GF0D+_5aa_TAlWvIF&QN8!5dT-(-ud$YLwiwdkFaKP&70NHm7sgDEulfWLZOf+#=py}fLj82e{9pwlRD6hJL_1fS% zJxXAauX6>@mZygcAeqe*UmMl}S2vt<$*9zcF=3~J6%)VQsGXHET}w#&J2?_S(-&cek^@sWI(c=}U4$Ixnu6{5y!u z_dD!AlK(glEUXb+@ygl%fbDC`{zqEe<@-SB``m3y@tD|&?!)*op6at`Cn^u<^i`H{ zy7O_+oje_(Q811bpv|1u&|^=IwOEp-%Ul-KD&Z(g1xUdsK0kKBC;BdM89&RGJHbQz z7Vt={SLe6O^W&_=uK5Id?n+P*XF=KGG|$G%hUAbK0dl{+;& z23_ztEq%=Q9o3Tra}7@MEaE6>GwdR+&D?`fh()&A?eJm8)(zGU=Vf?zUdcDpxtc@E zZAawp!L{b)#UTTSDuhk~S1GzGsOT^KL2woL=J8cxv{fd#SWe4Pv=d9ppJt9|zE)Y@ zwoT2Oi?V%tkFDsxs-9M7@2eHM*OghqZX6iOSydhbAgV`6ic)ZDQ(fCU36?b9C0Z#P z)%PIAAo7PIpdQRV*neXE`V;%b*PgMuy0M<_Lv?kBd_`4-__|Y+Rfw-dalQtZVCYM( zIYdlJj8oi9jC)J4`>=zx=Q&KK3qz&*IWt0cuO|I7Ln*&ZQC4kbPW=F~n7EJYlr@-+ zCf)MaHeaTZWC~%}6Wi6NW+cuuv~icj`O9g*vuA=fbEH3V=}bYj(-Au@=}cLIUFHFg zfBN@4?C*ikJnJUr^K==fteY60OYrj9j=vOUBL21*L})a}J52 zxCMs_wW+_-uqQ02W|hXNX7x=jQ32BLdCjVTnsk%EMq*2*dk^uBrP_OEwD&+&9`zLK z397nEBIIE9Z%^F$fpPY zI(6s1FS7q}MWM#D1vzOx-dnwWtE}$0 zSvM_5L8{BP)+G0GE{X4RS}LsDE}?)F81QjOXe%UC&=Svc3}Amb(Q=kJkc6?>;o;|? z^@VXEv9setlo$8}Y8E}o)a6`Li*bMY`!qY(z~+Mvy3Cy9+6y&SPI&S%jVU_S#oM1z zdRV4Q3}9wKl3&sz19liG;w+>oKthyBALrRz8mDY7#x>1M*<7K0Hgg3?GDuyk@#t$g9+39?2v<(HL(v2b1*}pV4-F zW6jF+IFbi2r-dPnoV%QA;M zuD!NnIPb?g=h3DGn058mAa#Ma?{L zVn4+x_3E4?jhj>=dB|)1<6AEM@!GXNzI4mu>sex9XPQ_seQDwHtD410)_hc#^UL^I z8Av_}Wt_Q9&1oXoGFjH;vxjVLhuEz`7xmt;y$s~gv4{GR&z<|HSR!}m8p3}%opTx2 zIiG3ry4)epe2)q__jfrBcWLr(a^vHgod3|i5B5#|J3nt6e{Y-ocfMbKNU{Nm@P>8j zPc<2K?BN${|5M&Wx1C6Hjl8&D$yP(z>#CW3aqj1@zrJ*|wsut1uWfyO_R#fcmP#PrQPSQoCVu3lHT zwLCAcyxdlf34kg2z<=kI`%MvP(Wpi_-`ZmkvE0UFa z4Y19v*jlizre+<5E;#%Fd_Dx%xg9qH66|TUo_%fyy@5}C0%XkUXW>&Q$@Tj<*9Qvx zF4zPl_ofTjh-~lOtm(clW-y*vxcX@Pj5Z=xOoPX4!A|o>O?eh1&JnYCcwx4spx%a@R^FP*@-=y8^=?gGmAPDAzjeqfXaPB)alT?Sm(7{h z)Ps66uYH~77fc6(bDR$tVt;Tr5a)v}tY!6dIKsgf+9fkWf`gG#B{M_vkY1=Z4K*lx zAgp46JMC3L6TVkzZ^I{WhH;#QQv6Ab^Yek8Ctm^M(0&2Zt@vH1iU24&54=C>+1f^(+C)F zho4Q7t4QK@2!+BNGAoP?Ju_lwEvq=rlD=d6+%CgknDex)H8RHLmNhq*so$~{>fO39 ze_K+d?I*UQFgv@DG;2I^1yr?`Mp+nsI+OKovKPziCFp>`XtbL*@$rYiW*w_4lv*M`~qUNlr?NSCIeeo~kw0!gaL? z`B8=03CUhY-}J@Bwd)JYwuAN@3ff;xaqKUB+<@DceB6X+6V3_946+B(_W*qrGe~c6 zx`8iBOx5Lq{m60#Ll0j~!G{|f6zaTytL@#*QahdCxODk>={Dk_n*VLWPjAv1Pv8KqxyyJs>N-KAq< z91(1%=b^PioSugU(YUT_ka$F@ou40P-duz5af9M^X+05HebZO1LOUF7M z8ts3e@Y~$nWjm`DHDB#J*Ux8LaG)_OF*w7SoY z&8|wRAGAOQ;eja zi5zSD=uQ0{c3nRk6|X5`ew;p(X+<^0-K=_T9@W98;!Y)* z!%yoF>YSfqeTn$W*UxfUXPhbEGpZi(d;_NvV$2-dpeUX()Gn`?*>y0(A*)S-d=J$LR$ z;EMyAkn_N!nKzT+@zaf(X~=EAoi0{Dq)*TRp4WXEMBu66h&E1D9kLq!s%yq zf%sMF{&P>mF5~kVj(~nqs8U0mX?hwu-!X>YX4mT`?JSyvOH*uWYApUSMf+i+ZaPkb zTSEd>vP4EE=-+=tN6-}5D7G=J9L)q9Rh3)_E*k=0)A4@_Ol+~RDFwX{QRVa}I^WOf z9Dr^B)CHg(wxzRVjI$)J8Js0!COoFXOG%#Tz=Js0_XHwv+MWnx(9+uYV~Bm~GV*6+ zN(F3RD~~}N>#*~2E+gQ(2D{@ulR^7CPO%i=Af%h#55k>hc=FS8BaU)S(J_{+411h4 zZYpB(@4B0Cp2#dJ-p6|+)b_#*h^FRNG)IQbH~A&*b?4pmu?7VZiQDHUcwzkBcF@0RKKJW)(-(n{H5Vx;j^Wno3IP zRAYFO^385klNA@`fs!K+=7Gnl&c?);AFDAwUi&Xt1;)nYT?dM{-PJ#Iq>^Q`=hXVbz8uTIm7T3i=Xr1T^1t%v z$Wuoe+pnXw32RP8zBZ`!B4A+B91TbO-K-H~4jVki2CqUVM9%Z+yq56 z1^TO@nkHkwbM1t?TQN9Al^HPsn#C^emATO4jm%eqO00oN0iMA=fgxVMeBc|9=D$~- z;iOqoYVq(GHn3EXgAJ;;!I9n1_NyiadXuHx$@$qV_{nlPA105xYno1s6I}dxXt)QR zo+r4(JQ7P6N#kddHa~|K{BVYt&i_eJFQj9}8FKkC94oQ}?@Va}C%K

0|7X=zr<~PCmt+Nww*i5cDpb_KG<*jtqV@}`AJKP z`ThAh!l)OCd>ZqEx41QDzssKBd{^CZ8J!^IZ*abLYkU=QqK%7c8t1w6=!DCh_WSRS z$?Z4YC$HcxiJdLRNfN}$ARMP%0(@`appT=fSmnNV!o;A<_-W}%?lr+uk?GDGzrjot zRh%$iBiGuV$#X`ZeZOCQ$^j|sc0fDyrBy?~>ttZeiO-EYqK_O?YS3xwa?f%u10 zrJkEn`kd1ax!&v@~zCt zi?ScN-p~Z6D27S3QSeIR=w#*ou6`B6s8#sMr}o>gVwC2^+MbHv|A@S3L_XxWjFDMV zEkEsJLpir?FHsNKJULR%Av*G%g}KQ%V)*yFQS~KuPBpM|sDPW-(ZSAibl^Eil>DJw ziRVRkER)q_yCe?;;=|zC#<0r%}k!Y3T~-`*wN;+3_5gY#NZ3KOhmSrxD7Cx&TK~RT!pj~UoRgQF zoHuuk*M{z88!ilbL~=$(a(i#D4nMfy&ZSe4a-rS+;?Qoq^PcfSo%ddZVmk~4SL~$R z_Ag^UfpW7fuMQ5bUT%SMV__`po!Yv(?nCtrhn966Zm2)J%t^J`It>Dq$K6bd(J&M3;YN(`p5B%ShO&L^GH48!lU=4m(gd?fQ84V!8)x;;j^fdraj%HQ)$_z4K;18wMDBBTq@7D2aC=daim)H<#C`}wtvc!vtj@Q;DBK?85o9zv zu7c}jZ-N~tP<(!PC~rr0{=S-?E1LExZhtKuZb(dM7y-a#Wt&H;TUx711``s8{^5X? zRcxKt>31MLzcehQtk9I!k)O9Dtur?@JtxDGjX)|tQPCZ-649@xFrS)*f}x zGc?q5m8WO2(@vji#`rcJUfOxMaoH_>-6{D?(o#DLN_#Tn6Z%3;LB3qP{-pkXMiWQl zhT*5&#<`m7{x@aZ!4Z22n3H}GRK#~?Y&LK-5K}G`}?kFO7ARa==YhgY*d zvW#VgwjDavJ_jUJ+k@;6st+MRdjC2S?4TI^9-Y>C)q_Ank(Y<4w5!^FMQZD!h1K!R zWgw+|KR0T-0yL72xvGCTar3VE-s^nkRo6chH=O(=3s^c@S9@@2*Jy3sXs5L!J-x%4 zLtnwqM0jo602x*4l&@88SRQl zPL7C7O7b1};jUdj91vdz>kjqwP*p#D{Vo4{WJEIO-k=NjPD{7IBQ;EX14;WFl17l3 zbl9Bw(mI0%DtX}I5y>LMu4KPjd_W`4rFC_eZp&#)PjAby^wI&@`{XF3k)^2!X!zQR z0s;-EcjVY<(+znX0qxjR9pt^D_ECNsdVst>ht?z|p!u$L4In@5*?ra0{u?_wZtP!r zRk!Cs-?a^e-IBn(E+5iHodc`uq(a3i1r&_f$u}Aq9I?yfbX(C z&S`L=we{Mh^TM!Cv)*Tbg&h+%d)x$KnA~O*@O-I7komYBPLqL;>+nv&!`*Odp94Fx zOvP;Dy#ExN_!Ig=4#FoyObStAbRMS0kS4UpH7g-(9#gM=!TjSZRht`IaLwzM@%!1` z?Wojyi>0euS0ad4tv(0dz=~Dcak~xsia9nFmAQzpf}J%^U7Kk4p^N}WpZx2@qLA>+ z(CoCJ`Ccx}ItWH~uDY?K_1eC&t;t>sg9qe~{JjQzebSPns;!9*_7PJr>*_q*kd#u9 zSQ6dNB_wm=0dc+3!Ie&h+WWSsJQQkR&hzf7~Ylr41EBnH|s^!k$ zMGKP-n-{Hc*8Wqx@pmhl*zIbR`ti(X6?9cBXnG2|YJteCIq51R&S8_*PS#m3P~Jtc z#09a@wRd#3A6r$kKRYy{cW3RP^;cCk-MqbgWp?(;@-q6G%O3Ns+_SiOXL)*oTz&E8 z%)pj=A6(XYS?gdzLsnLOVnV$+t095C+tSe@JV(P>O*d(l!K99#lX`Ze%7JTnGQNzH ziltpOy?k*%*Z!8a{dINw+eF-=-C_}O3yY;aoqdPS(!fCH({wrqV1}p$nqKl__Q%$x z;Y~B2w6v%0Rm{i_^8C2r*T{`#Vt1QR0YoyOkDsM@IE_yvxs0DeRPjf`eo_`r*5PDu za@FZ86R)(c=EN#DA`eA!DK1oQr-45xlHo*y^meugL{M9x!RtnscH(dw?OJ-U)>VT) znLHfFZa$~V!w4pE7{3OLXLQ(MJ$P{Ifuar0IE_pHFP zRup);k9f^X>?r?VxsaVs-*GMSxrLB7Wxowpx; zv(q^8Wz+Thys#cpADP_!Q&R*GVS@BRD(7KX7+v7p=antNn%*E3Ry`eZhWR zrlc><$y<_?--g4>x(+ogxovoLTy9HpT6rnx$bhB>x4Gf*YbCH^iZtuvVHU}g<^+d^k-Yio)`2c%Er%3D1Q(!tNW3sV-K86bL^o(J>?aNW(yv$b)^ACXo{;Wx~9HJVkcUDOWX zpeXKK@e}oPfx3vN3}BQ2DaX3 zWkuuaS2B|pT96Ub^w+I}8I`fnTOR(Ni*k%6p5>AQD;k?%u~U8V{eX;W)wn0Ddr`bh zW~NT7<{$)rtk>Syf4J7|fMQfvobG) zeXqPHd#O3vlDH@^Dxhb6Qh=I_ReSB{$&>JIkj#qR3BJJL55B*WeW46MN=^wPMfYxoDsdq%FL*h@|0eae9IW z_7DNf%A94ucf<^=kFjJXN=01%hlI{Du%~O>#tik zczv7O#h&YXm#;A`YaC$Zi)>%YIsJc2&Fe2NUt3(Zxu&AEzBIldx^to&+LEt>6Mi=R zW3iH6<9neM*;?(4pK0m4sqHy-@*?2*1A!-U(N?$f_K!Vi`*IO0A86<{ty$i?-t%HZ z`}KosudgpXG*l6UO8T6E@`=vqg80(<){2_VWyNdDi~I9Z|JF~Ej9M}|6gz1?tXqj8 zSnX8&kBBw73&-nfMy=bH@okF6{;K?NakcGzS=*Ps-V@Tm0Ay3l^gGDrDeZi`S)$J; zi329TfD{HI)`T3Ay<4Vp_B+nLeR#7>@?M;qW!AX4jrQI(+_=B6XtZqWZuP6?eYLgw zn)6pLwuYxI*%?{0D?NQzO@3Q)a@&;y{u=^+sIJ;@Lw!Qx`V7_oqsJkg7+;?HHfAB)z@F%PC7no~u(%D<; zD7!?>NXw})FUYT2wjj_aJS8F`CEO=)f!NKU7q>VCMDJ1lNHPdILLvnnp@#ksl+-IX z~~|ANzfRc)l!7Z{11%~7Nt{<0r2!R_{=9H zZ!L=t9kR|W@p5G9;)uxd_{IvSE#l^OY06~5Z|&)66M`>~`61jke{sD!ihYupJd|PD zGBV)5!sqde_CWV2*Z^w*JKg0Y%JKjk>=rGf%Om|%_!|`SZB+cqgvjQO?#}u9=O4eh z`(RCGar?r6QRbdi7*@F`IU=jDwra~*W?zMQX-a-TQsmd9+u)nZ+r|9oZiPKGbH&pB zQU68l#hEpzSUQd+I=efXBNJA}M=^KxKVL^C1>~nJHS?OKs@lS=h~!0;VW?SR@O&kC zJ#6%_;*yovWO_3wiXSCC1VW>GUA#ejH9KQr3*`fBO?RT z^6RR%jAe9}CKY9cT7#2f9h!sNuY&WZsY@h3qq)9AHk!uyzEwg1i^v_KDlrb{?}hd* zGzyR5IB7waKqWjZVtQRzyK_m}BvqK*@$g0{!-q6wxE4%KK%;cD#5cEe#>RCvXRT~# zYi~%XfyJ3XkZWfvAb%T>%M$jbd@rI33f(K>_m#+spaX&@P^g1a)#xZ&-Lko0w7tdJ zo|-nde!C(gFjQH(t3!R2#V+f~>?#Q9+E;<@qTkqO^WdK*Lskgg;F>b0mn^AL%an=b z7dmdk)E1ihH!P^DSDzY?S524*QtZM%0dlJUIRXm^IW@lKyBq_O=4scnYIjJc;xeKY zw@r`Je|fDNBJKW8Yr7l1JV~?^D}P~!2yr`U+&=!1HjdA**unE`9L_WP(-7bP+VgMD zc#TV6!rKOCa5@RjATq+Sf@KXws27uo)GhCEMe#sjCie}GM3;sJWGu|hw)3hMoFm*= zp8D6pZQh=PXy9J@m7Qs&y8xE97Ui+hxIUl=zxMf1I`?K;+bOpu#AF&6oKkAGFNEMs z2MdRUQ;Bpcu%0S6ZZ5e$!1jQPV5*-wGJT^{B{a;Q*|UAYC2G)#t5gZJk5_XRA+K^B z>;k?g+^8iVnJ!e#Co&O;1IpKpj;i-GZ_FQUZOUm$Nt;u*^?vz7+Y8ioE>Hcii|+}v zp~EWh)1(i1OyQsS$sPGs(q51q=~X!&xap@s0Zl^fcj<|FYrdcv0 z;^QMCGi~O`3(QR|ijybJ{~YGuG38?^e`TU@z zq$(8o5t4BRB-14$T1bX#ZI%boilykHF{i7#X4xl1s%@HRTtO7#F!r!NQ4c;ovw#tu z_;~LTz{)7vEKktk9x8tYz5-U>*O=z_WeKNf&fAtQU8zX`Jop0XW6T$bTG%+8(Z*@I z8w+He?)qJBGMaL7(1Zv5dGcz@WlUP8UDKYg`1r+ZO^?$P67%G0T1h^DJK2f!4}eie zaCRI!cRuJScB>x0xA>jN%8bbi3$9J7?@N($)NAt7{O51*4m5Y=vHcX448jCfz)P6H zPogUBDc}XMX!Uf96Hsh-2MeAIhdl8I@eI9^iEgGDnk7dQF~=&~&?Y$|tg_WV z7)_JwIboHFchN7|0yt@hB;X*VbdZQd@y6u5q{d?km#&Bj84+?U zdqEoPx@@9ndR-Pp67vLV%V()^P+Lx}G5W6K9m}*Dr%KZ}s{yxNyPGV9uvn(#fD z)@reKWGqZIFIXR2oEB?iO$`bL(b{+hKVmq&#c#ne}iTDLyaAwQx;&TQH8Pbppo-tblF zoRP`91#kHLIA`R?Vc+qpaVuRT!+9294TyO)o(fQx3~ zJ`M`=Pr%Y3cCjLiPSkLi4-T?8TQ4*vL5_PM&JdnP=7hRCgKpBR+?wVV8*DN%y&NYx zY(+VgD2m)u2t`gNz6DMXaQ;(Gm=s`&Gvc#kl>iuExCp^xom73eGmJeJW_z2tU-GZ0 z@-@+{;41aA0CQqOO;mh6`%mk!HrqD#57k?lUtML}D=$k-3JK~94F&cP0=AMvA5LtlIez;m&rQ?UAldqR_8e~V}c9k$SvMGc|T2dpW>+0+oa zePzuJFi4}4o6Orsa?(QsgQCz3+9%sI7-9B7Gw6mgYyIy8Wk54FO4)LaGQXCdqii|! zMiTS}Nr#}=sfp`&b}Co>w{*%r?ru2i%Q=Es?%^^co-Wh6Q;L(YiAaod?t%8l;PWo%m`GcyRB)iUIZ_aY3PMn4ubeq}C@nz-)vk7B|IvikdQ# zG#Wk1+8ai!qYbs@hQzeFRU6nu!&;|b<(zT#CU)_wlfAadzeBoll%jid1h7w>;IhhK zJ=w>JDq?(`s+(ziA`9K4E1kw)1}&*Nj=D+0W|gd^>!*#Z)(;slB@AxPWAe6wpKm(s&EQE65g!V zn>tT*{7(B1oR=TcXf-3HIG0^r@tII2zVJE8i;!_5lFnh+<&b@7StUpQrxb69JArZh zmE@I6pZpN`PSO`SGrx3SdKzfDb^l=oVN0juol8uT{2-T@bS`11q(gjfC*ec@73>h7 zWr$M=$&(-C`%yeUE+Nt&B+q3Yj0?#FH6PS4AwOaU$)mftT)tds8cJ683>bU}GhCjy z1-1sk?@#x#rtwm-o{PWi#~yX{Q(qp9cGR{^byR)F)kmqEWi^$vutDM_s&RiHC;(nj zXZm+JPsio=@Lk4U>){X)?`Fhz8eE<6jAp;E#{~p+2VJOpS{gk^@eLtY$WU>)ayNW% z3725dL`ZO)D+I20;G6tKuYbOOW-*f-Ac}nV zyWqp@I%QunbbDv#?L*>g`HJl973I8o4GCeo!6q(y3}$o|7ItQcuS5mPVZY0FQ8B0Z zn+#bH>=r(bwH02M%>4y7yVWT&ag&SbdgKDzD=cOPFp!=5=PV$>O*mOV@}9PDH3+p@ zU)Q|8Jy4Wu zO#6FW1G-Y+!ZL9_+$joi9$5h14*W=W3rNmr-SY{Ov+^@IQCw?MoI6a@y@KhtjLr~5 zJ$=LHr=)m$ju;DbQ!=rSr|#gn21SXA@JsMbNXsah6SuYuwc^w6=d*l2w`%*DpNil> z7QqSl#rU}NPdg>?JcmEqrK)Cj7?5bq4vYB?I$sbIk~W>{XDJ?}L4Afk=TeiWvvg00 zk8>Sumol;l1dra)dIAV(iRSwy)dfeMMwzU~Tq8_6a#Qpw3z%FoG}G1z(@KVP$ls-$ ziv}Y}x_w+h)es*y@tuPmgzi)1z36-Xt=aE+dw=Yi^xzGSf}xoJFV_l&KwGMKJ&B#@ zt{j3~(b-f$I}@=l%7{Bv2*rqGriXhpesWZlKvPd{Q_cQvEveF)>MX9WW1az?Lw9IL zSpJZ@Eqi5|eG@SMcxp(EPXtRdnTkWg7;Y_!L#=*O12fotxg z>h34qNit0;!w;WY3NNbg8%KOuXU$rdn;N_bzYZctr{?dma=U(NQamDV5)~#(o7}q3 zc6uLM?0jriQnR?qivFCOe!1Ot?L6mmvoEWsxR^d>lfDYc!J2%>uhe!tPH_8fpgkgX z!w1p_f{<@>WUsa%!>8&~&H%LqUaS#R4Zn4oFEVPU`Z)JYfsJr`Ui&HuD)Vg~)=as;%G3pKMr!&{q z<5n8(HIZ*Rr1_?4oM&R3d{Z$#&Vs=^lD8hEp8MWG^obeNFVSlM(D5K#5#v>J`u!4I zv=t>(Z0G3c93gPrM0`?Qb{IO|*?D{jF`Z?$K|woLstFU4))nxGn_+Zk?Yy*MF7YTq z&0|iaIxywJpY60GQU$usc<+zZ*i&BjaoQ4WoPVH+oqu$@;{O5v5I@{>eg=|l=1Uxb z^yv4AMWmfyP`wZgzDnV_pS4_AkXS_rtGR$IVSN*1>2-%3)2DFJr~C$-zs&!2te@q~ zoGJHlP^ZmSe>*0-M%{SsyG>_-cjq6!YfA1bl2rPJv!v* z>|DJRl8eX;ps1geq9HP`h`fhjhC(9B7|v?2&FY3uoW?&Q;QyaDbk4ktYq08Ftp7YW zbVRg;{T2ARiK+)4WfS>s%BL$xe{AX*rO{` z;mFn^D~O;FW;lBY7DPu8S~p;A4k2Gz>)$;M&J+$ICuq90Z`Ays>1gl?IX-qekPjp1 zFs^xNNr<&NV4^6h8}Ft><7c@QLeGgix>`H;P;-CXu}7juLqpef1SZWFOlQ)# zeP6Jir;a!Fxw&!1J2)B5>4^xC7GHAZq}8Rkema_ZxtPb(Q8wFQ7o!GckYfev;^cF8 z3%!mY@q?+!fU6?SrHUJ#Z4%KgS7`j$QuU$KQACL+2{rTTV3$snC!!-+gV$hgL=Vw% zlUG`soVuesK5NAo!?TW{HFZaquPGhvUvsQg8FROE=C-z38#i?ir_}CW(sf0f+nB+$ zxV(CCM%gt>S0y}>*O6*oUKE)bYDx%O=;1jhvnC9e>E;@~jp6v6p%t;>2iymja2 z$EDL;r|@x}1?3U}Nx(Rlh+kthWa!|fg(KevnKrw0HQ;ro&h=Gv(ZH#zL6Bcn>rF#L z2MRZC@VoPdiqXEpjRhrZ(t4LBjVv!pZ(6*(xpyu5vg<%?PIqq8s=0xiSCtRu2L`PR z398PIFOKam$;>TX(W<%=3;=}gO`u9)YBhEzPw}D>8|mK3RoHmf4SUj0evLT?Stv;?ShCqj}3fNlNaE>&S!yjRSBDmbI=#F zg<>}P#09Q9Of$_H#-3NJe5RSWqv!o;rUw5xq>34wJIeR$ubCIW1?z@dI$)rUqn3`} z6m!EJSjQ+u1S@z(zzuTYJbqUCDQ1%~`w@Ay4mo*|%1n^U3SG*0XDaA)6GN__3cMZm zhwQMwiU`AP_VLH;xWAaDJ7!`>_~;C?dboHY z^x4Fd1uMpF%*)|-grOM5S;qFrKGE)WcOz<&uyN|)&W)Y(6#FdYS@Su_9C(74$bTaj z=yG1;+Ax=KrZ^7jJ)D*7ZMENVKtICS2#Ql5oF|aY^QRrcJ!hA?J^tA|MwZpQ0hN1= z*2O8Qb85Ff9W@%|Yp>kPSXMwEAnQxHyU}Mz2k|-i>2o`mNCSf-VWr*24ao zaW$&ySgx;_oJK1~k4EEje9zeoGp&mv5&Oy%@u!t>Gj$iOtHHx@j6Bb}nkhLAwyLU~ zNi!`kO8@K-khT^OoZ_+UBJ5V8vKXb^x|iQrcZO^sBBnFbL^Cy~gY9!Y)II}Cs1%Od zn_@MJvkTZ>UE$!?uQk{ZXJ5gigwkK}x$DGBs|)0>N`vT8@21i8*PN!Yh*YD7dq#db z)vHB0{j8hr)8Q1aH>+p#LriMw2JMiYkpTxxQPi>*_BqJO&J%N?#9z;55dGaW@Zxgk3m*T`1;g2&liWoKsUQ#gGZ z*sq}A`FSM9F3V(*h<$uUs}aG@`uLyarO5H}eXk_(SQ9-@z3MSe)+ek=A!%7o z?>YW1>Xi&6Q~Z6>MZ7=aOcE6^eLwqY(A?wF(q0WY57KhKy|4YXB3B^bp*js$e9lP7 zCEM|hob-3(4zxWF&FZ8D-@#`OIkZtU-#PI!?;6F$9hwHUoi5(cOstf*Ic^toiQ)dE z*#D=uIQIWO!MsGepHgsd_$guG>7FJunZdGYdMp+#Td(AjJ(t6CdYVYEX%VQIIYht#REm;qD zrfh3WO>aW%sjD6pJjc1 zB|>Yj$;>u^=e!ZvC;@(`c68~nPMzWt(q10@!=aPr*AM-E>}Q!j8?9h}x4p*xtGb~q z(1-mUYxB3cG{3~Lev>`V_rT=wQw*^=`}1_xi05Br2vXR`$v+X}GsxOs2*$*^z29-jpNpRu1o0aRq;a~vwkc4zix@IW8;7kRi@yTYM-bN8X2 zn}2oa!?71Ke?I!-jGO!tauXLM`+0@WTNuvnQTrT(0dWqe<}C1>yUN`&%#Iz&sle`i z1{R^R`#<2^Q9=6{`g{WVs4(h0g^L01_>h7L>BbY*m#&8NF{B_>jtfG{e|M*As zmksI<|Mf4?8D2ihGvu#v&cB)mT8de@??$5}i7(>2EP28{{25l`_g7KbtKERHLa2RZ zEADH)p4A!?UzMC#8Z>c*`r(4IrXcm)HUE4vBzX0L;J7q7%XUvz(ZW~gBcy;6pN(_= zb-v1*=z~X`bRCCZy2mj}e5a?~4X<)XIE{YGtK$ZxWfAI)V6eJ4N@NlA#vmAJ!;STd5%ABy%>z6@k zdGQNUd^{ooBI5kUjb+XLM5b3X(y^bW<%W9AS!M8!h*yiUib*B}E_8|t`km1{az4(r zv7prHokTxDspUiwPO10t86$LR;B%(rzk*U!_e>Q_u#r)cJ%zLL<&SMKZ`;9jW-q&` zL-m@5#SX4fW>3KK0nPwW3SVB9|19Ll;dFUG!4%6La3N(Wt1Vg=Bo?=ztT~t&uKB0@ zXKfkV8*(2>lIWKVhz-;pb(nP6#n zkydb?OOH0W%=x}%ygTOR=K7TRA;T?>RY^yQRmaJPrl+C#0l)J&kDL5V2l9@-5|LPiZ$y=vI^ChRK@@RDc%1 zT|0&wYk-O>dG)(?Nr)OaR|bA5{TP3x9;3YDF6c(RX$!Cers*s1Hmgpx>}v1iL{pr& zd{WlZz)Z7hPCIX2uqoINo#^WfbFwm%^4J9%({hdj?z^yS11K^u9n8=Xj@p3fv%dGG zWSHYjI=ofM=j}V;?dKmC>@UDfNl7U4oUsQ5sEVkv@*0m&1wuB#_o;Cz*h13}9;>Pt z&DqpxDqa|xtM;>B7gE?{8umNg(wUBY*SOx)0R=!A`7@0I(67_3%P*BqSqf$Q(=n)1+!H`l zPx%{3(dr8I$5Hulxjn(b-Fb2OQKR?f=ihhwhPnc!Ht(q_-(KIap#VL9Ix-E_c|~tv#~1y1ux&H9V{}f{p~fLMc|5N1y+r`6XfvQP(N%03%GtGFmUHth}hT z?V`%6i(08&b5%>r)dK8-;cB4J+f-WC*jQH9NRTH?Y?H%S6+zCcm$mE;c10B0`_eTj zd*5p|k09UHP`|N&-Jm{_=~m@{oK+Q!R96ibXrMO~)wBTgNIDxAOT!yT&MSv9WLY(C)(Ag6f1hdEH;E+z?Z`AUJzbZsu{J6NhsWiB% z-)!t%;XQZxTrYEux|--J*4V{ z&iHn@^V_Gqylf}rEsnS0onh^#6vBYR4|oYgnm3y0C!a>72=P>V<+~j_eqlU%cNp;N z8wBZ0`H=~8@|Vf)+^>GlOy3bG#*`#$IAS(rVQDtpkDQX{M1!H==slskS&M(7zo{fH z#4V|*z$Zd}=PrCUNb<2^H$!xFPGre&5x~TLVcv2O=1Rsd_I#o)Zd!i`+CWDV($<%@#It+SD`FGtGGY?L76t|x zJmywsSJ%c@rG_SlhRin_=DI~$;J19j7NAeVJ0z9#>8#F9lU$l``R?!Sy(ankz2Dt) zdD4|TSQp!?RR%crm zcDV)Jd%v4h5gwTr9g{xq`Aa_vw3hvB?5U$c0bPOq5z+Vry|g5=@g8=c+DqqK#>CC6 zjaN1Cdwpb!(f=gL8FWRJ{_Gg5 z{RDrB2bXY);;YI}*My0GNv|P$>Ta<7l~y&ec)1eyMyPHG>PhtfzmTSV!!J14Z|8yq z9X=JSi%R-)bNfq*`zzFEqYB~*dX2{3g82L>mU&-p!95>+{81&8>_}zxNIv>-aaCK~ z98&_?#bV`RtjzTwbGD2o>%q*HJFb`B>bACO{A2O?BUMmS`J@Q*dm78i8yd>X z8-O`tBg#Hh{ZdwrXq1oN=@W>dZSeu)S}j;qfI@mPKr!hbczz%2>)26Ly0b;iEhvz$ zwY{8$)CpT()`ZU*e~g{TTT@xFCYN2s`Zrdnzf^B4FU!p>Bl{P#oA_#olVPJUv{YS^U&>rwFF5D|7 zDEy83H!%mO=Q}Z|ypQR;NTVA4%tqdn1Ij)i3Txsw4N=^@hNku1*(|3;Jh#`DoFv~f zR%rWco|_@II?I}xJm)s}+p_xgZMP{$Y*q5X!X)eZj7xsdm!9?9(V{Ve78uGWIfm7M z5}(3EzvI(9>2tB**#Jw65oCmB0daL}2X@qHfV?MWRH76si{p1)5kd0sGb=2)_bWs6m;WDeZywO*arBRCp9hc) z27@_dY%U>$xGy0Dk`RXgao;yKUAw!z^0#@LP>H%^?}PMS7%;@oMDHdmXgIoz~K z^EPeTCOz{_+thE;G)a?K55Ldu^E^W0Fu3m@KWtsk^X$&-?Ci|!?Ck6slHj;GmVAE7 zac!65+N;Rtu@mIocHt=5-7X~U_2YW!IgE~5{J5>?9lHMhEzaCZ7d5XG083R7TNP$` zp;*Ekm(V^#mPkr+!CuTx9Q7jK@SQx)GUj0k!eV}v#)X3yz)!gT5_kbJ(6ufa zraoQY_2>ilI{BAaAJFpotcP!5GJ^eP=a%6&i^hg&1dM&AKIeA=V@&a8a2b<)qKWYi zcQ+X9lk6o+()#KmEh`hW_|{188Hrcc`g-99$y&+U_=q2qX(_P zE)n#ZFQftp5$ME+Oz!`i<**}q(sJhg_sQ~UROn~`CvyKnIm9HhF()gM>F#ZuG!qkS z_G00Oq^fCeQPEzUBIhVH+szi_uHR@!VtJ8EvB%&7G_{U$kYW;9ixp* za|}egK@Ad%=DeD8$u$dmEW$(NjD;8_h`$vfa8H=W9R(H&lh%qc?0FQmf&(yyo%Kv3 zt#(l!Zo~S!W|CE0S63`NN6N&;2z$~ULiUoS?gQPeecU6)nS-9El=GzHECkVQ7&T(o zOAT^G1jlJYp>Yo%HWOn}U0o3%mp1J!F5cU8Q9YT#R$Pb92Uw8<7@}04#a$z>ha>{PBU&M>j8<^r_f}-0-D1o~CMh zxzFUX&5sI6zP3GD|un$F$%4GdsW9|Y3XD~<@WMrfcDtP!521eeBsYRIDl3iZ!k7vTKnVqPf6SoV#Hb6s!+(q(T~4`GElo6Gdcg7z4(3HW2#m}34s0+}k#a-D&ELCQ zNVH6XXYW|s6~1bHl=|9}4{{fVERKBc$Ce??zZ$3h<#8XVM`aY-li=EG^duH~79(X4 zWRi7K6jCgNR~&d@Xy}Cl2Pn-h{?BACA*8pR9>db!cGzw|EOnbmH9whY-YeW)->}PO z+tq+`r76c~@J7%7WORd-gTOn{c_$iJ;`vZCp#yhJc#?>sm)t#R79K0EsVTle`0!Su zC0F%u*}XPfUk+s0tL7q;sW?^mKIx+MFQ597{}lCyQb<~ZJZGZjheSEH-8j%mJuW<{ zA-bV`us8om5hLNfXHO=yek+l2S)Y%uJ-@ItEUY_9y?<!5#=Oy5 zbT*u2I6(@wn{;|L|LGn_?4EO6<$;sJpRUsVTd?wR?M%qF#n;((}8W2!QJ5Z_AiSedJ zW3ZAX<3y~f(qIhGK?*AaRKc7owP{~*@xG?i)IC4!@BiVRy3{>S_xC@&2PgIHFD~BS zn3_h&1R=_ege%-uT{B+D5q#~sbJOr$ojf6Y=Uu~_?(AY03&(4!x8eQ|4*POzm+jlP z%yEY=+qaHAUhM2J@f13E0Py>Lel;XTs#8)Jtvo%+ie(;z$AWo;#94~WdD!Ymt{?q= z)6?gQJ52F`f%!f5qgNAfdrr{*N@>- zF8hi+hY^dDpIb)73M_AaAyz?G07v;~ROTa}fXm#sn7I(w8H`3pDa=QYI^GyP9Yb!7 z5`4Jy35(E{?a9~J4fS5VpPxGQImeNA9G{YpEwG9-r|(X-e4MIf@IUqz{M>fPBBt!MW$7CgP*Npg)r&(^CQa|!4dSEr$JkAx- ztbbHu&Ya^&!r4XhXfVT}!;w_AAFJ}%zhrGqWwX6%WK*%NwxG0q$$rnW$&QBIw$%02 zTlW`F`lYp7)AjL*n&|d~$k@1)l<gLRo5Ter$k8agn~3y5<>oyw3X8Z9Y{3L#2*>ibt7WnBE#c9@Kl+TcfcJ8^&bbXt#p6XsFORgs7 z!h9H*I8WH67Cq*YfyEO#GI*U3McNc}z%6#wLc{*#~Vj&>YyV5|sB$bZn)K8`QArNr?iL@}fE zJt#|g2y2$i?}_3BGm7FvhsYlf9TEn}TS5kdjz9}iM{_>YaY1*Od$HY)Vt?I%THIz; z+#P5{3LW-<>1#GSK4-Bd(oR8?-N0s1v@UusmKQt8ilbIE*C%1b@S$#j|4dB$gKKr% z%Pn<$1u;S?EUqnQp`M?ydc-wo8W)M*@C3ZF7YxjHExiRl7ComjUXi1K5$j1eBtiGn z4Q~9jFs2)CrBn7GjA?j}_EO6Q9MT~kT(&GDuyJ2?{oaz2z4g`m8p*@S)tNOrqoQ`! zWL75&(JPwv$~X5m33W*|`q~|8^^RJ7P10SYHswSr84*sU98VPahQuYB zLnkL5KCt_$<2Phwt;xv)b30H4`VjJzN^RI;bXEipyK^?ioeeoK8sfNRBADz(aI3H) z?DM-->@$o|1Cer(N2FL*oXgND5S_>A1Bxf&$Uw+M6xkXFP7stj9r;>(2UrHx<1k7} zb1_$_(p)Su?&N65#c))!JC41f7O%L^AI8bo_>3dpi0_~QTEN|gx|}mHT(~`*U*oi%{}{ z#gLL}SexErGPR~r`hxpNM4UXG!mvh zTXtz$n>DvR&2F;wo4#SQws1gy)D_yyZSpRv(?}<6y-ionhI3`p`WqS>Z@62y72r($ zHj_QAJ=fZnR+??=Gl}58q4TwLjQ;5?qAdF&q{AoZU*t@MOufitV>+@ivZtnVG~%f* zz_>Kauos~#zj1erxXogjg=QI8g>E# zL{3)LN^W`0?!&gCBT*Tez}g1nx1N{?R3DV89%|uR>~G#4 zT-O2#K;ty;!|J+&3 z|G(R)+-bBl0+C`z(bCJeQJdRO)Oze)vc9f%ObIrp6`^R6J#Jzx)H-MDuvx^xVL(oA?2wp| z<$Jo-+y~_Y#)CbrjlY4o2$7oyhG!fRMi<^k#{Rr}JnJApX zg7Hfw;0ijUmFX?|a^#dd((DV)g8tTH6%*m%yPHY|Ek%3kX&i^CEyQQ7U(xPbk|q3X zmA%EZ!B)H>r`K*a+w;tM6_8QBknqa@A(-U=mF*kWM$xLs$_fUU|4^jWsE7xn;1qzD8YF`u#I4xA+x| zm9N_!qz~yVNGXkvFHNzwhWRgvtTUI@V&%nC={4x3gexa3JRmGQPc#{^^#dR@=?#NTg(+#4-^nT;Rtv)|4`4miG1Tg zIYJry3N~C*Vcv2pcQL#7Xid{qy(7`N9@JxwGsy6p&Pb^L?qA#&(1&z=9y(Xz0qW9qXJ&8&o#Byd#%`;xzimu> z9~m1P!RryO(D6JDz23R=@%8H;-?{S#z5QoTox1tvQ>V_7j$Vi_N;U3fKw-JM={j#9 z%aY5}&=Kki#cl*8Non~url4O-Y$yL35D{WZjVVyamU3%q9slP2&E<}5b8x~(=f)-##dbDyUtupC zuPDH}|7w!mTUaq(XuqPnp)2?S!-h^ez|1XylBmo-_8(LK=k!NY9L8pGajRd3^TCPoTXG@mx5-SET*S zh$$9RWOIg0v5=yicJkhI%#*K7u~|!|L$iW|<_OJ-%Ay#APmPo3$U}5DfRV+Og6%ONp+KBD z{0-r2^u;f~oRk@z84^++YirbfkQKInTgUx|L_gnd?|{Ub%xA#|G|sFU5rmR7x>URn zlSY<~0|kZqJ396k6zuP)X{fKQX>9xs`JrGZ%c{TA*?DDk^~trJ->L2%85|fH85kTP zUTc7F3?k$Hj(9HWNpp#Vk0GTs!Z7)X@S`F`0Ih5yF)Q1fgx{@frxGj!I=OO`5~)%D%J?rZ7{ zyEc3s02{J|dZqb*HLx_pV(tX|F#!vg!5?&m&8>Z`?SnZ7RLu zinWbvd%8z`IA!sc0)2-y6E?a>e1pX{V8)q8B^4#PG3l#%a?=bJJ67cgW@ids$=0zL zRS=;;n@PC+lUV|y=dN)0lNT6yo)V%{hq}mTC=(6rKR_8OKk&4XS32iXQUVmGieQRY zsoBrP2ufpuPE|8%@43R>c=O=knKthue&b#FYcmsDY{r(nV61{|)CeyVja6scT3Rt~ zvu-Rj7in!N$u{k(xDdeAgEPEAHCX2aT62cC!ntzfvYaXuV^hjIlIqqWCT1$M6ZBKLCL7TQPUY8rwXp7HX zZ#EB?SB@8~e`M>{N7hri(MdF-e<(L0ds9s8)fO$W|e z;7lXi^$c$;4d~F(_yBf5P8IA$Kb&?Z#xuUt)<9oVZA;GG^W@#v-}!@=pM2t#C+@iJ zo+t0wv48&#`YK+hA061RRlknAIJ~Z{n-5;v+c!8iHrUta9n5#Pts8c9=US{Llhu+- z+E!+04LLamZ3ct~QKSMdrASLU#%B0rWV6&b<2Y|6lsq}sdS%NpO3RK>nU;69A#LaUvO3Km=H90E12%0z0#bZP#N1RyiTUu@)`xpRqok?l_%C1?2V%@Q z?KkRc7VR`w${;2bw1Uk9@^K-Do*Xk6#-2QM=ttXga<>0y&&b}rBlKmfsIV24m-DOh z$8YWIymdTZ{&Z9yIdE`t^5B6Zm2F*JZS)0+Bf}4(W1gfl^KlwHmfH=Q0F5%(7%&TB z9@#f(d;H8CfbOcQBKKrfCkc;InI=OBqjsuREv?+5f4!7?aaEr!-v+Dsnb5hv3m>-@ z)M=ADit~GNJlgp@t0gJUOWq*L zbipS`K^5Cfn7k7wjV4&6HCR=riNHcFI@4Y4Kr^aizK_T~c~tMrA^4QKH4$&l+Q`NGx~Zk03~^ z=sEBWglVK9!RlbWL2uB^>ZG%;ys~Ck_zf|xX=|GUy6*?;gdbRjhs)gPf?3)ux+q-; zjWd>#h_(9@xrkHVesHOkFV~ z+MZ)Ddv_=e?fKPPt-0g%CF>0X+jD9HBAU{z#=_aS3TIPvo=ZJ5Ym>FBV)AtPW7SpL z3zKRx?5$IZ%uIEnUXuuMH_iD$G+JgBMG?^r$*G=&)6(?)9Jz@;82>D zdr|OEvR=(Hh6K@-AAhjE{=vhtG>0TFq7q%kK>lp0Kf7`G zOnv>C;f-hOn`R4k7Yd7*E{G?5`2jRw25WY~m&DC_Yut;E+a@oKQdbo@pfy%!32jxb zy-jf`SeRzWGUZGm;IcxZgrK=JZmmBw7KCS8ta*I9M@~Mp3sg=ICwNo}g;NB|n%HpT}5SvUM*>=7Wgn!)@la_rG0rpwehU|3&bEi_uwn<4^g z7HC=F_NwZ!d|h75Ds6IYCUl-IrPZEMlT@is)aVn{nVB>C3Kr}_ePw{L0=>sy5_Pf^ zYn{&sm&UG(ycAYt6KdekaiwyeqC(NKFdm{@mS-TMrAOx`)Vg+(7@Ef@`@r%2k~kk3 z-FB@2lO9jb&$#J+}KRZ||=T-c?t3*Fk(EK{GV$v^C>x^A?pWzRWpi za>Hu@tO8xM>fD$Vi0X76_^qyblyiZcyAVCF_j+C(IyWX+^C{2yaGOtSy5b4-zhJ*D zUSrOUIEh(=uAHY4H47<8H$F_D{+9*XGebkpgYsGz-8c_wt!_#YaSPMq0q&~>uvl0l z&VyLc0=jS>WNYSCg0AB@Us1Uj%onZy=4pd^5t?tlcFd4WbErJGo}hlfOUk7{N#D(l z>K;kc&4W#=tl|_NF1>HYoUdUaxS~9~JpDE|j>LaK%{CAEHJ72%+{W1=_{8&a^w~TO z^H_u?o4aY}X0XW|3QX*=4CpU0CL8`Wx|$Gxmv$aq<;__fFVh!^n5CXt9JMVE%%13p zqs!L`#R$@vEDLIi|3ldcyl>ODT5b@SWrl_HqI-P#kQqOSj54z=7z_j<*zqAu#O#C zm$)dA%dna;bmo$e3cRDU8sRMF8-9(E#{GM98b;g19#We-F<+5lZ^AV zv&0>>!kKdmUu37(oYpv-`^k7IE{zrMA`|Dqq)wKQb0Xksj*I?X5i+dtg&@x_Md!g} zY<`(KH*WI@S;-SEVhVfw(wNf7CTV`S2oKiW-C46JF+6807GaOO^f{(*^ANvsQ7UV$rY*uA$G#}UZhVY&|An)q9o=+WbMtMR#P{Lx z@nO9DIv?J->!Hrhhj!t8^VP?WT}@wLH@Yss{SNUE-uS`xL8}9l2(bD1--sp*{DOoa zhUDPp&75E2U?!fXTZDcGED+ZlI2}9U7GuK}$J4ylo>|v-vdpw+{bx%!t)qh&t2z_f z*X6D;Ai}0@cX9qkn|b{@(^b07y)UNOZE?93apf5l9~%4rJ|HYA!e%;K4WKHsl?IF)6OvWF4vrF-G+rt#3S7+HiH}&U5W;ceCi4SPKF|y7uSA6eh;yMkf`=>hgPY zOv4pbbb{=Y=m96fvPmg6_x`_z5+ zJ$3Jm$8R}zc6elDn7;CwlY2Wmdy`w_=o;_7+0iu?`X@%$WM^e%OVKqHufK@YyhL;j zHY2+4sH7Gy+1hkeq-N8`Ov^~ExhW~GCdWEd8_cNLaHzE5nzg&`2Q_JQjgy!KNiM|P zRE`x35OeI29!wjYUK4-pTZjWkx%g@VGWCmb8k5vn#;I(Odh_LTx`hdjSU>4JxK79KR9a?vKFJO!Zsc@n!Q!_f`N z1r0`59$2l2nvLxY1JMnk^=@XN8(?>HaN0ZTPuEdr^vn#3%w_($fsJ3#=yOA5eq+yV z1f(lwtQv?TfC(}fBnvM$y5X3=D5K6D&xMUSS2`kCZw4Lba;9G8wAkEWO0|i#(fuC`qZvZa zo7>{JhHP?^c4(u!?^0};oz)aOXUpaiY~Ea*<6)v-P@79h1@Fu5R0wJA#Nzu<&!dfDE(j*SK?Af6= zSL=9$#4SShph1`8ixjEd>_>?Shie4V6pTaxQo=OJEMn7A+9na9pz}x#*7P!Xk3oFGo+foJmJ_N1W70 zQBI-h2Fz*9NfC9_@8*xlVP?(Erov^5Qa55WEJC8XVRu>9+yZ_0U%1gE(q%77+PWgX z7;BEB=Z|1>&x_dYBYAOXlrUXMBMJS6=f8Is85XW1FA5`MmpQgMTDZ0eJ`99Dl0T6* zssCrTk84`6B_l|;u&6y*Tb6Z&8R?Iz&m|YeWMpQTJV{zoNqlT!LY&nYS*2|%><&#y zT^$h{M>nZpFXC4?j^|C9+agd7@RURMsAFd4+>?fhQCE>(IWau2am|{FrkK%fl_OSe z$g3>ZoFA9d)D#`-pJK7vJV|UtyWNy-LZZU(^hir>QgwQAY-se#2)q9(-<9Dym_6_p z;q3w_BGJRhi~GjNoxS=`SCD;zi6}r2E4h&f{~J?nBhlDXZXrqJUh381zf^dUBwWeH zm49+y;mTJEA9f{%XpnD#Ucnhy?1~!C{6ALe>#46v5?}|wJLHEeJaC}f!tdE24E9qO z8fpYRX#Qh`j{kD%OFXUP1)Kta3~2mU95!y@i9|f1mY(=RdIBWH6JK+Xba(>(bB}T0 zpyU6e;G`!;r6-QECo-pQA>SjN3NQXk;TDug!0!*q2K@fYL4QY2UCVi}-~VC1{c!(X z_Wai$(firtW^yOr0hnI`!p*p%$CZ!yUWzAZL$5@Vt2x51{AcPLc4al5`6Da!_0;Fc z4n`Dal0ag}^^_8H2ekQberuJqNBebITo{k<&DD8SZ%0 z;7+Uu_8B8joFC^&OHBQ9>I(A3)SqYxB}xQxOC0}+!2V34uo5fMINvA3v;yXR8`}?KcT*VofYB(zK;;w$yu>JxICYlLOu@bsd$xp1xAZ}9y*5;smsdDcQ(etn z*!1@B(A%2_-X0l!hn5LNnJB3YTM|}kSO}!I z4qYDa9UW;lM@D;d6nRfeLrZ9a0xV!rO--0jM>AN;Bru{jS&)Pg9qv z1IW5}EtYqM3*3eE=8G3C>-h#e8HOiQ#3zxvn4U+2Tv}mdg>ZpB%w4$n;Cc)1L34to zPQ663_>kE>b>7)ie8`NR3PTxHXToq;HIE~{4e5)z{d(S`(uW?kw2le_RxYKR86gM(vLs*Jey89%9XHImEbySxK&yD0 zNdsV`_yxm85jY$0FBSkq#~W%^e2AKn@h{M*!nl?VLHO0NZ>ClWobey{19S2l;1&~1 zQPr*t$%t>KxTPwZIWrdq+@RlpTb$woJ~_;`n>h=vd(ja#`$Xz$yc7b;gi|6nuf!RiVGVw&y(;0Kc*9*k;a~KH+VHI6arP|W@tTZ7E9^ak$;g9 z`5Wa7D{%Q6s|))Z#r18eE{d76W~K5*sh+(JO7WcQz*R;vAXg>$J)V{rtxinR1ZTvr zrJOkRAMPg5Ce{O<9*o^I1=t1smPTG-9BD=ye$RRu)T4dv3a#vD@R}4&N{R-MU>AM) zNn&(#B7ISev!?ox(YyovMCp#?s2KMcAkc%r_6?VkXF2cbf}COnFdE027WLRD0^M_| z$rdO}r4|e&?n((E!%!I++fiH7yJY{;yRJNWt0ku{JaP-qnIsg6S8736d_Zbgc41lm z*rDwM8LbhC{+iWMK0(4oC+eJH!Zk9CCUVLN(tovLSL}esJ(V!W9vq8C*FiU z?iGi5C4FOy%rw(QSm)K96a)XMzmn_urPN|$c68h(;p0vG(u==yzZVD-dmmYeoAe$Z z10Ko;5B>>~mhq5&1`oaK!b7esgmSB}F1i=xF24t_Ua$v0UQ7?7WvRrZ_$B#(_7`>R zxabHssY6U|VuuJHk>05rxFA{%a>A|Li=lBURa_{0C%y7F`Wqh76UfacW*dUfoo;sq zXG*6I!+9re_LZ{L(*%iuM3_h{j0(4E%IsMqt?_vgVWz0kBI6jjdvK5pvC|-}CBo{m z%~pK(^|h9ezv4R`*`}$7_&7cvy+ZXl&06ffygch@26C*gk9>b=&-ePbp6l*Dx3&L! zJxlj{=Wno8ZM9mrR@wRsy!rf#o5}9BD=NCrjf~#kU2&`pnb&caUHvXwN|S{`HBUXj z{e(LXsG518I(IO$L^Y&(%^EW1-Fm#a``Y^YYrC6{w|VdN-B^{|k&)4nTe;DXJMPes z^x`2)#nBdI6*Ui)~%(4+(AQdsm6cTwH86l^{iCdPin8cR=@$ zOX-QIqLbG+X~?QdIy!(yWC}Sk_}vbEV#&_Fp5DmHqTXXiYOY?t{@O;5L(2Z1&hGFM z%V7T=W9@Z?eSQVoD(#JhnGs15N^ig64qIi<|k= z*qN9J6K%la`8hdcCeT~7;keb>+!h?b)gj%Tf=Hq1(%{U^UbfUHSaOps5W0tWIryCfv z8dKXJPaM#a8!rkpb14QfboZycn)?Rdlyam^FvUyCqY&)Qx6<*PR?hDDA$@fWJ-@$T zJUMx@sq9Nk7Ey>Aa1qWUjB;T|)w;sYlCLuv1s?ehSZLJ52LRBL{Aw3UMsE`8O zD@=BKGuaP_1VHJrOZfV8@{zELd?_p`ncU1}I?j`yjF4ML>`Z$9vx~}p%Kyb|lq=!w zfOSAeI*k(2`tkBgfY{5^#H9NKjr2qza;mS-M#hBikYbYBda%CXJ2*#f=<24S+t>R{ zc-sbwYPJ{Hb~n`Qvh#l1I@#cVsoPMzr!y<7|5SVXss5~riN>s&q{6)|E&Gcso6G!# z+t4P&EAf3S-X)Wsut0f_X4qrOSoAwNx3J^X2k~qQ;3S_7!;%a?-*;Wj>io_1J#FRd zyVvzDIpo=RZBNhD)e-p{%GVavsE2y`wt7!^wx1O^-hA;D-i$QB1@XDbQOg3Cn^O!$ zWyYcbQ_ebTLT+NDPoPgshT2e4Vl5dmk(I^8Hx(CC+U1}FO5F4&=oW~eESg`9YS0|+ zK1a(JlapRIlU=v{`ZVdekv!ThJPafHdYo~118Jo_h+0^ND9SgmrYY^|KNWLTpsNT>YmXH`$b-capM4iW^cUNg`S6F;}SaJjTMSDfZ z#LE7m;L?=vwA8H9^uqPV?DaN7Zeg}ICpE>Oj<*|tNdeGVf%b?QiVRHZSI0ldjm@-u`*c2U#ckGl6$g~~m$j#cFX<2i?w!5`)cVjE@Z{1&% zM^wV!@{0Di3ZD|MmUT$xUc0VE_y*L1n&N-tLVtP^4b>X-d=k90NIGZ4%QG06w(z3o z(t&5`*wFLI-}%mWE`BjOI?DaU@exNHPjiK7`)(KPseA7x-)m{PyM_MY{ajVm-BndW zgBSNOY@LU_*eQXKj&z^NC}7i5l34yI+94XmmWi92#SsW$LoPP%vDx-C3V$O0_J#(# z@JI9H8s5%bux_a+-(uZRU1+PaIILiaKUdHkqI7*52eL`^vTD@wOj@p;3Ndbp@R50v z3{{O-ts_-}hWskWW^;VX*}1DM4jjRTb8QNHv%8B-<|4+QlfBv5R_6T&?|!eM;yh~ybOwuA!)O+HdJ+7bLHT(hUcPqi#|v{Zc#X$lV&G}f$4>hlV~Xw+ zBMp<2!W-PD4uAH_KvPP4iFYTS%)_AmiL;b5Pq)lZ@P9F0Qc0gP&olM#c<$qmFUH=> zzjE=J!942nJ`u@q6?|CibwR>KkJQw}i336L|`{V8H#J9cOv$pmj_AdM{_dHnZ zQAZ7ikd1qeN|p%V9|lak5CQ8)bLR)DRWuBV9;V&@#Ba&}&3W+x zn+rxL%DHN8H=~_`p5nyhsYkEyB$rmsr8$1_5%R!eTu0#Cp=x9&g}qN@Rs_98uImL4 zS1Gn~QCyK&gOR8R4u@1$k&|{|+-}Fm)8u4{FwWje&``9t84x$K88qg-t|5f3b7JIjl4=|wen-LojuW1(k)ZE0LrX}uh>?i-Ar3l7QH&??AfO9aGJcE$}FgTK7^8uL} zd~5iv!E;e~DjZK$iBAzvJf<~hxhf%=e1OW*$ke&0bM#pZ7u1)9`i4cGHKH~x9;c%* zlB#hLgNfcdV(#dDVOaR0yPK8?N0}TjdJHJGgL?*W1`OqbNN~Yj zl#2qHIjGM-@<_~{V>Qvrh^m&vvhsxH>Z+E6^0LI{YBCY+@2_4@-%xfH&blop--64* zXEZK1fP9?e_?k<5gLvom3O}b@mI$ABAx47at-|Cmko^3CxQDj>w)=Nv2hkxD*AC%0 zj0Bvl^KXyaF&m^dG|g^+sVxl>lyxVS&#o5qrS9?Qf3m!Ev(>t}v}}{rx~VL`ygVN- z?!!4Q?w|`sB+uAeP_W);TwhSoYiuaX&nt&?EE9U>yv*H$BO1{r;OzAi7o6!XWMta7 z6mFNgCt6CPtuU*-m(?b&YmSCGhAzLQ4VP#?9i0kp0s_QSft4k7fD!p)mtr9jI!NVT z?=M_WGDwD9_In6E1gbp2N5gf* zdW-2uIhQBwAP=VE*Ade#50K+|e813ul4eCDAHy9HOG35=E7eOm9SJ68@+{yKksp(I z;XMY2oLFAxPc{la6MkkVD_iOEA02fExeo3HsXSb`I^u~_G7X-}%?y`@aoWJBN&bxN1FlZE#WOY327lJNbQ94=gF@G zw3)<}3!ji6hWkNomkb-im3n!29SocRF0PINL$k?op@Dk=KWV##9cVXs5M?vb?r^!? z1gGX1j5-y8WkmRJq3Ha14+f-Mc#oVI@vmF{tsUtCg^%rop1qz0YLCm**3yY5(Q2Sk zwMI_E;1H{z>mv^WS}LQy@L~aZkha+aR!t=T4d7D0a3DRM38!cU6C=8r0rvzRSUWT5 z(GlF+jO^tk-qG#ECh5&L|Iz=C^XH>VSQprZpAo$iw`^cKGJ;;Bnlla5os>9g1%*UV zsm7qgF?3qJC!m1dN~5Pnt0E-5e);HUSo4gx3ZIq>pFTFayx#v)EAcNU{&~W$xF^p) z@kB5o!c^5~pDCX>e?AzWRl*e2SQfOWhtY8p7sli<7mnoH zQ5TO@Ac_~x^FWSrmtJl+kC;~pEpNWb%Xo3M!W$&()fwH58d6YK+jLzJyvT_?JyKFe z%p@E zeDmVf{FoE^9AlNBuY!0#?tL1kshyEoj@(suq$@MC>qwpS8I;p=u(b4GPmcTvzMn3S zmHe4C=rZV5emXU#CnQF2vudo%=`4zkE$YmXK4(E#7iaG@7&`57@+Xxq(=Z_+=Y=^x zu=$}ZTzkMmO_^Oe=ix+t0o(&+TFt`!5t&G{sweAZ^30^n%*>TUjV{omOJ>zh>K%d% zfO}u=UHMbERe}KKq_iiGf?p6-;G{fxwDI_QgJJ#gMkjW7vI9^4LVVH~AZ>%`kRA-; zUX^QS@ua7#2TMGgGpzy6!)4i(Vj*c9hEXf`Ew55 zqO!{k7szmGdhzl$9tkIv#08X^l)=e6?eWlRTNXklmNcfdlPnnn`Eya2(CL{mU_nfF zk}blE3sNPP>~BvK9^if;qac6IMr9UF-~*%Hmj(N0XL~%9-Zcx+1ReB1TD$OujDh?) z8xy*Ud4Vs0{=)yIJb4;=e21`uSI~EI9Q;3h9~SXarThe*`;vGQZ}~}_LjEPaf)Z3C zq6XAROYmBy%19cCM)qtoCN^Vy2T{ds=jFRJEj7Paeg!5Lz1;|Ypd!?8`rnD=wfZQt-E$_(5MtHV~OwT!W_>mqtB9|)p13shU|RbrAt>O+Ow0hqgBf^sY!-y5g*oBL8r8=*r~auw>OTKW*r;KrAm?LUS5ZFaBj!d?-l? z4GId2RQZSSNg3)CYc~0Z%HEieYS*t&sj73ejTIrQLYH}Jm##xYDEB=_Kp=gxsIGZY2f3GNV;@J8h2#$pJ0|K2TEWsrz1!cPlGj_?Jy!trA|W|9=0 zoHw^s)COQFT>!j= z*kn?v{FaA=}S~-o4e(Wwmx0vO2AXg2K$q!U#i9)z-YzhRkb! z`cyhWE`*kYB`vpZTz_p{fI6flD8j$*>D@iIZMAmX3GTu9#ypK9T1i)_31WpLoK;XO z)6PGY9_%1u521j(=PbbzmJGQcOekyY@m5WE3>SBw5T@4`?rCg0P~vmtPm{Vv?5+E3 zrX3yX`c_?;zGgU{i!l_2Bt~L{3(4fBr23@nhScnJzxvWxrZx9iays&T4T1GWjU^@@ z$yGTT55{4!4?Yj-9cAaH%QzsbMZn*QXMgGcvvy@=b&*?H#5uXS)UY-e=_s%sfY2IR zhhb_f&!K&>G|waDb1Ccs4UksbMX7})dAe;25g+Iyot{pEXpp|NP%kxEUY{q|4WF*! z4|}Av7IxHU<-cFv7T?(Hr-{n7eOR(4srdM|;nS5K(rrV*`-AHno0dnbao66FRD1<@ z(cG04m8H(qL}`O^gVL;t#$w-C-?-4&#Gw43#Ju#3`m~GNU7rcB&Ce>qBf3@EBX$QkRa4VdgE{hNaZ2+xyM0@8iu_4J z%x&qp!=+g2!byXf=0>}{$$}*{Pw22S-1DprG;Sk@?g)3z8>LAV4MuPsPt?0(bY?QA zUs#T0sAaORczbhNTJ!c|`4h=CY~vN>+ibRN#pUG>?HFAh8M%6NN4DL*t7b>*ndF8o zg@s${Q&Q^bXG3yM{;1tPlAk|fw~ywN`-R(%=7g2Y7h%^M|=B8vQ@Z>+zawtS$++9ra7nfnlkQp0)o+NYlY*~ z`!2sIzowCo$R^37Ju4#0)&KtU)Y7=P($rMy_(+Y7kB^N_NFX0YTjSz$qoZ@<+~2wf`p9 z!s?)BpwSxO2Gn6?K zVZ+aAcNG=wuA%R{YAKuIB}~#aYCYDd-> zZGpST%h3R8xy~}=xfT|Oq{+y0%t%vzKrQ}hD-W{~f?1GkO-2hv?6IQ`aeCwStUP0a z1A*500eMcaF_X1#R-JTag*wH@o@EblYIxe8rE5zGQ*ZI_AtK$&;~XCc2vOV?@-qJ> z=m*J3^h%8qF_DdyIuIV zd%dfZg4EF-9_60Ow4^wTpA5o@9zwdGf9Ec}7L)`Nu)PQ`67izK<3VOK?g2i=Z3zyw9SWR)%-G z8_8ajYT`a~BUZSGMHH}jgMSZnqJ1$Zom5i2Y4cDhSw^*B25l$=tYL!NUg0A!+Ns3S zO%wfJ?VEfDmt-dr^|HMg=EK=MxrkQ#(UvjRGoP8AyKSex%(vw$Qh5m#s=>n1V zgbU8D`0E%~8TGTEZaRJ4j*0Av?Xw|2bJM`UG~`=l$Q~acCjUXuIt}xP_xWJTwXRT) zXw%ZPv8k!tp?I}Ap1y#+0$4u6--<~0^9?k@{e!7LusYP>qHsPE?NCs8aL4X_2l&Vb z9zaCx*SK>ZN{HSl+@pGd-j72?cq9(dGspnuQXV7b?8Je6xJj|5cdGdFoDP&yOqBxW z`Kf;^p27+6-^ZOgp0?)~l#;;Z5OCpGiC;2Ys7yv}TD-2>wRN0i?;abYFdqO!GgqQ` z4O)m|jQjQ5Q@VH#o+tw8NJm5@^PWkZ|FF#Ak!&QoJwv1(A!d1;jZKfN-(a*V>Dq|= zl+LAJNr}{FzmrKlhOW}c zthu?Yys@#I4j^qp0ykKgon1&@d68k^k&)qHU_!(YpuhjXM=={_PT8=^vmkd>dU9Mq zNVrFbM}8i@>+s!^J5<)ZB4kCx>f$!~5r&>z&h00E=ifw6!t|c1L67}(>T|_2?9K@8 zJT2WRV58#D)ECY>w@%*y*LpIJi{j2;b>LIhf8eMnY*RV|RzjIz-1#m4EARzer3}gi z#jBvpN?L+{TjE5NA>c-o6aGX8ED96jO*f%DtC@dWD(~gV@AYF`(MMMBMYJ!Wv8cW2 zrn3%&MQPNLfzbg0nc+p1cCIgFX++?PKo5^Q!Ws;ZOn1LIk%+Rogp6Qv) zZBe|z+nIlg?$d%{1R)A(Xf(cB?5Rmih>1yDu}a6=_i8oaVOm{Sh(>D!4HAIsPjRxR z(&GZe;qKX2C`HI$74PCeXY^(utu|U?$VijsMf#9;+pG8`9NdzzzRLzzaoyRr>ZsNJ zx&SIgqNe)EFQ%SF?dYM4=W!)%Dns%9)T_919#k%goVe%hKEGQ8c{BCY6TgC7Z!1Bn2w8% zD3LOfc9MEmb8D4B6OZqV_pXw!b#MeI~G0~ip=o=gq=pVe) zllD>Kl$lVg7OrojDp*ffEva24h9iUQ&*y|-gGsJ`l&`OTb#A#mz{A%kboKHGo^SA2 zl9?E5MmtSYA&R?LJO7M!-hnGoQwih^cIAI?1+ouUbmV1r<=43KH0=%X3gr$@ah^Um zcl<_X4%D)0Du?`G>ery?`?MBTuUpSxZ}G(`73VQEO1YNqRq>#Ei%+#PepGx#?j#kA zpK&LOObUBRMI&%gOnLGpQ*VF{Qe-z_l9VF+!(H8)B0TI=t!lArAV>9;3isMV+gH}&qjq2 z$*Wc+N1#F!nfeSP>@|!qbPoOv3w5sU;ee(64g~R?~pAEUi!l4#6?kyA}UV)q|%Ai<PYd%oPzwnOk`@L&k9f2xVm&djfV2#R7N&KC^NFO4e~I-7+VK?R8vhc^4(M0h zsgmwM+dY2f&MR;Sgk^WWW_Mh)<(b2Wcag@)!@Fp4TE2u^u6R$LAA)|{GrG77A5z*y zq2}M9JSz>Lw023Yp)f2DDtYnDzdh1{7qyJ>kW#i(zuz!8uv2>6BmKmQ&C?~gQfCR& z>9nQa*gr6Me!4^$Er1yf#lmBJF;;A{M{efoq?jmb=Qns*4B~f{r(VYTulY6p!Tzg5 z3#$Bs@DVLSKpQxN$683cRcHg#0B#DvX?))mrU|z$k6MoXX@%t_!n1O}3OiTN~=f_*Y})H5NTL=Q#DI@+y=JlzbazqohPC+<*w;@(-l(@?_r2-4%>Ia?OET30VrwzO1M zH8)o!h<|*9)sSViW*Mvnt5$}tS{1sIGLd3Ap9;I@#rdtP<;s%$)tTvuD^y{8hti(6 zIx{V4MMyZ$XB5-~1P6de#Sb9&#&CtCiK`7D=h5ot-9sL=S3 zz})Eax&}U6zhvcdKVL7;YESRv)Wn>bGQw%09Ne|*aO=)phnb9kEW!EId^~v-a&oqX z@~V@(WAzPN`FNS0kXci~WE}B{3+v)?eRN2Q+FaOFUA)e(%O_qP6CDvAr1q&;*2ww! zrpG7eWF{{UQ2G0>@M5}HhY{-o$wYrpp>O1TgjWj&$M}~jE2&I#y^rw$sejYhV1e)| z+EZDH`^~UY&P>ts*A*&Iw3!saMuY^H=b2t~28ZAB{K<>jE-2?ZmqESqTnJCfcf9C~ z1;3-Am)?cm@$&d3p}*yshnENRJo_!$xL(YXg{+vKnK)&I+pL3?5oZiu9 z0z-9tp=@EU(nRCr_NfV+Hb0Cr?a}X--k)q)9bO*3(rC`N`fT^vy?1z@HnlLwZzuU< zd`56)kUB)0Xvk<8Zmo?kR;T(!t%NN!bua2ZE!7RrqSH?gTUk1XocZ@dLVmr|FQ_mj zcHi)}Lq6NR^YYDEAra+KE6E>Ue>ybMFI8QXP}|zSHp7sp4T%cQ49Nt2Y~&A$m!S#0 zJQArnq6i}&DxPB=Sw*0<{uwN-lRXy~A0L-i8XsTEeOac7iqw>8^5PTnG$@sVQa_hU z$tz+8ae9`LRu&gmmPX5RUuyCa;`2~0GK!XhwWhdR*1`droK2e$!}>26Ni@R`SIO?M z9T_<2lEy%>==QHBg$KD9$M3ip?tdJKy}jHCY%t)d2Z>+LN~o|#$m}Amk)8+(7oa+j zSF^c-7ty(FVqudWwJ|W1d?hU9tiEldhMdjy!ZNd&{Apk`u}f&wlWVLcb+y8CB+Fno zWecbD2C{tk?QoLWYP75|kV9nCx~AkNvv7Zb*_cz%nVXYnED)Zi`YIV~6a?us*1Clu^%Zr)pH*=TP)DeNIeD`>)SfW)9UU@?7>SWxA-XiS7p zV0tYE9qx^roEW{rFv__Mj9lHhi^k3Id#9A9=ES0~JTFUfck1aEkE`T9Su~cylw?sY z3cn0$LH*abNoMNeaocz~xV<6aCVcJWe7dV*alDUkd>2=eQn`2>H_2id6aU4~P*Au{ zpPkfvi*Y5EVi$|C3%-lT_6f;1wiryge0h>YB}xh;${+bj>_#YbUHg=xS(xWU-rCQC zT4wFISNZY0C}_xAvW(erO}ctzYtt%B(EEbp$jwVXfUYMbm5cyl6)(8rjyNa z>0Kw+?0J5e%Iwkm2K}$}F4D-?B2Sgp_+e(|9A-Z5ELHrdgBq^#@+2^SvHiH9bL@7oJ^H#F=n6yKrU z6Tz=NfEWvCY-O7+#v$qet%72P8_5ZbIX+$-I6Ig*@T0x!ZW=UluQ{$t^R(?~Y~EK~ zw7;cdbFtr%5eEjj^Jrbnr0dl|L*QwxLBfje((E!{_vSQ zWoOTvIdf*_oHJz?#pdoD-YIwW;E|ruS)Ics#V1aQXwxeo;q@MJE$dg$5oQYV2+2=! z%BL>X)12($j>li^*ls+|DWpGg7TUH_-V=4=xaDFiQR90)pOYrwiCMuJ+Z??zPZuVt}35kmTwmD zs@XlQHh}4uf91pA%qDGQ(1gIyXp4S|Dm$ll_xA3d-Z?tEb6ED6E^&Rjc1ZE^ zO6xGUs$wn;?;6+KCC$~jT~1=v(Q$SI?B{s44{O#Wyo0x2-_X!R{~>``VDi@ow56*b zT?3DK0>=Zuk>8nt8XEas8AFUBWzD7+`iWc&Yi^aauus zSJ6__68BZ452(GbqM2_C+*c9UuRZRoh@3xPuzuqWFbqz7-31Rt$`YHh%j$_8I_kb1 z6+va)m+$RiOFGXSp0=!yFw4x)FSt)!P)v4Gx^LIl68q1OlV*utvEk9Nny@8(lfAcx zB(x0}+`&cL)XTeh3v(+AO|*BrUOw7>A&uI!XyxwYYGz~6R^CmF-BN5VFmY4qB<~lJ z%azZInkz-u@CXdTbV|y5{o~&rI&?wI<2&@nFu^}IFeFhElF>D6u3o>~w#S?S{b%<` zd@nu4Be-`Pzxa?&gS0*#L%eaoLD`qZ)GX_c3fwp(W39AfXL;Y^i|O{ycnklg zohLuH7FjRJ0MDqCu<-wZCych}f98peFXRdI5$t!6se6|l!@dqT?21BXeKBzHt>%vbp_+iZcuUZSXy%+S@n$}u{4LvVzt&e1hOy-`AunEiD zgUhR4OfMt%cs#Wq=w-2+Z@OLR1!%rnXAcp_cP;i0$@nUImWhn~%z4j@lDDGN(6Bye=!e~rFVp5G zeZ+JP@&(kpYh0o{xjL9bT!s zq2>7~vD_!Oy42w32K-)1QfyFn$HG7NtelsW6W9#nRtHWm>=t@Sd6~+#^MA&o>KnT2 zABeJ9UVBqV^R?J>B$xYAql;YfOGxW-=^w(9{LdI*L(_kYgS^W3A_|=4d#7HA#0rHY zLX{rtFLSI1w`j;4W6b5_B6T*&1O_3^c$7m{pgi2n%w0V_+1khFgD4cFZm;PrTh#aW z>tg3>W20{x-n&_-zr9;G_Zo$&>QqdM4b8L}_N!mV0shdM%pI&+1UB+gw%KS~H}-4o zU}s&AYgjAi_6(1RQG1n&V*>?8^&Cqb%wTe@wE#VHcBAWkH31%8>IR>%-cfC8aC(-K zSI^$T!^y!x5W0yLzP{dJR?k=ZWFDRi3d(i}*+K-fN1Yw-&&KAUY>K%0k2uJ-h*AGs zt}G$BfQMgdZc@$~KDF2SMZEOcbM_Jb18*&% zSPJBCj((_G3TiBtGH*R)>yvlI)0otivZY|i!WiaQ}1FH5DWTcOJ-11tPenr_cl?$u_&z@_mo8)z(9{s^Q!6@?syhWUDvYdekttybP$^ z@rm=D*g~OLEvPsBt;&wJl%$!1)~3h2*1p5cu$)O%zxJBiwd>SgT_+^^`6W(p=`zvZ ze`1$*ac$bf=@YhPIgDwzwJLec+ugl=MrpjpdE;mh4hRMG?$^qrUoYLT9&zzKa6HAY z3zjd1>U-#d^0^S%771&vd_$Og-bA4fOt^|+(998M=d2m2BE4tMq$$0}hR%%d9TeZz z&$4?SJCg7o?h7?^o|sT|i8}Tlr0pNtr2o_i*+%q#WtsUOyl-*@?wf3Fyl-;0)zB`n zxNp+?AMTsfcL+1NZ}R{3{>I$g$NF6pCeXsFaz?8zQjePZ8@Kf9cSyOv5x<}H?0E?H zH+HXkf1^6@T~@eGv7XD8`8t}+Gpy$WS?J@hsQbuO{rasMss4t`x5tEs4b$s~p%45C zW9fj|J$lR@0F&tmO~3Z-`)Sl)b*3$1msvcxr^a4i%h2kYz?1fR)@SPDf~hptw8+Te z2E*{kh~a^O!&&^}rFN!e4$%$l*ipXtSpJpIA=y?B6uK+5v{r6KQ&u4GZ5MOLMmQ&> zu7)U!5$IVS{J^KVYRb12+CMtcd#0J$%-#+9&xnne@5PCaeQf|e<{xJ;432hkiXLn* z#QD>HQ|Z938s@lHy-klN&F-$&zWba3A^o&4Kq|lgKeBf>)=1j~V9LnlmK-0?Pu(X#Kfv^OD#GtoX`Q)2XE= zR$Bj!ecNd3e0gkt<>r%@LzJ%K78Di3FF@HKg2DE`!O`?o#LMGb!WY|($0uGEZ!Ejl z9Ag^{w3 z=v&ws@^5+;{qM%B)BkF$qFd^YR60)M{~KI?{8zYECDr08&j!F1XWnGGcKPoqEBtp< zHe#;%FKZyg*49NN9jeEPD_9`*J$qo^8un!z<)TedH)RI>6@WX}6iSohQZPVv&5v)dNDC^qUs( zWPHYwv_F=drK7559)vDt3pt=Elg|h@SN8|tnnaWcK@jV&wMsS6YDN!NMcGUajG^XL z4?9HpMz(A^R2M(OpC$~v8rt&xWvTbNgfws%ZPPd?xf8|la-1@UCOp&#Th@ey=fS3z zWd-=~)4*k|mvw&zctNHod!W#>v3!H-)6_JfT_YOME34hiJ~2VDZTu~|jiWaGY!07t zo9R|Vt$~{U?PY42Qm6JVZ1omw<5ww&hkS96J3bi&*T4%~8?bO}jvcHX{LXcLN4A%+ zNWS@c-8ZejS*PB7UFAu`?eE+b&hNf||55Ah!uhH*b{dn5ufr)jT6Mm6@ARakl%%Ai zxFfrfayzz6O&|QEqrS@bVU+VR1}fiK=ZMXpj*+b1<3F0wPy68F;E<{=(Tvh=3HOH3 zWiF{hsxn>Vdzy*#D)|69TW{QcfSAdjsKniHlYDp5mkcL4ms&_|r4T7f8Yvx;?wPeTYhyOvY?|3lvm&!!%>FhP%7v*D)= zPdB{O@Uf$%W0+&E<8;Rbj^8*@CmW|GPCiZsr!c3UP79q@IBj;?>vY2DTqDayj*W&i z8r5h*qputN(5RqMWn)d__Kjm2FKqlnla@`|G#S!lMU(YSb~QQPrZ(9Zjz_YtU?1vqjBTHap+!M)O9^-J9dce)H+g7c@WG{I3=rT6AkMpv8z5`%f~L3F2OF5 zF1=l5xGZv6TU)nOx3O-oxgBx4=T6;i z+*`Q&xOa3P;hy8Z-hG$*SMDY5zqvndW!cKHm20amttPfw*=j?p-L1ZDb+uJVtKV8Z z_OSHu_2}=B>M_=1na5g>LmppwRC@gB$vka6n|g+NcK4j(xy*B|=XTE`ua;gtyi&c^ zd+qW%;`NQ!6|Z8iU%eiAclA#29_BsPd#d+b?`7VF-amOi^0Dw~=+nw)w9iDJGd>qu zJG6GjX^^R{=eGW}^`kbiZ3ed4;Ope;=IiI1={w1HmhY*yPHp?OO=-KO?X|Y0er|q# zejWX~`6c>I^!vi^u74Zw&2b;d)2cjzJvnO(}_;!I^}mN>-2kP*4d$Ri_SisJ9eJYc~R$;oi}uT5EdGi7B((y zTG%IHpM{+cyA)O!?hxK1d|>$O@HfL(hhK`Yh-etmGh#r*I}sm6?2Py<;&jBNh{8zM zNZ-iN$S#rnB2yw)M{bV%HS$rEMO442l&GwzDN)Cx&PH92ZXVq#Iw$(o=#Qf-qVIK~ zUF^HKcF}fuv&;8g^1GCEb?-W^>#nZ%y6L;k>~^<%lkOhf+jbw?eN^`~-M4h#*ZoBI z+dUk5#PsObV|I_zJudYq?eTj|WX$@QT``AaPR3k_xf%0IPi@cGo)dbm@42hz;hraZ zUg*Vo+4gGM%d^*nUORdn>UA^LGS)FRI<`;j8?kT2z8`xr_EzjKv48jO-TTAdyLun# z{ZsFUeWX5)eLVV%=`*d*!aghdTvbVNXI~!cU2g ziGGRg5{D#aB#uv*vN8<6sGl`e`2K1fT_v^lw`WE){?3dDSNxv)o9s5W3&*{Ie z|MC8J2RIJU4~QQyY`_}>b`1DA$u`M1DK059X>!u+q(w=mlm1Mq8rXKAVPKbm@dGmk zzCUo|z;6d$86*zsJ}6~S_Mjz$-W{}V(6@sw4!SX@WYFJ(g~8T?8w_qcIAri^gXa$3 zHu%)wvLUuZS`X0V4jn&q+R)8IcMZKZtm&}s!)6TIFznN0+hnKY z!O8C?Z%N*ld^-8jaB+C&;XQ^=7=Cd0`QbN)KTc_q;*k=YGA3ni%F>jLDLYg4r<_jt zA>~GDx70qVIjKuiccflO{W;A(Ei^4PZByFiw3{QGM)Vxff5fm6qetY9_+-TGkq#r{ zM=l?^X5=^NR_PAuEz*0X_e~$2o}2zk`j+(nWCUcSX1teCk?~u`!%@0X-A2tH_138O zMtwW#{^+pLLq{(dy=(Nj(Kj+1XLibbCG$w;?J*6<_>D;#lQm}Tn36HoS)o}ovX*Dv z&i2Y4nY|+W*4Uu2nPU%*a~u~oZr!-*oP?bBa_;Aj$o(X*QQq9V)8iw?@0&m;be!@B$7ILJT_wd(|1j;cun(KcBq{(}V$3w#%(FIczW;DT=#Tv|}H(0O6&g*_H#E}XP**}@GA z_b)uV@an>nMRbwFBKJj|77blAd(rMipD!v{^w;9h#nTqgS^UxB}BeqnjR@<;F3zT^JRw09oA+x*>- zcL%&X@!fad-S_T=cYj;qxnjVI=_@v@IJ4rnm3AxrSH`ZKxANf1viF?c>+;^9_r|?< z;Jrty!dA^$_0Fn~SDjpSaaHB22diyXcUm34I(_vks~4|cy?X2F1FKK0zOnkxHFj%! z*7RAEwr290*=ycf^ZuIcYrbD||NYDFSH55Ufzt;&);h1vT)TMf;}3^^c;Lg*j~qT~ z`H}9Ut{-K5wDY4cKDzSJf7a1;mg{`hbzAq!y1nbpt}9t@x4!fGr1g30-&=oj{e=zU zhWZ;kH|REW+7P#4=!VPGr0|O}}rp+3d8rgQ?6$aV@!t}*C232}mUUac-*S7)-&>n)4c(fzb<@^w zxBkA3Y;)aa*cQ8O>b8a3R&U$9ZP&Jg+rHZN?Y4{CZfq;rc7I#-cJu9a+Z%0fx!r5K z|Mni+CvV@h{p0PYx0h_cyTf`%{Ep>23U=yu&fIzKW1o+wetdnG$F8hht9Jdk+je*B z-Mx0N-F@|wmY$GplzRUal_b=Ih{(#4UQ3rM& zs65#2;LL-c9U_M!4lO@a{He>Q<3Bz9Y4zcR!!r*bIQ-~HyCbw(tq+^*r}6@nc^Q z8|C>1mHyLZ;cLw|?U7VW63Q2-g zj`|kj)||)0LC7Q4tVoH=X$kDMFPS3O%a zK2PEK=D^=eeE-MXF2H)0dqx>z25x9DrY|KP!XCUUJ7XI6rEsKK15cwnhxTiNcpLa~ zo@3~9ZIrm2_RlUBw3hR@u3_sV;BRy~rR(-75H&%Q(Y;?^`!UMsF#`Gbi zyCA!bDAO4JxBjbm|5@%?aQ31fJefWo;~nn%5M)1@#3=q)Vj;C6w$F9LaEiW3^c+Dnh~-tUoCv|jZVnb#G5#h5IQ4RRQ(f2{=EulaxA zf}d9g`vE)!9N1yfK`g=dr@2GhM%6ek5-HdtzMA+bY4+9k*pI~Hxh|%{RvVV%&v2JW z2U?G4;iBN$0q2`=yWy_GorU`kTq)0keASl_t_S}nYb?K4eHU?xZ-I5DF~kw^t8kZj ze!x^<6|&aVe<{z0V*E!wwgb5A|PJ<}+g$zF-n=4f=Mb4~dVG_7Hu}7CgEK<#R`FKz=ZZKP!xRTWLquhDfmE+A6*r zHo6#-Fwz89VrG1XvMtzk(m{6kT6iNRoUg{2(U+gg;U^t`PkC3u`_FJj*rsqX-g?!f z8)2RX9 zih9A3yV&FX2zw*$DseZOgp|>GjY$a8TzbKxN`a#QRO`IWfCe%2iYw$mK)zzQcks?! zIA>xD+E|ODkS6!JOMrKYwBX|(#%H3H$9>%X6NP!eg7ALAA+%qI$-JMf-K zzfi_snV&JX$>Sx)iVpBQh`aFKo20$q0UZA#?y?T0$P{v$$nwuwq7^V_Gt6(e^RF%H zVXll(e`9=TP8zTX62W{)1a`tj2pfowjeyI+R{=WXvmd9xZxg{&0_r-A=;&p9n8p!h zw?f<$>^Rc+QTKn zy(E705Om81XwN|KRs-UVwrbD3!2balGc3?%Eis0SMOy@rHsXBDA-nK>5NpgKe#DLb zj(OyBHVMUhxRIs$Cz?h&vIV50D9>Xn5XLNTN=XnKjqenW#`rW1 z?@fch5$Co`z$8r!aJVON819AlZZD4UN)%zBI1DYwlQ@eL39!c zXR-_)isu_J{>jdjbY_c5QyB&!Ri&(X^?YFv`o}#Is`&fS46a4UXp9iF)lJ0?ben#Aq=rA_6kO=7{v_oIbBu8YL!9eLtZzCQi&CITmW^66qTY_<_Kic74yd%fo0nYu=hD%7Gh}C(v7ks%N z{eC~j#JS+dxx^2+XyF1d*0z6^WA(rz^$7MFKJk1k-iKqGV6z~Y58woUI1P7l*whWd zocDkX7lxs}5Ag0Y;&PgjjxSk*Ub0J&T@9Q$7)RHF2X~{*1Hq^7k8JR^Qp1m`1&%M%Xw49Is4C{BKc^>~7t zi*PFbX(wCTD!AD+sud#f4RgH5&*bz=HR}RY#`^ia59&QU? z#?=&GxojCH8J?Uj=fyon%6l&GjZQ8L;p#^`4W99@`Zt7R*x22#IAeWqLzV(ph8+#( z0%uASBc9EWF2gr<*n6SW%LTriPlkazGvr&hYG~M(LARq;Xs3?ERdIh{jHtvI@GG3` z7sE}*v%ky|m@qdVB|aR)NaO%j6fz*|@V+tTxF zV{$s|W6E>cE@rfm$hJ1wP5~WV)@>F26MClH=aqTcL^d#$6aE*&o`S6V$vU4b`^n=A z#ucNEAj|#FlFK=+p3i!w%>S~i4%u9m3FPsJLDnP`o0*HMaZje%wW`ubpPGFxr zw}r^=)1~sgcK8NRe?04<`mpcmC@uu8{aZ`eO~g`+DZOHQMUmsg)*ks>h=5-VC5qn=zjFBviCAw4a%vDUCmM{)A>>uE7Z-ZXr_1Pv*tM~h9-{Y{ z3kzT!Spw^aF9#1~$!rwMWZ5i-jc1eCt85lq$QHA=*mAaltznzk9(D*jJU+)))^4yO zK@@BRJE4K#C^QpX1Rp^s7=*z>vhbF$Tv#Ej61E7R2>XPC!eQZ**i>{E+ldk4Q1Q(c z(^?#EajeBj=T*+{J3n-3=+e~1*~QJp(?#Rb4m$_Nx#YP_cbVm~%w@I9hc4?~HewIK z4wp|{_PKoOa@5ty)y;L5>q^)4t{Yvqx;eNxx;1g@>NdnOar%IHvLF`5`YIF~%2E`H zjZ-K#jlIt1DimABDYgqYksQYNP`+kY*lmFbW}uior~%Tla)eWCsY0R)+^iAOKe zA8`@Ucl0#GEH(ZE^deUy430oL#iMa7xh(JpH|B1D+E=T=G(X5^_m66d?_g-cnzX7E4Ppik-lK zk!L>1eCm^6%*Vev37z7KdxZoEB3PB))fo6j_4o&%V# zn$I$yV?I|-H=k?1(D+=m5)EQ&vAx(;94&4a+lUQCNA!+CqE@UYYQzqrP7DxJ#9`ua zF-VlZxaNtuISBJ_IvGtSky&H~SxwfF-Q)oIn*2iU;Vx`})~C&B2O2?p$otvE#@MCS zL>wj7ms-%#^iAw-TTed$|6QhK7%8oo1NKMyFn^4fBUu(3$ELGIY?U$|o@U>&e9=kV zBQ_G#MQbroj1v2bUBm(6UU8??O#E07L{Bk8v=Mhnp5iCs2+>@$ketOGVu<7=nhB!l zCE`GpNX1~{gt6QT^S(Xie0PjN{jlFPh9riM`}8mRh;Cr*SuE?sBG_=&T^!1W zv8`-1PT{R%-(e&>2Mq<^%cf(XVa&m;m+xVQdLJ6X2axtxVO47r=|s1ZB>FY!O81gP zXg7nQ5p|>cNH)Dr#?WhI96oqD7Eq`=8-On!g^`=AANiS$ zAP=BD|Ha0Whb)hZ>@{k|=1@yEo0_xNX#=*D;*0>bXNze&c8D6-UfPnqPea&28o)lG zL2N&bVqenU?0edW4rJHq5O$Le!mPHCu7EsP27Ty5jQ-(dC`lzl$S5&_AG)QfGT-fR;+M;z!>Sdgbf z<6BB5&=QhG^U3>=0!!FnQp$#q5%dD|jbIW9?K6{JA;(w{amM#?ThS$?f+a&kn*!}^ zCW)uV$N>5knNR;FZ!$*Cvu@-TB$B_`1X9hWQg`+d)w4a+n!Q0CA>B-*rQ{VzlwXnt z&>g$e{p14cL4IOs_>MpfxyX9be(VynrW@H<%$I?59oYz5bS^C0&U_J~zs2We}`NAj2aq&AYTYKa}e zf3wHpcj7tmqIgX#5U+@5#gk&8cvZY0UJ{ESyVzp(wZt6kEBFf)#ieQU@g?coLwKYwu8_Ra*iwZ`nn0N1P@3&-h!9lhuyz=Aw*~=gbMA2PC{oP zLWmTiglM6wkboVq{e-?ke_;S5qd~$zVYo0-NEgC{a3KRbWRrzqLW+GWsv8vTX5N`E3#VXd4(E6BUdk}RZ;$r@NR)-osZ z5o#- zP3p-u(Dv*wbz>jWQ1&SeWcz3sJ4QRPqqG}4Nh8@8G@6~DUD#K&EBl)EWM^p%J455x zc{-RC(4p)W9ma0c-`O(ArI+a&u+B83uR@cZL9FR`VnZjw3Nwk=(aBhynLtEXaLwpg zVot{q3#_tBG@JCLUy%Ov1Q|}hBRyg9=}nJd_5CP`qo2X*a+oY-lq`nz<`U~guCU(Z zELIV}!}n;uXI;oS)|GtAqR2HCN3OF3a)Tw30@j!0vv}H?ZK1ww8}(y5sD^z^wQLvF zvE9_4?W76p0*z-s(7x;<9l)+a*7FcuL^sIEuHrs%zu1ob!hU7HvESKUv4KNLR_*NJPzgW{*+A=pFypo}hHZ$nq#N8F$_c4yy^8T40b z#%7Y&$#}9!(vtUx9_t>qn!l_}3^9}OZcR5}D zy6tfu7BEhI$HOA@Rx=)!P^vc%n;|`%hb_=r^8Lp$97}NWC?2*ZP8gZg^m-;?8*q{l z2U`*dTdbO=Ni9AQ=A2>UQPQCkI|~zd7~jPcmhmv`X~JhbEW*ZK&chPE7ykzjn_-`g z1rM9o){$Dk9`C@@ElGXRnTM^gH^+~Mt%-vu@8L(iXg$E`3SB&#j3tvv4#^-RA-CjV zOw~aG(Ba<&57jd!xiD8y2fx5Dr=5Bal^o{Q(+3bZ^x z&&90fkN=S9~=HW{O&W+OC`WCGF%gtCw)7wK|Yb<6a51|D&EYb5e!l4O*t z1>7KDuYttUi^Rf3A@nr%ZAsntp3dR&M2@~FN3K_ff}x8^Nl(A~B=lautrz5}@sJ8- zYO0i*2xw`<7xC`!lTq8YnER7KuRN5Ji(ff!Pn0LO%@p`~O73WKTgfp7d0hbYHlE3K z&*o1v`5Q7sxfU`VM*`MF<=+o6`H3Io)5&;KZeh7J7f7ut?OjMbLL*SdL+T+Q_t(DV~cm<<`xFFH?LZI4~9v zV*bmwA^*Z9t&6Mlmc#Xc`9dJIRBKhF3f&_ zZS3JuX!|^1g55JUX|@DYckjAe8^AS7QWu#vd$G z1h*BR!q_iRkxJBznq%xbi8DS{)SA|#Hq;h#q8;=kdx~$*(FU|3b)-(T5p4`PqX}(F zn~|Nc6fcDa;tc7uC3S%&;zr$RE9hU3Ni|8O9@LY1;W7#z+8VM<8|sVIg$n9N{b8HZ z;w(`B)l&lvq(K;^(jYg7&~`MGd_&tqGwVn@(atoChLh9K(jv(j8bzaN7uuC}qursk z#n7J6++t~ONbzwro+e;^N~C>ZweCkA(EfA)PALtfgXmy7gbt;{XfmXz;WUM&(lnf2 z8cEY>1|0=mFB9_W7;=_oK^FKHvcY$d4gP{`@IB3iE;SzKoF>vqbTXvqw$Q7lLKgO; zuhQwzwPw(n^mXV)v*>KtiQa(DH4hfH1<=11LHk-l3Sp&P3SIIo`Zi`xEnN;v(z|p8 zbj$bXDrjeG==<~ox)u`rhiId7^dq{Ct|#YVpW8?`(ao?qZH4x*4VI@JkoPV?X5K}2 zLo3}w_tJfIKRp25?hyTy9;Qd=QAjIqLc;$TR;te-(d>fW_9Z<5dH*NqYkJ6s2G~$e z!IE+Yy2`h(seDh*(eqd>3WSB_M|zPILuULN`tB8am0qLy(CC8b4O##h{T97V3uzH# z^%7c2%P@!kNXuyjX5fqT4m7=V`V;+`{sM{NH+q*`qQ66)zeoRs&iQ~ogx>i#G|$Jh zidHkiC}Yt7Lh#jhiJ3v-x4=q~CG=}+R*%^*TV{vVi2BeP+C$ECfV|m&HH4n%#2P{K zYJ&Yh9iT5_jR{(qGiwP8rYmz}?)X}Q2P|`5%$xa;NRq)?vo_2ZnuH%Tb`2TLw9q62 zn4TFR$7aHU7tBJSzl~y{(1$xfr|-l%L%Ymk;n0jD$rwm;Rg{oy=+M1c9~Q^rVLeTRj$R3iU_aKM4S=ST1Y7DLHkb{;smEcYD;dj@p>3x? zV$NY{Yy|8F=_~`b*3l#vI`HWoI89G1)SU`w39Cc*+cnN5KnG?h(buaa(T zI_b_{V>6)f&V-$D7MsoHus2|DoCj<20$6Gn!LGa*CwiB%H(|4V8}jgS_6~a&mdKUt zJ&Z4xNgnJKtH}hmhD>Ddvk$N?G#K z>BV-lPhk7q%l5JT>;Q>n2Vn>P6n4!c>?ky|W6;e$XJ4={*$Gladb6)!?L3KdfTu_w zcAA}GXJH-rj(yM0vGeQ)c7gqf^?^xPr;j6(aRbN|>{@$;U1Ry|I`r=X*uST;TkJL~ z#E#V|teBOsQdY*wSp}Dco&xbm9Sr~5>^Xqg!hFHgtfwlIPAxiVFylvek|+~c4J*&kFXbO1N(&oSRXhfe2O)KBf?QwK##!&`nm9h@TG7<_)7R% zI4OK1oDxn8XN0rDx59VA_X55MDf}Q@5PlRc3YYMeXJ=BK#`+CfpT%7yc0L34aRrg$Kez;V!bQW7euX9y2J6Xr`gssn8^noq2jp!@375zki*e|t;{ZbDb zXCUmC!D0wjOG07o>;Ri&C$X~_CWd46BocPTXxJ6ID%xNVMHlP^-B8v9V?3p=2{(v3yPuNp4p+jatiyR9* zGKUCaE_9YW@(I~P!WF%8BEFtg4|cF&q$BApP7)`JQ^Z%qsp2%)BEKSgp)>A>U3ClD zNi$Sm;< zauoZF=8E&keQ`eY)rI0Bak02WTq?eav%_zb*|3Z+C+|S3eHXgcFXUI)&s<;^b;auK za@gkHCYvBft%sy(LB590yAs;(DrmlI#P`JypzD69=)CL24dO;|lek&jB5oD8!Ir!O z_V$m(UE*$7X7|8iyN?`#{(S(t@gZo(hhd99Dt;y&6OW6Zi(iOeiYLUcaFY3?_>Fi< zJT0DqUFlocnZAdm>Ad&@tV=(_Zfi;A!d|<8%qI(>KMy74WG49nmZ(eOWmuxF!XlL~ zUKek`E_GA9CEkWzsz@vrOT<#KOe_~G#7glFZ2LdK-v5jEtN0tN{J)ETi1%RYzYlBw zL-8;1Z}AaqL{(z7L?kLPNx%w#B$-L(l7(a`SxMGXJ;?@Z0(Me;oVj+88b}Q#N6ATQ zBsG?rNKK_?u$8rtoTZkqp1DeHk~=JC9+Icz1^bx~tZQvxS8EIVnm;UST1h7bNP5X2 z1xi6uFj-8lLmFO$HRCH%2(+Gj?0URPu8{(AOKK;DO6{c%Qb(zi)L9CX!lei)5~sbR zr7lueshiYY>LJBQJ@K`&SgE(vM~ai;r35Ka>MQk=`bz_(BxxYlBnC@Eq@mIX_Pct%9O@PSyHw%RvIVeNV!s;G+vq@O_U}{lcg!rE7DYHn)IqP zU3yKLAUv^Y_Anc|1(XuhS@SG}=f7zDB1r=Huy6yqqwl9F0z+__~Nd zr5s&Ekj6Z0Oma$2c9wY<4j5*ojkXHQ$;cX+oH9O-qt1)a1t>-0)oAmusG8>*Z4|G9 z3R)YbQBV&H3g=Y{)~YCL6x1|2eE^RKTSug1l2t@%c3yHyN?KN){1~q* z*)-bVAho6e;fk+~R=|N$M(`nKktxY?1r$PSd4;t)B`g!9(a^7IH!^$1>g1Sbi{QK!*cMb)XEHY%LI8g6XJ zK=UZx5NbU*NUh#HN^Kam`XRhwgE``1Au4(FJbySxQLDn$21l#!g{x%LM)CKI}oFl^l zzwj!nMI#<;-L-C6NaNMfaZ2hGKw&{@Itox=garjhvTohYy4CbF@P%4oorV)w$J;7G zc|#i&p`@cHgqe5a1n*Xdz)H!eT98?{yo}7$G>&wHb@#eRf<;xP(dl_n0laOZ)FQM2 zyb=LwK5d9GA7|Z&AhVd-j-(4v`XuTWX%kFzOdhWg6)X{D9&6eM4IC!}=W2EE z23K?PL~$Yp2U^GGW+vyR*EA+b!AsPu1lQ?!+`#K+;G|RsdNho&Cxw~CsSOfW(;ylx z&#g0#ZU#;@1Fxq+#YCqyV$4fbu>f?vS-c5yfeI4nJ-pol1FhrhGPg!&9NPkULj`is zfxPIDC@}$D+AP5YO=BL+D(LBep%O}GVZ;XEsvJ4W7l;*PT zSKBO>{Zcd1a?)}$a@9^5tWZN66&!5VZ)8rgDrsOw=j{~Cs}US#J)jl{e^n_LRo9yh zsCiYRRi|7eb2{^JOQ-TM(u2*COt>gSW0h2AKF~&m@WRy!0-~XQH|33?-TgANQ!;zS zMAlO{BgEjBlAN2SM6}TwC1R&EYCyPOW^!Iu&#_X_K4$0tT(+!|w! z8jkO&@?T(>S#k{@q1*DYN1Y2X2lH9WSnqI!Uof0;@`Q2z2{Q_1oZr+r7w}X*(`oqF z7Rjd`oiQIzkK*No@jkBMzCnGi!#I=7c`CVXcbi*hNk4Snl{1E z#Jh@G1mqnhAEqi}eqOY3D$;PVAeeUoO^9_$Ew@;u)M?+aaD)1~HdtY3j1ekB>-Zd{ z3kWt#tr-urQCePctr06e-e}Z$LucTG(s05V2gPV!Ohlkrn$mt|X*EzGjVaw6(0Q|j z^JdlZ^au_$f|nk_OON0*&={=J>eLIIz+VkFwrG%fnrScBa*$esd0K4?X>ZvXqE^kPjWkwV9jSC`e?haU1kf7s(DHaRM>#OSY@|W~>yfpsqYi?)0IQL8 zYNOGqL$*f8d!a6bLs5kQ2v?l%0##od#h+^<#q?S!jaN-Y1uunIr`IhTX}p>`PGOw_ z2vVAo4n&MTAF4w5z!GRM%c$v>7`OR!q2aBdi?+;AhKq!_9_oN)6fhz<-8A7WW0ZLY zZ=wv-CJE$O0)x#mOom_`r+k2l62=_sQFW^WdKp`Xv!ju_^&GDNBfZojv??t0xqy13 zN9LrZWo0I3rDmk4?Gj>q$zYag(m_KwCxq~+F)+d`vxc_;N<~2%5FuuwRn^+pl*7wa zsesH1EQ}M}58xfaD2=O{mPQw3Z0~5ZECm(otmLuTxp_I+W7E^jvP_ECbL@=TM2!Gw zq?(F>&N$<$(ienfK4R)agtV-Y3ITLM2D5C_M&?t4DhNUVqBw2_y><3ef*&tY&+%0m53rQJ0iX8?RVL7A`6Lq=#9{I#&;^8;<<#?Ouw zRvkU?PJqg#fgw_6cGk#TF;5l?^GuL6<}mW1IvQdUGY$(%WdMouv1**q=)ySJjq@)o z4~p=h&ORDlaERG>Qw}pqRYuyV!o-~5a(#9!ytGhNE%Atx$`G_gj;gLueW%P0T@b9N05x1L-JOl?usc7VyrU^cl11cW+H z=V(sn03%2ReN27|+IWq}gQLu*nDAJL)@n+f2@3<55dv?q@WA>R${RyR8O25}97ZSt zUafE#SQF=xlPUm0IOOxRF)oZ4IzoaQnxXt=T8tQ)=a-o=GT9=J2jn{IaG4(Wm9CGt zGHk(DDh*#{BAqHbYINZ|zmd0%HUU-U(`k%t$jwA4J{=em_%?K~Pim#p>N z4yO;`cm{C1RT~8IsdKqrH2`V!T%OSfaQXzOolegu0KIAu!h5P}spn=?J(t+^T;R~F zDmvitda8yajb4={;B!9XGKJp2>7ts3@SNkRO1_A5zTze!y(&Wi4YuREVqaf?%1ENAw_h>Q#IN9^wiI!B;pK#tk)}Ue%g(dR4B0ua5kBReC{OodNW! zjD)yaAHAyNARcTnB`qi0FD)z891AImpEps7Tjr%hJ&;o@Mr4oAQ3Dwh)I7NvljH=; zTr3u4DWSBCk?DC#rmPGVkP3NBh6-8sWytV3WQ0`6N zrQn>KmzAHK<8?B3mxZ(a6hjQL-sXwb|^_CZ%8?PtBgFR?B|e__W--jO?tM6sNIiIT_ih z^71Tf(5cFt5fPTq?AggmrB%9z8*EatF`V_}e)ET%$76r;xHWb@~|JyfLvt8$TI zW=4)OmFP6;;wXG|=+=bsDMS^%k**Hqyzvm{Q;KS!)#+3r7QVqeIVUH3;`p&jp;+64 zVHI(O0z4_$Qc03)%u_0eD zrYbrHb%%h?5XIk6mq!p+r&9x_$-JD50S-j^cG|a=0Jl@0Lft^LUK65B#%-V zqy_P`AcXanin1;P=cyVcRne$TQne=U$zda`aQ;?=Uvg%i%p`cGGzxrU*%9)){F%a< zh=*A!whWUJP-#tYgPPQ#PhucHHbpfG0*DfL>0+ppPkV4^(`f4l4 zt*=)S_|qWMr%Kx^3AQy1phj!xZu)Mx>AOl>n?BWKEPyqZc6d@@HJ=yHxa(8HAMwl%C~T9`^PhdQid`aIesqE&09;;d82D@R}~ ztP@cyV5>AsfYvWdnL89#(HYk$)JY4L9Yz1p=(t5k$1OTKE^+8o{Ra7z0S)<-NetUS zq8iBHv0utBJT5GvM^u7ekXB>U#Wbem&_?S`(;T`eV@us6#Tsjx)W~Fek7>FJQKvIa zbE@6;W16gH4XJ~koQ8EF&C~g!Opw$Vm{y}g4~~8+4Z8_y(^Nj8tK#d0wPS)98#4nVzo(HjYDd7W@d6uO;TV;h=PgUU;+)zkXDlejpCp#!PZTB zx)sz#Ow(KnXe?+xnIbrTNl1s`(!@Qp>95f zUfMc%H!xa?O=#21m?m#Uz*d7N;mRw#wP`97q8;kAnOa5d)3{?5xb|Hmqes=^1eU8W zufsl-Ngq&~30zlS7L;o;88k|lfdn1+WExfg>%8coE?U)O11tqutU=YL=`=REwK4Fx zx=v)8q}I^1o5|Up8fthp6M|_q4Rce#F6e1b!o!++9j0hxvi|gR-e+-n;4?Vdbf7VE zbYPuwnt?N5wXCt8)s!W-hP={ek32oO1Rc6es2=&dW8%Z&RG)qPQEuhEQgQQ}z_+mzv?X1#XO> zI4d**zpu0T_+7}#@LSF*31c|ng5O_oa)si=3eE@P%*qt}PQ|Tw6lX8q#EIbuF&e*J z#Tfkd5_{n{PK?8Ef|!6`?Ec5^$Kr1M9uN=U_oR3MyZSFmxMT!7#Bt(U-W`r#d&vpE z*bk0hSIL!7?DX!0-*DWDPO)oy1b#E5N%+N{YPsg9Bf&`kf}Q*f5G%!Ba4XGk;(cO< zJ?Rgzub*KDzg!x@UiWg``1%*hbXCgqRmyCul6c%fI{odr^FLm^eC2BXje?uEZWk66 zmz0*3S5)5l&rd)9^6PJRfB)m&pZ6a;{Oj*WkE^OF6GX|(+``hzx}J@#oxMYYhE7eJ z5nSN|pmf2a_tvi8vUTStd-m=>cE-Ryx{YsJKYxu@7oax;1_g(Nw(rofQ|GYoh{&ku zuHCx#i0K&{mypZc+3Xv=Z|eRl`y1>Ze&E!B_yZ#jHar-AFyr8> z2d5u0KjeHU>C;Jvs}B!6JmPTfk)cP19~pbJ=g~1ovp%c-Y|v*J$NC;iIhJ~C`1SCwN1QZ2>2WgZoc9s#GmPVX2{tqXQi{|XWh=WKI?b3-Py>qz0dYJJM`Q7-?seL_1lPV6ThAE z-J$R5f9L&O&+lTti~HW@`>^l3ec$8z*mLL3RiE=Y*ZN${xt`~Gol87#e%|`L{rM*6 zo1XVR?|)u%-f%wbeC!W@|6u=v;}1SRwEm&}4;_Ap{UPy!)dj~3Z7ygow7byxLYE8u zel+}uk8b_g{>N??PhEU`QMy?FqW#4N7o9G;U2J`^%|-3SZWo7Ks<>2jsoo{GOW~Iy zFZI6E|I&cVe_wud+48c@<))XLU3R_fc3FE_f4TkTh|7^z?p$%W((H=Qm5?hPu5`K* zb*0Odlq+Mdai`G)+U{E+;1`Jwsk^E>2s&JWLz$dAmA%I}`vGe16mWPV2e*! z-TJ!2b*Jl%uQ$E!e7)6m&+C5Iwbui$2VW1l-tKzn_4e1pu7_VwxIW@~&h^~udDmaL z@!*E}je0liZZy2nJ0Y^t_Q= z@UWn&fEHL5*cI3pG%xTd@GS5u@GkHz@Gl4|2rXz|(4nAXL0Ca#LDzz$f}sWJH~+jz zZ<^n2cHhrvFXt&48Qwn}(Z#H-l~l-^{yp@77YG7Pl;K z+1+YztJ$p*6-WzQt{e z{fhmIHO0E(z~aQ>A;oFM8O2#8zm?oA`Mu;p$-|QB5~;+j#Jt3^#HPfy#ID4##Hpld zNwbpXC2l1iC7vZ-CEg`IC9O-^l=zmkE%7VyFVU1}OFEQvfs-7D0^7;cUg6rMVV!pOzs6%Q&NR{T}*cSUuDd4*+#RYkoDn+n?syNdc1_7x5l z4JsTf8dWr|Xj0LlqGg3kgTg0D*P)n71|1YMR-MGMc;}c z6+dSYdQ^H=`c$^5Y+LD9sjCdCY**Q(vQK4VW#7utm1FMwbm!iksypnC zbiAkQ*UDfCvZ?5h)_KZ7HykQba_g zNZE)KMnt4+O4&$}QlOMl4szHWq)2I`Y)X+@Hk(Z;MMOl5h!{ySNhXuYWHOoc|2@yj ztfAI({^#iDee#{(^WNWgy=%={i+5~Ij;5q0XH#-hN>gf+t0}F?-IUqnY07HKZpvxO zZOUuPZz^aiY$|FhZYpUiYpQ6fY^rJ+*fgkVNYlushnxPnX>QYZn-(^`(6p>+MRR9! zsJXj2(yVJXG$%A$n-iOD&5q`zW@mFsb82&1v%5LHIioqV+0&fWoZXz$oZFn&oZno~ z+^@N)xwLse^T6iX=26X$G>>h5wE0`j)0^itf3JCA%jYf87NtdMky~^v`W8crsm0Qg z&|+;#Y_YZ2TO2J(Eh#N&E$)_#mdqAUOIAyEOHNB}OMXj1OJPgDmXenKEu}5xEhAgT zw0x^&V#|{)Q(ES=ywLJ}-(lYw-&a1Lugw?mb@?K`USHHF`Sd=M&+N1KtiD8_-IwHZ z`jUMqzEq#fm*#W((tRFZmM`0v*lKDuw^~~9lccSQt+rNsYf`JT zHKjGR)zzBT>TXSM&1lVR^|WTSX1C_G=C}51EpF}KI-s?xb#UvD)|%E~t+lO>x6Wu? z*mk!q)YjeB(-vv#ZHu-kZBm=urfV~_8QV;4<~B>4wJovD-j>wnY)fuSX-jQ$wWYPW z+tS-I+A`ZbZCPzOZFz0^ZG~<9+KSuyw^g)Nwhd_;(KfPeQrn!i@3+0!e!TtD_V)Hj zd$b)t*wHSx>)Q40#&%P?x!uy9&~9zFwcFbr?Mdy<_LTP2c2|2^ySqKTJ)=FRJ-0o- zy`a5cdr^Byds%yVdsX|;_EGI)+UwdMX&=`5w{f9r_MqhpEHdVd+Tduy!PN*gEVTjt*x>az{!>YKN;Mt;5}s-jUIf+2QHP z>d5WL>&WjY?kMl5>=@8d-7%zNc*ls2F&&R|JlZj?irq)7_cgnbDcq>FLbr%dl zS=3q5*}t>2v$AtQXLaY`&f3n0J4bgu*SV;3b*DGDIr!V)ouDt+8Vm-zg5AMLus5g# z#h?_FgSwzTXb2jErl2`!2_^)s!Ni~~Xb(DqNkL~YIhYbm4Z4D9L3c1Cm>Kj0vx3>d z++e?8aj+sdFjyTN92^p?2@VYo3l0yC4n7iG6kOGHvn$Zm)fMXM>56oTT~e3arR&mn z8M=&JrY>`rr7NKeKjhnG>%!0Sb|rN=yOO(7x>CE+x-z;lyRy2ny9&AryRh|ZS4mg@ zuF|f`t^r*`x`uWQ>l)rQs_T)iiCxciE${M%j)m@qz6`a6{GrZJFw_+ag}OsMp-8AV z6b&gMF(ifLkS?ST8A8U8DP#^=LJ1*jC^2LU*+Y&{Qpg!f4yA-rL#|L-$Q{ZEc|zHt zf>2?oC{!9M3sr;$ghqx&hsK7U4lN9==-%CZxch_dyWNf5&D|Z{q3-VPaCc94v|H&G zyYbV>_#&n6Hgp@iP2J{hOLs!IwL7uf)@|>0bSHH?yOX<9x>LJd-D%zK?)2`A?#%A2 z?!4~&?t<>3?uzcp?&|KE?%~~|yC-x{>VB$wN%!}=z2QUQ!{K+re+pj?-wU^e+rxoy zSGYSI4oAYh;b<5?Koge2au{FM!iKOhYzmvhmT*GY8cqz`!;WxL*cna^r-W0(u5em7 zBb*t=&jW|E!#UyHaDKQT+%MceTpq3nSB3|LYr@0BBf=xYqr#)ZW5Scd-wDqR&krvQ zFAA^f`L~{fJ#Y0K?)jwW^PYP>jXi;$U{6<1sHeLp+|$z&>FMo>_9#7KkJKag=z8=$ z_%Y}nQ;)gF(v#3*?Mdvx&oTEndXjpaJ;^;OJ*hpep0pl!PkK*APgYNMPi{|sPhn4S z&%mDQo;8KazEmWv`74r&PXuQ73q$IBR!Ewq&E_c zC=oFtMdXMsqK_CN#)v6mj#wfI5o;tdVvE=#j!06(8A*<$L{cNJNLs`lNso9US&^Jb zUL-$K7%7hohzyGikBpA|Q)Eu$yOHO65A}Z5+tSU_Z@=E+-jd$Z-s;{-y_2KcqQ8pb#~!06qqn1XqAgKhG!X5K2BTfk zP_#Q5j`l<&(cUPw*o}%&DJn;?U2N14HAYQQbJP+|h+3nGQCrj=bwrb*_>t;pN;Ea< zj;2R5qM6aGXihXYnjbBQ_KOxphed0nqoa>02bF(U{-k`4U3i<6W~D{(D*+{_bSWXF zTL~*YN<`^ZqKcwm!xsgcDkyrzpcoaCVpc3lf?`z?6`NvL97>YnRFahxB~@`LSxUB& zqZBH|N`*2|8KewRMku3{Z(}E%L)ho$I_*0n-WQu6+-c^2zt4;){{5~-5AJCc+tKL3 z{fw}iQ6_eS#Xd$N?N>y*6XDm~#i6_pQ62UenuA?~p2c24-xpV4x7734*W?CvCuzj} z8ee=r66{dfNqdpR_94MumDqzsQq(_I|%SuVt$34g@S2Y=P}EKqkW_`le%;OqNq{M}v} zj=$SSBLh2VsC#G7ju~aVN5&BBi!o34JpLvBkMaBQr}clCmee_!9E zZ`QZyefm~?o4#G|*LUax`c8dN-=z=fyY*pxk3ORB)kpQpH}0BX?b|JZ_DV>@J_(uF zA%XTsDC*l0p%S|xjK%H+Gq6v=%ZBxaUm4CD{%ZKx@QLA5!yWvB{TGHt!&ip;h9*O^ zp~c|CFWR>m+6{g~haq6-Gz1M@hLEA#5H|D}B8FZ=)SwtdgJh6#Q_f&88O(+RgVkU+ zBpH$oE<>gv%aCm-Fq9bv83r3h8tM#>7`|nggWlA%-P3fjAQ=w^`X@R-k ze9PQu{>psc++=Pxx0rqAR&$%V-Rw7am;>fcbI{yn4w<{nVRMf;V(v9Z&59Wt0-I&C z&a5{Z%to`xoM5(^6U`2DlG$laHM`8|=3H~0xxcy8JiN#P&pgVn`ja&Dv8+IHPMi0Of)B25)%@wiHV8ML|39aF+I_f_^rh6CjN`< zZ?1_!%o6T-Zwx!wJwhWucmScO;HrY1A_8t2v`+wN~Xg_cNv;D93 zTlP=vx9y+W8|&!{Kx! zJ5n5}4woa{k?kmU401f=80mPz@pRJrNf(nYCH*DodeTQpw~{_dx}9_<>GPz!Nna%0 zOZqbDtEA?n)}*#1e^N(MAgME{D=C~LCz+G1N%kaHQd&|$QejeY(#WJG&ObXZI6rWH z>TGa+=Dg!PIe_{Cl5>>nLH}_;pA_p z{4M2^l-ntvrrb&SJmp@>{gkE@e@bUcIHe~glG2+Zrsz^KQnFJ9rF=W(pHr5mo=d%! z`a$Z=)O)FosrOUcQoB;cR6}a2>zeBW*LBwo*T=3;Tz6fKt`?Wi)#hqihx28XxaV+C{Mq7qG^DmjVGw)}1WOijLnPR4tsmruwCS^J^Gcq$X^D`%B zKJEF$^O@(4=bq=jr^yrcC?3h9^H@9y9+xNEGtl#}=i6CVvZ7f9*{@}PlKoY7cTRhb zlKWY1du}(rdR{2#EG)t%PyXU?|KIgbu8dZ?tAYbFtGlZ`gR%y19h^BdP}@1Oc&2lX z^Lvi@?j@PnDTtn{h{CJbA?S7Njr6bBUFR3VudtVq0V?0$SB%xl3v-pFKT%%Xs=PQs zdF2`G{zyK0ezJF;uAOLep8{C;ut@pm0K-4R~BE3ExO_~{qIwnpLC_l5OM!iHvHbF=Vzi?Fdp*wiAt*&@8= z6JGZTKlKSeX%)7%3O{ZW-fR>0wh6Cy2>UyPwVlG2PGMb8S=lA5>k|8S3$KNh#4P3eMauXR<@^1W)Ir=D zwXL(z57aIf_F=bRFR5jCHZ%e|`b`q#vEwFA7bbr1LI;4k(p?xQ*QG#8)9OaDHs{@+^DgDQ%Q^3A02AnZigk8lLxB*IyQiwM^c@I0&(ztzrhBltNdgD|-CdT9gyf4?+P z8Z9%GImkmhUe=QhpMx%g0Cz#faBpXDs^N ziQi!#vUl;|@*UwEo(cb+o(YR4dL}F;(KBIj4?PpcHv&8p#=e(XxWacxwb)UzoSqCz zW9i8-_JqWfVeABnr@_+S=xH#1bq7y_rMvVr7`y-BX|TM6o(9X`r>DX459n#Iypo;< z%P-T@V0jfi4VHgMPlM$(^fXvrOHX>`SLsQwypEpq%5Tt6L#=PkQA)QA^4; zd_%x5gp}<9>QXT6@6hX{%%6pj<6Tu5W;bU(+KY)TtT?m7w+P(4O+{%!f7M6mMa~X2xfZWR{poFr) z2%`|jBTPk@jj#ZL(yT&QkAU@{WFNu^gmDOy5oRJ_9VuCYfK{agD`ClIgdGSJ_b|c< zq}|yU_T%pngp&wo5iTNJL%5C5h~P(vsG;0|9&sRKAQU20A`C;QLzu_`we7#{8^RuF z2N8}ToI*H{a2eqS!X1R>cqp9-4x+b&zF@&$3MmNL2qg&B2qO^2AxuU$4h=*p(!FXl(-y1uX(e|I44 zLpY3p-YY$WZ~@^e!Yzb*eW4A1!{{v?f(^lqkdIJ~P(wlf^P@gNcr-LlLQJ%vkI;rb zLW}-EL+ko9w6TxS(mq=EXS-D@@r|*j)(q zm@w+9qSDcr_5}Ls3G@;^pNr80+9l}M+1miRPZak~J}U^5FOv4yFoZfHT8a878Y5nj zJmih7OQ0gnTcuGg^syXo5@-g~JkVmME1;VQcjGJ71GGZ(#^VvK{aT7c-w^LOw9^Rh zYwO(IC)S~*o1fSL75%Idt#*A{C`R#kaWAcgeJOHdMDZ%Dl;1abY2EBgF<7IiF&d{) z#i*DTFL&Jdm{t*|c#7GYcY#JrV-(M&Vijt(p8C0B3&PHd{jf(6PF9>nY^^scFy3lS zO4PKHYdVGULr(at)p;ChIu11*=dauY{UE}z2eeZ$ z?*rQTm{xgNqZ=`b$0Mq|1KnTQ3$r=k z{WbRQ(&#{p;$EVvqa3dubdG4~PK5oeUDD`!jL2Kn@U?jNAN2O64aBsnXpG`H4>aK? zb)A&UKu^p&un3>3NPA-66NjHTF)s3$VPLIBV>Ox-qbK$uE|2BF88L0(JdM6dTda9k z#HiXDqk(HR+N9BTjrPXqf%+YaX#YW2mYZTY6XxdGU?#8HEiscfo zRrM%Mb7_>TQK=wQ503X*_4vmOY6>PCyVbMFTfLxqRrUJnE!8`#muj?M%jJkhCp9{& z5oSp>&!)!;ac->S{BCR1sF5G>BIF%ph-p(MVqJTRHOCW&2W8Nx4ni)2IF~_-K+D-X zOe3slDZjTk&`@Z`a0|KRzWwnU?qF`~4C*HPLb?wB@svqn2&^gtQ= zV%p%t8l8yI1M$wpw80lNx&_j4O|NPc*Y0Urn?~Un4Y7f^mGPPm$=9@Ujkq_sUuw{* z(M%IG?{tmkXr%SukON3@iBoI^VNHdNYgj>t)U$R@i+3H=Kw6Cxp@_ZrH412i)lv1< zm^9K-)L?b&^I~PJsij;V!z_M`XK~F~jV5Wd7V&OUyqXyr&C_VHMk_Rmr`V)v+d+Fd zmqVI%T%*&V_t|?TrVX`X9d{w**4)*|r%@(;qeIw(;uIM&$l)5Yt*PwT#LlC zVFryH8lgsNe#4e)RH)H6X_cCHm`3qTl)jpDgz^gs%)APn0$ zY#S`*k6Pykya!|2us9uyX%uhRDeARh=dq$A576b9ci0Vly2DztAPl!K1!FWEA60Gm ze9#i|4o}fY)1J78^LZP4v*9gasvbUK__*PdhtGTJ&psP%`K&THaYBW4dR6DwMSuOgZc44g!*7h|m?j>?lyea7CDdK*K(PTcYNB_lW8p@q!n?`xB)@Z9n8)Ec8Jk2|GT+Ca0AVw6g zb{FOMP2Sq0v3RxhF^b1~{F0_&q|{o)%YFP@OpB+8Q?2H$)pCi`)Rm|YkM9%c_erQD zYI&V3{a4$7aGz)@#=JIA8x@3yOtI49HF(Gg-SbcpJk*Z*X(;odE0m7@d#FnN3#yIL zbZFE0(?erpG|`JSXCtSW6r<57Z8UO6+8HtJp?MlDj?n{YSH!f3)@rl~=`nv{_aYo( zIu1I`^nNT}JeR3g(Pqp`#L?O@!irgp+^2@8-h*u;ifa$ujCmi>?#8r-d>Vyf6p#1! zG(!6Ooa5e!rvzbyi)uB|fuFjMOf=y05sh*+8l}-mjpCX{-|}nT(ip{aj?;+2G4BX1 zZJek+s1em=ycTaNKFuDn0Cp+DDvB{;J;Ig|J7M?7;>F7#QqMTlqCP_H`{=Aj7m*(I zhP{Sxo2fCTO}vA6&8$UYG!ivawYWDBp9j%+RQCWaY?V_`h!C z#F0;poGS<;7scZdja*JC{$5jaiEAUhvHTD>pX&Kwijf;J+cA%j_qLIHMjpi9V^3sb zr7mF&qd)Qn(|L_9|06w7jT9p&-V+vt6i$JD=_9mtWRNrrWuG<*WsE}H2UCnfx>3jp zmb@yV9(^kn#}RyG&7bn)nL?o@=hzCRx@q%wESt)AI^_aOhcR{F>TaJjqYi* zPNQ$qHf!D;8pY%7)3n1HozUn^jAC_}Hht8Em^Lb&%hhQ$qi#{{AJF37u$H3jA8Aqz zR@Tw6K6qG%zL-7=DO7scCI}C^V_x*o1g(dN;=NWA%lYBa8con>I&zsqxlCA#u!-p5 zg$S#emT9yBw3W48paV=tLG?t_ki*00Si26oq)`LYeP{uq(NzeGS&POfzEY3&Xqpyp z^gK-~(kSk&)wHpoNnFMX&^WGRuLks(~LmC~A(E}+? ze}ne^|4AEt1?AkN(#DJ*Gj+^t{9Q2m?r7iW5J-w^Un48U`=4nq8_T78}rsJ(r9^%;whe>-V9QTIMsQPej{mh+hX3jJ@|BxHT2vg z7N%nwozm#MMwc~;r@*S*m$n)Cak-BmD^;7|!;u`KoFBnj+(+3lnt*va0kaWatjc{f zQzNX`ecA}bSV{37NHI?HPL6pWiKm^fd6#J3b)d~$+78XT587eYZh`J0v^{b{qca*^ z(CDg0@f2|i<7}uyuu-1qX#-e06OXOIr_rRxzemv(=v1!N*a`4XA3FzjA;Pk;t6?{c z-8y#H*aKsaj;+V1b7L>TUPox4T*lrn{qhs-R z(xWqAu@=EDMp(hL7PN^8tI?x-nGS)DGo1##&xDbB^yZ^?VKJ)e^Sp6Z+$DCAH~w60 zTq(YBA0};Fu10ZfF!WL6jeqZsKLd(CD;hT&@fJ|LaZ3?aF|7yf{2Fc1wEdtX?2Xe& zXlGfw2)f2}8`Oy4kKdVn%z-;18RU)Mvy49#eBiFyBE(qE@#5r#wvn}Mpgl|nLC2U* zfzC5s2Hp4?-GSE3S`d9jAdI)LO#x*il<=o&&7uzBlstLeMhSR)aP$ zZKW$@rd=8x(C8@Q)sr{=9W4GWF#c`v@#{E>_HZr@NO7MjfaBNnHHyZx2~JQEc_(-@ zssh!rHWoCAX$EK>QT)1T!eVGE*t-_AiD^4%FVi8=afH+Hb?5=Q3C~@Q=L3a^z9k`8 znOvY;rc%&grct2rOjAL#nHGSSGOYrwXW9bV$+RDIgy|&cEK&Sg;9D1=U1RTUP$Ln2 zCsDs;#lI<$rhX@he+!&g2(6M+3f4816Pl8*3Lk?z;qRKi|HPyjVTINeDBZ zoJX-h5bKzY?rNy80PuYl1+gRHJI>>Ykbc!f`M8(&p zDd(Z{y|gJeG`bw42ja!OcVb@Zu=pLXDa~{TZAvhv#l2H4nwFwbwnilyRmUh^?$i-6 zEq>o@>SRrusnL9mmT0sR$N4&Lxh{4!LU+ri?jvpL;aI!}N;?7l%+w38R}pS;ynCQF zgm64wY-L3b>bF3AAEa++(-u;SY0D5+Gi?CfXWFXKE{zUobX}vPAihgC4aZhr+PJsj z|ELAvjZ$gx^(8*r@n-JX=$*d6tCkA3G@k4U^Mw{666OOMH=1)77&Z*3<#|TeIJDR2}IENl5?OMed?(toy!KuoV(7rDZ zsu)#CEw6@lq;fwrKd7p5C$wM7#pOrPm$DFscD-^vv~;<$;#5Ttlpw!TF|5K5vglsK z<|r2t?{&Gp!cb`g-Ow$om{);ZP``I8=HNXbSy(T>Sz)c9wq2C}qhfOT3HH8MF|neN zX?I0k#U)UOe7@pX#c9wVluPCjc-VMpS%lA~^J#OJ=@~0J^ibbfwWckAi zH{M~Ag;r1sl`A^stK|nMt(XhiSFx39D%F%%QJpcWHRZFp+#8^JuGL?qnu^1ClL_Ms zZ8^8-1!#LJ&O-Yo$W%eS_ADqt}N6b#eKY~h7km%l!uT@I?{&lmK#P;@|EA{I|6+4ekS6* zF8RvFGTi{}W9kKA)rWUAQmEQRXj?SzCZ-N(IMNXB4~U2L6+~&VYJozMuksVV{vkq_MlK0m&E6dSqpdZyNDMzm%-tLp=W8%Jk7O~crZR7Uh-D23Hf5 z0#BJQa%m=v+jb)STU;N#cMWQm-z+P{o5s-O-DSC@HK4V~&qH2umt0>{S?U43ixf82 z-i24kX`g@wK#K3;9p4ChugP!rkKk=*S-d3g9=NjqO`_P`5aAs%qW>vqe?X-ZK)a9sbRp!G*-gYK7J<#_dj8cOyteLCoR$y}xn2VE-J$+UaW zxsr{bAM5I>0;O25kjsKWM@tWZeuDO1<68X$Z_4oKi$6qPoo8(U-aA*%x-}r4iQ==8 zuRkC0;%aDo#7k&bf6QMlZU3O@#WXU@<*@^6u(A#aMyG|3)iuwLqlI8gPecndB=`OM68Sii%kKK=M_s zR0;3osqOece5r~@U;MMYyRxA;8@c>hey{R6-mWKlXFx+y15eI@T6R8w@2>AgYa zMd)#1ukPuR(*6@zdv}n#XcyC`gKR~Um_8h&E7}11DQaNi{CgPq<#up)H=@rRWe1qbNnBT?f%sGei`IRCYv{UHa?L`jIQOQ>s=6G1) zijOm$!kDk%GWN=E79Z$`<4rh*{PwZ;Nh!a0PthEt{UcKBWbNnTOBH9BN)V6dn)qkL z+fYQuAg1>m zZ1K#NzKxA4H*$)bxDuS1e;D4IxN@6ZK&#^&U0ucKei71i3o1tR3!%pc;i!F|Qb>dF zZt&^8w7V;^3veuOe#81*M+)?0|FP`-1J>vJOdmmy= zJ4jw>t&r1i$AIO?e%m<3O?iL$seH=$j_&dD!$p`8sMWLmR`pxU^kn&-ei&70 zFUnZPxnN%OQ_sIYm*4C+Ki`LVZ{Yl~E+4%nJ%xBraXi?5Q@M;caMn;)fsf%;YyUC4 zJlF6q+kE|MINly;A1~T7U^x0EK+)i@$`X&ctG6R$*hTw8FdjMabpnx~B_o7B=8G+9dh%)p?52 zN`?FsDns{7|6t(-F896u&G}BI`u=zFXe_tl$Wz<46~~*}wyikcs<@1e^6vcnJen^X zm9l(yE><6Lqgs~B7mV1j+yjylKkBLpFC3feB|=Tr-gra)kAnJ~z0ls4KP;P=dzEQ-SzYcqBGjd- zU?1ovRv*j-B0(rn=jq#WV_8P-QckhI%#m9N`kn6af>i};Ny7}|(Z|1eY%ExoXCe*9 z2am6mg>!(3^TUiNn5hzSp3L-noHaHu&4ag&3D*n-qg1cFzhq&~N#uw1zGMz-^|*>u z)7Im}hF7%5t=MHF_{9Zvy4jl{Ix^`NAGyh!9a?lC+y`1?u8=2lIcq)h1 zuoH59&g7hhM3OIu>j&-KoDn&5q2bJwuO1)hp@NlE8oZz8Wap@~J0~Tl8roH)-OgTI zsphM_c0%5t9n3xi?>mB!rLI1kaVD{3H$q#FIX*1=KGS>omDzr#`uxIdnoYQ(&ClTc zUPFE^mBg3wtxOx?RmbQ@@akCmk@!+xm`HkCelstay&k!sMmTd*O&^zh+30`JcLX7i zdrjCNeO^2}k7hg0jd|C(R&T+3mE++oS;8|y{0wOwtbKqhydy;Tx3#{!W2&ai>c5l8 zF4g4i&)$#x?8rscF!Fh7zig4Md0Vqbz`IVq=(&}*nJL?IH7nYuUC5&&V4d9LIg_`F z>AL4c-U=qsb2!_|gBplY5aS=6WN z`IiAT`bPT?M+NWgQ1#LyI@yaR;L+uURwslg(Q<628Q%t~K z1rZ`$q9-NS2YM}fr{B2D9iaJAO^G*)j@osylo`yTT0H~pde$Z)92J+i|6Y=6a?fO; zZPIJ9HTNjh3jY>4l6#nPmR=Tw+=E>12KiF%3C~5O9V$z?YM;IVuiB@Vv8wPYD({wj zo)eq`t6*UjmoX9XmZ_TfYN4kuKPh)U6Iz)&m+4<+`gfl+H~tlCvARyaD2s&`JiDN+ zk*!7BJT#kLlyeGCcxc>Sl$6559y*SS(T-ftQBaXGD_1S!2U3lvgwvvQk9sV8551f1 z*?@Q`Jx`t4%MefS(7O8~;#E@(q)oC^^u9_Mh0|QCwMtpe<&3e2w^_cFaU%0H=*Q^Y zb?n78dd^{{&9aoSLrno~4->^Jjm5(nO0C>1W@aoYrYnVyrDtQU`MjsqyZH81+ zv@>%Ya`_l^FmnXFb7V1dY(|jcNj2G5GD|=n`M-*53PyuA%GT_28J9q-HoA)swsE{=fgT}*Y*)-yefS{-5<+rovrbn5YvY*N@F#S1uTsqZYt$ZoFF8vG)8*n6Fp|qf!{4MD?8nLcL^4F)IU{bP$ z^ajwY%B;Mxblooe8Ef%9%2{|-s>!;Oj&)bK3ynuXI4CJuH`6hn1ss1_S2*66AUsb0?N{gK9)6wI?L0QV$&$%2@W?@~Ra;c16 z=~$bDgV1msqUAr7|7X@__Z83&ndGPU+dK z1-VqyKcar~So@>+YStXiuRqG2%kv@kg@N6L1fMX2h}I=3YUBW1q(ZeIUBmx{FYQRXF0?TvLf8CFh>ZxKm~t14|9YljQwxSCb1V0xOjPn+O+ zpULVePovuZ5UsqGhWR4?5Uo^KAFrHKke@b=<6SFor#XqFNP#U)B2s1*==i$*dxDVJ z%r(V1C9^RN^F`bwTQl#vLY!90#}?tN-N?McE3j9-m4CrS@x1bd{4<$YucXCDdnV0B zg#6Sq#9PSanCm#=?Ut16>#nn)_i&V{^?Mg9=1wm6U1e707N*@e&X2k-Al{DfoLMAu5x>c=PpI=uO^HRXhh z#trwlvjeV~M9M5&A(7W5i`k=G79!Mnu*=F+lU?enW{PCzrlP&HI_5ZCm@nA#N60$P zX+6rU6mMTHIa#69i|}F&Wbn}~rs7!5PQ_T#ZzN=#<9MYwTg^?q0c|fdwb!1Jtr^Es zSEID=$YS39R2uW|%I7lnXB=WJCvQtCRsr!lxCU9Dx)|if8Jo)$J`{x1e9k#fx{_U! zL4BVhJ)5x%dCTG$l!5aAC|k@-_2yCAhACwkdy*%E-15&ep30crM@uuNg1TfWV-lxW z37SA8jgrn}jLVov@uVvm)Xj>AV~q>7m-b91TA7U^Qj@{CbL=%GdI z{TH-yX37p|Z({W>O1Xxd4@(0w@^ffj?M5!yT-q&=dIoz=&dwT_Lg&2aWHD<*3aubZ zm9pFe&TXVgN_r?|4tpE3vXiOh&&h|gQj+Ot#I;3wLrMwgMa+l>PWvLXt0^g@$vK&q zQfNlJh&g#K*++zVRnIkj4p(lMlcUg{!?oG@WIC%oCx7HQl}s~dJ@UJs;vzzdhQ1UN zJbRMsIG*I$mb@4AFY=A_MYMX0xN6B+kbIX2tBN{{pOGccmSn6_IJ4Izdz0ztepV2Y zmvbKsMonvz7omqrBV{7U2r?x1r&v|?y}uJa?3;`SwHqg+tt%w*~ptbgtXrysNg-aE-OsxN?UID@3Y zd%=lugI41{pX_8UC)4V@N+d-xCFf4^qO^m~!6qo9k7n=V@YZ0m(V0n7k3r#-^sKui{S;~9s~J9yhc!0+ zqO%;iV2#D~1og%+ak$%%Ni%Gi;>|cpn)JBhO$)I%M;x9O$lMBVA+$Eu{uRV)6ZR%0 zFWM_j0Nu`{7FEI9!0|SN)ODjun3Z-Za~r%_&@QB12Dy>qV%l}o?;X(lX_ufCfi7g` zgZd#J)=JQD`Ng!e88jXPM0eV0*2<)@Y4sVmkoINyrL;?F)XLw&dzid}7g~MVK_cO2 zX-CsAOAznHv;!HNL2pUXwCx#NNJEOPoZ@BVx0$@+0O@ZTQz>UEEkix;eY3@!;!CbW91p$dQhWTS_>%i6PJ!oxeQEy$?^@Py&%-rN zC1~8I^t{Za*8Z>Pzbf|P4uP#fCA96jO5#hlOH6Meh1$F54O=Z~(({rpajHs!kf`jT{f>#Z(?Q5m%+D4eZ6WXeyT`;ZCJ%7026ynyNDlPa{+7 zIo^B9tW-WO1+*xY=Q!PiN>%&RCi$$$9W&xTp>dyz-$gvNPrcB#a?ZH-mAY9aXzQ5T zC7+x7Kx)TYQbZcv?@V2!65`Eex`Os%zQK#T8mZiJ@qNTo&#Tmq3FH-aNWS!|D#`V! zBN9iEhLz?FYnU6Ul|+&kX-kPje0xvzv-Uk`9+jXu*?RzOyUg@6Xt$Uapw0_aLY-$5 zNhZmca!(~eNKxbA*-6R`j&}+g=YkDx2V-5J6b9u~+8*|aDa&mEqd#hIK8-0d0x(4Coi2 zMHnSKt3)np?Vm-wdDa=wo|Tle?N*!@=uDTIVWo8lt9;4?E3FJzR?+7fv8CG0`VpAbeQ?@31gN%;Pe%zY~TP??py zg>!xc@irwaM~X*~%Ldl|3EH}Zxk!N-oQ!$im*1*{iA2y)3wZJFNAmuJVMNf>oCiVM zl~4$65VWnF7NcrelHfqvWy&neeA0v;A>JiR1lnZ8E8=+Pq2*f|p;61-tR+E9vCwq{ zdNSETnluTT)pC{yn#5YIRO5_VuqsQng5V5rT3mUguC(k#+7~d#QE$+5hgb2`VHj(C&Jrx9;D;*DdC+E!;i1noK5>KtW$3Ta=F1~_Z!I#&7(($;XiA0R)B z7~;JMt&$1%1f8WSA(ve9Ja}G(ZrN!b}(i z(M3;p<9(kRX^8)-2^5ic8B71P7whe_p06QB)Iyh+@r;y~drcKn?VZ7{sKNtE+o zcs-ooE6_@l+?2C8KPe?C1M~{KF4c>)PV!?%w-KMYM`ZL&BV zKMbIQHX0fvP#&}hms_lO9cUHG=n~(-9>`%(KwN~qkm>lfYu-*_7JlHw0L>`<4c;~) z(d}sF{ET9!d8v4MYj^%fyO6<2x>-|DO==0(lsn3hCbu@5Vyo2Gmr4lrCmTW^$ zUSVw;dh&fD?6>))s;XSt9-#X?Y%qzlMfchSE+$i!z;pwvW>~qNg>m zT6l9g9_EyuM_+6}e(D^$kGd4H_Y?U|N3Je}6j$Z*4v#JX`bggGaO-G{K9JvYoMi7c zq_uF0t8%@=pu0g{XzDD%{R4-Oy?ArQ9?+qm>G`?6jWs;`v^O*1O2*#E^c!eA(!y_{ zHL&&>G<7ULgQkup`c$`qy%+GilH413hA|Yof75v%-}CGz*}D-#jDCQ&5!xXp%nkbi zm7uAkwN$p+cj&PG(S3B?9Ni42Y{zt+n<+;(K}W~i_mwi8MZzc`UXFc{t_Jjs?6ohD zF80xEJFSGz${*_7_BkMY-`9=O*+`QV`vsi^v=B5-N43J;8GD_4o4m;HuuL znkz-@r7~ui__Tb@UL@~j?=^eAydAVis*z^M_lcBQ(j zU$v=YiPCJ%oHM*KoJfl&1Gd{tt)QDs$k}#FMz3KH+Zu77OskX^UW_8NNkWcv(N<5I zm}xslpo!Fv#pc%al9N{InM=A8Ou2>?u^-%N>iaxY3h+T z4BmwjuKy%6wE0{soK0`yk~ zPh_RnnV)6OfR&|Bk*%JU~|WDp_e1D+}1xvGNY{b8H`D`!8gLX>8T99%cRmS=~?Aa#{NM%v;Fn zPm>jvla(#Z-E7y8700kXjI3cX>n5_&C(KW={WV#U73pDC4zTh*^V`f$=ASX&BP;)i ztfaQ+7VC+ue86@DEAz?fzGUWlivK|@JwjHWMpiyaR`(lbiLA)ID!xRlcaxR=ooyyr zp^WVjvT`$7@o~1_A}bwZT`fytWj1@qv0h5nP(s$iv%~NfvBaeqpJ3idR^&M1Hs+s_ z6>g9<{FRlt%mrjEzauNK6>l>SX75q5rWtIXBrC5YE9_#so2)*S?Ym^f?abV7`cuR@ z9ufI2vB13{e2$1OGfC$Su6C7-Oo<%)V%R^7smC ziDf>@HCf#&tap$#oF{AH+@uC#-IHu5k(Ja^dWl6IeTmyEu_8rDm#4G+C$jov zvQjNsv52h6PF5$A6}Ug8o6Hl)N?b#M`$te$oBizNwL*G^SjW1Ba})lR6yu-RZX#<+ z;W#{k@-bp*Br9K#mG6-ixhKV!S?98Jxx@nJqobLl=UZEn@DA($O;);2R=1g%t=vv5 zewVB~koAw4=dyj8tX^&1cZdb<2WcL$iQ6USvCgZR_yc0`*K7;ea;?OCV#&o;%_osq zc%3aDO@i8=>Wak2i?EEmVmDi^m6*kBB`Z-WdNq}_j})2bmGm01ZV4-=*m4i+-Xa!u zkTt6Ff%6pJBt_)DHKY;i(#h)Yla=_0mejG!W<{NuTsNVd6k#jJ|1YxQ$E>Km%3d+T z%0{x5iyVjhQ$LM$p0|>k1Lv&gUK0Knd&jXo&DKIz`Y~JXd5QZ?`XB6lhV@^OmG#W) z*&ZY-{*akxvKV9~N><<&iCnIr_Ua9e`6=6{*bZe|LssDVDe-Y3EF?u|V|$CN_)E4t z-^3HFKSWkv2P+B_Sy@WfbcHRCnmCGB{5DyM`$yW%yo)UV<5EecR%5eAR=5;t6|uy9 zD8Izay6`_a4tvFiiNzUgzr&VG7hmK!d=$%DnCsbcZKbKidM{b=CuDWpBHc-1xs@%i zt9o^At7E}OknRzVe~hetGFj;;S%K$($bBX89Fr?KCXcT?gjhTds}}@fJ6(Ur5pM(P3!dRQJe=6IfSg*AZ5h zkd+Rxok`YkmaOh=vf??ivX5;AS;NPy^ZF_95vpgesE)!)_I?JdQ(GthGbu8Ui=fWf z*`(+?*gnQq?WNxli#+RuLBtY|k@yBPuanYq9Dh1nR>ZsPJPM{O*2%Yt<@;pyYss2VkrjH_sz>N7V%fmY5A`0JP z{S~&8$jW@w7>bCEJok)hoF_>!@Vvt90`jV(e4JRvt(N&%6M2T16G=C5txO+s+G375 zmaN%CR_q~bC?zYbW6LvKE+E#cM=q~y`thuAPwKgih6SYPxODw$W*$>XJ@(Z(nLxVC zqb46^R{N0Wt-gsQaf+m&YT43zw&caEHkSdDU?a?iUFwfNY( z9kqDLJ72DZUWvMV>0Klb1MiX_g1sm|j2u>=7WcfX<$prG_MsMG?-kUd(YqA?5~tC7 z5T$+Ty{=0|O}FUMP?z(%bl6+Ep~`jd5#2*bdq+1)x!^sn`;GFx*Qfifa>HA%?~gj3 z(U&Se^{x}LxwKp=ZOtY55%lV8d4dx3K82E-V5g#IC&<&-QY|K<77_1G)S}tDM4rPj zU*J?fQ0}g|i@JQ~U4^>%y=zeyzjuQ!U-`;APgkJa_iok|GWTOHLMspGieZ=QN|^gI zmm-ISx-zyyl`p(|(T?Ud>rrdufLj0DyG^JP>d=Ns!W=GFDox$v(S<_p5= z%fb(o|H6ED4fR?qY)710;TY>*vh|S_4VV?v#Uy3pnt+(De7q)%_a?f%3&c^%Zm)nl z(>>m^xHs0ZW}>(cbuozlh1yxg2IY)*kNBC=xW*;f6yKUgX&vPz?ZVh>lFp*X>!cg7 z@U37rA!$GaarhdG9x`^uY*p8L`} z7o+&GcPm=o;w{vTL|>L-EdSekO7{rTR$)Z{)4LKQdds^5ee;F427R;LJ3^1yiyjy# z3|78ca{&Dm@NN_qp)QBfD{bCd^h(d_v%*EV z#2Lz4Yp&4~q&2hY`Q#6eh&cLIyTyG_c8dF1{~u5mh*#MASGEm`Z*?JhsAF{@J+E3b zTPi_cu9Kc-UPrSUz16d3uk<_0O*%uV&~xoLQqX5VJR-kFd7|gq*PKMpwXQjhp1bcY z;XWIuGosGp&|4jt^O@l7y5X=BbvSBP??DgUTyswM2-;YK9{Sw75q;xda{+yGea&V4 z7;2IJdsK>k8CmpG9qKhgn1d86Fq6VR%)m?vulfI2x)(T`%Kd-fYpvOPX3v-;CLAOo zrz0_@NisvyVD9%Bb2nzj-0ZpEFCUcdM2Icx9Pv*+`Do@YJ#S!>1!BMpz=M&oQ_ z1CIAMcIZ*`s>buiIXu4}z3Te(N+i0hdAs>HzA_R$-@FTnemXrAz3SR@Uu8K)Sq3u2 z#@Xgu_O0oO>|xVqFw5pZ{;n=OiTBvg!suw6&HNy~ds9Cp6o_n2H19<=SDWLJ$A#%V z9sTiD2OI<7ImbY??^Sy+JnX>C2p`5*wV+r1-psk-*mUM}wE2t^qp$fKayr`F?&NGR zn>jTHGNn%dCAE;acZ%aB1J`&L5+ zlK5$Q7W&rJ=0&_8ugMoleBHbhee1pEZ5Ute(`%Urp7}b)+AQXQHhNXV9_G=U!zA!k zC+t5PThObnH-nyu!o6&&2Wg z!}JMA?qc&QBzLBHt;1>m*j$SIPBdE>eW&5y?Bh*_qqluxdZgnn`<>bOSx?kH)_^Hk%eBxoZu((a)|o?BI;ma0Dq{XucCn$WfzgWR{ED8`7yayP z<2I$+Q@Qp;4?`lzb$`<`bF5+1FE*di7+hlCY-%@G+U-rN(ZkNxA6BCN<^fbJ)chX| zHh;$RcbO;cKbn0pPOsM=wpgXtt;zf<3*WcVkG-vFqsNPI3(vnz+Z`6%}_Y7&LFcRP3&jen~-sr)3&A|oZ0IaaXdCG~-rgSG6~7W`C+%h@Nz^DF^fW$%fa_pKdkm!nFmXsSv&DWK$`6)rU>h z9*ga(wKveWzH4ek-?~^=@9@Mc%yf9+UCqIa-_mdfGk#0sG4#13^)B?e6HPnS=jLN< ze%E*eW3#QX6}|0j(;{T{Rnt<;XV;rnAi1yVS763J+O!I@+KHyMNb&imL7efCz(Yvx zXwwWPS38KKvMCtJwKgtha<$b+Zg0~tsMGr7ifNbd8-jY#g7#w|$hJVqRntJ#X= zuGWV#x!PnT*V?cF$(?F)Gr1;DCASjEwbpSa+uXRF$u*2&a*ew@Ua@c1h9JN8M)WQF zd~E>o`?FyT^J|=|{0=a`rg_Nk=h|M(uc06EJJNU<`JJlSg#7-f9mM<^Tae$4#*@fz zcijZ8ff_N}*~c2$3+Fdtwo^V28A0~Vn(hYXJ~+<)xw@KJ)&%mbfFs#>cL$YaKC=9- zaU`-lUtf+aZ#GtAoZhT;Gt27J>V^5p@q@x9Y-q4sE!_ba4df zRv+crvvx2NJ=Mr{@u!V0WO=NyGsk9a8j?F*pMvCmtItAmKi8Baxr;TmNbX`yGm<-1 zvlz*pt!{LT=BP(-l-konaNVSZJs)fWdbzxox-591X1 z)pcclID-7@S24f(wal-fJM*hL%>3#%Fu#WTm|uf~`PFY`epQE=U(H$Ohc@!7^R{$qoyh#E zMlrwoL(H%KDD$g7!TcJAGQaxM%&&SJ@_V^{CiAPm$o!Cr@~d3J{2=nHXlH(vo0uPt zAiuhJ=2vx^`PFe%a=zv~^Q&uTepP3gUu8b?t2)j6>av($T?+H7ImrBQ1o_qFF~7QE z=2v%^`Bg4qes$%{ukH-S~!^?q%QHvC7@j$5u;R5Z)kJdUt6GozTB=uZ=BwC`{7%%&<;+(T?-*=< zSGyITnkdd9)%?`dR=t*Ik(w0Ff;Cw@i`3*H%daZ)kmb>uVq|%urW{#5UwxJ-B8`WU z;(^*(OtI_)QoL3<2q|8!`HLx525>!G>w_%+UF$%W7pjVxW!X_?iRWWIj7O1Z`B3)4 zifr`4KWn=n(b=`#k?8)~KJ0}x{DkplZGR+swd^z!y<9evy|BCsay?#qA9Jl;hFrJQ z79-amYlkA&8?}Mxc{eLNavj8;S1}N|o+&%ao>wu@1M896QOvb=G1n~RU6AgX+ObG? zbL|A?S{uppO4(mJqr~%!Qk#NYkJe@(*B@*1kn8cXL&)_)xes!^P~IO|9xl6xEKir; zhZOgeT|W|rkgnPv4x zW?6okSypdnmem}~7b^QQ%hCnR63<7LcobQdo@AEge$29Z7qhJ1%Pgx8Fw1HmW?6lh zS(b8LbGbB-S(ff%menolqtVQ=rZ2OsKFKVr@wwH$S#clxXw^|>S$2e3mSSDQHnXfg zr#>3WEK7GW%j$M!S*_JgjlnESH!{l_Pi9%;%PecUGRvBt%(8SYvn<`tEKB*x<8bL} zW?9<8EKAoh%Tk?}OV2RN(v8frbPKY4v3fPLtUAFgk(IKnYGalqq0F*u0kf>iXO?Bl zm}ONZv&5)JmgNVTWeL{LdOotmqsX#k39~HS!z`=nnPt@sW?40xSypammR0kZWl1!% zbf00CCH2g*Y9X_%wwPtrL1tOSdGSWoa%Nf9!YnHnFw4^U%(5hrS(eUcmQ^d6Wz{Wa zSyIm|tJW~fs`c!bRhyV)Nj|fz+R7}eb}-AT-ORFTAG0h8W|k$D%(7%2vn)wtmL&_B zWl1)(ELp;SS+bm2mgFoYJ%m|S zW-!aj9A;Tr$SmExm}O-tv#hLUmX(dnvT`P~oW^s{1@|Ck=^n!@-JO}GJA_%f{g|aY zoLRbKnWeiIvvl`kmK7_RWko2nL{`eOBA!_mZ(x@0E6lQdKeH^sb$R;+Mm@4DKg29c zM={IdwagOFN0xXLSr-4rEZzRhvitiacgne1ch4bYYei z-I--YA7)vM6_9S(eRZmhN-R zvM7sL;`zuDk0Q&Wjm)yRm06Z8W|n3AG_${qpJpzX9%YtgtC?jHS3sAG9L%z4F|#aN z$1KbHGRv}a%(84Fvn<=fET>&#mZfW%W$_wjSyav}i`OvAvhB>WyeG3PTFflVb}`E` z?$LZ#c7RzH&0>~ihnZzr3$rXc$t=sxFw3G8W?8g=Sr)BkmPNJ9vS=N%ENW(!McT_P z+QKZ0W--g6xy-Vx3$rZUz$}rKvMk-oEDL)w%i=6%S&Drvyx(GGiBXR%OM{u^w3W=V zuq(5~^N}SUMV5tHO%^R>mZhQ0vNW1mmL@XGk_F7NG>usn_GOj@TbN~GD6=fhW|pM~ znPurpW?7ogEKA+YvUnY{EXiV)MH$Spa3HfR%3zkImCUkqAG0hBWtOG&%(8R_vn<8m z5wUL;j$oFh^O$AnLS|XIgjtp@XO@M&%(8GCvn)(wmW6|vWnnh6Ec9oVh5784g>Gh9 zID%Og1~bb-gISj3F-v5{EWdXj!QRb{{3FPcX4}{DZ(#@LhI==%yyl*bEcdx*Aj@+_ zBar3Q{2g4A7Y#<1d=yz0^x__2p%1eBqbSkrV#m1mA<>!cd1hC&yGcKJr|d3!$e!|V z_ID*2W-qmSOYHs>Wup%7xEG=h?FH4WK>jUM;HsP1e^Jti>`#;wGWEiEq<*dV8dERi zNZ;;Wf<%wFmm|xI#f`}Ff_o*hJUMM0vOMixgDlUu+Z@=-EHaSdiQ;gi_*;G&Qv9!b zJyN_`=s=2lORACLPwq`f@w9s@Qe5ZW!4!)|V3!9eImhzd8joNH@jLfe?zg&IkmdEl z6YQ7y_hG+lzk32Ro61%9jlygAnXbDdu~Yao&v7U5;Lu>aMl! zu&=nAvFmlijooR!n$?Ii{w(yxr`@>s7Zp3`F%$1$0ej(8KlH*s-HSbz*x$OBIV}4d z_v;Q1`?G?14u`$fy&5a_MQ&t%g;T9BOUAPb8dX@F}#X*(B9y z=VQF%QS{NgTF#wQ*KoWSr*OO%bN6-wcrTvI z@m{=u$9vucj`!ka9Ph=ibG#R?=6Ek&$MK%mhvPjjp5r~Q7@vo}oi-1jhbYE= z-YAa!ymF5Hys;end9@t-dCeUAc@sGH^CCI+r!7>kt>)NAZlk&Hofpe}@2Lxr@ztr= z!^IIlWPEArVr1NwcaeLz#j!~DQr<}Je&+`p*g3^H8lQY~@{reOZ21JCnA zieFEC-HfomEUGjkWt7CO_|#p^H%!EE;9yf&V)5}M=V>E?YVqU+T*9Y_Z`5awyosxxl{c3gq=Jl&NZRSb4eM$=E>??T_c%`D?0A_AHAC#bu=Vo6=@Yv0ZFt?_xihzZ)4pm0O9m1l!%DpS)9cmpx=p ziBUY|EY=cyes77ffmg#0VeSl8CU-W*#?{>O92;31Fg99q=W%T0@iXw=++!FUzvnLG z4q?^?jF4;DySYP%HmZ@`ig9u?cL~P{j$nr{tD56vGItVAHle=IfT%6n;z5LYVq3Gpjv;4S2n7baSf1SIDXUg2I z$bK(oq>t?5xjWF;+a~ux9bTT?pM5=dANzW?ffWdTQrzQ$87B92rk?Q^XPC($Nd0nd zAErJzmNQK5eVk!(`*ViLp34~~;~HleJRdy_kD>yZk*q-049+mQLy`UL+(6DSxuZD4 z6J(-HlF4$COp&P)GgZb4RwHvLs&PDb0;`d` zg)>#|T+URvk({Y=<2h4JUc{LyCqxw(hYFm@^kD_E#&M>~P2o(HyOC$yOzuOT$<5+S zm7B+zDz}(3Rc0T~RJrAxsd8&MQ{^^urplegnJVKVXR6HmI8$Ygjx)2vKRI4d(b7H0tDV~odHc6Y`M)aPQJgY!?;UX+cZ zGHKUPle3xgjT-!Xe{udX42*{x8Jk##46Io2b8JQh&S&jH1#V{ULL<`nsyDjp2ahNw$D$_$INp%!P>_+P@`&{l$tFPTYc?9OA_Q_p&#?4s6GcKNkGcKHEU(Gs$YFy3LmFbLD zo_*6dvL=(UAB^X>a@NYeh&ue9Js)+rI%Ny$a5-}hDsVP;KPqrGV-r&UV@fenZ_ho1 z)Gwu-#X0!T^k&Ch_}P~_?#BDaHAM9FMO;^AZRB-V9LMT1;|Rx0#xc%RxwtBUSKw8N zOS#uDPR{3|8b;ULvp64L&z#Siq+LTzkef4??~~_duI<`f!A>HdUj?d&k||l44ftK99+-NjOO+1^s}s9 zS}3Y_H8T{|L&~V${*1lmSi?x&#p~IbiKyGn%z?a~opBiFhqGxqKV)X}dUj?#uV-hv zc|AL|9ku!+Gnm)2(>!^W$lzH5UuA8xZ{!qP|CXOgyp!}0>vKG(#o8(VBX`L!$d0jl?46lo4oZ}fLqn&4%^ozVMo^gXSTc*J?Or|H#BssWG%jlm|j?YsA zb830NMov8L*XWcpm-lO+eXrVsjV?LOxL<=lWZX9S0IJ$22Y0&I?U}x+YFAXXd(JG> z^Gs$>RPsn>U);fg*KrOu?#;}{nFqDNnFq=6%6g{Th`?VIzmhf2u%w4{NT>9aUeYDK zrH||+edTSkv%Fn)k@!4?*Y7I3Nk4g~>@IuAp0c;>Bkz)Z<=wKMyho0+Z=@!3#!f+B zv#(9!y8Cjv_I@W}@7M6nZb4tWmOc-2^^Hk4I9KEN4jsqrFliZP>6@wZF-yOieG)j=w<8E-8|Q&tT)H;dzlf2Gi?;^Or%k=K*#@U#Af5l82V{Eds$jM#{aFf za`vyZ#Tdz#GhEhQjJkyu<`KA$dOX3e#i+iVeGa4gr|fo&>Py+nFp_U%-#}k5rug!+ z^%TP~T#m6@QkLVK<~xP!i65qP#rXVbN>7eS{6tY)JtS9h^#HMYNIJk&k{5FIfFoEv zqz~n6IcX8;Ob%09-h0B$8pY_w3Dlc^ifznOrD&EY@SLF#Oi_V zZqiTQDZ9%avZuuAA$2`g4}5-aiSr&_4XcNAp7*Y&Pr!MvC}l3rdo)(Y$#|I{6J?T2 zmXlM{g2XVp zA`>HqYmsz6)-bV>Jv(^|dv@AF)-ZV&*CJ`lxfV%Vf=XVPbc<_|L^u04o{zN%9z{J9 zcd(vG7r7QmTZww6WiCNIXQ!<}J=yLi{p6jpyX+x*N~~SdSWiB`w?sWt&!C=frfot! zuO`-W9h147>llc248%GHVjTmqj)7RmK&)dR)-e$47>IQY#5x9I9h2CV^-NredLB>P zs(KFLIwtKF*D+~3xQK4{8X)|hgCUFL9n6#Pen6!Od$D|M9Y@EpZ zNzSAlDA`q@CwFChZE>F^TzH$0RQ2Iwo-w*D;B+ zxsFNPirVf;oX2%c;tteycj9iYV-go~9h0~OwS6&Dy9KG+S=;1Ys4ddtY9@1eM{QF_ zvbOO{Slh%F);47mYnyn6wN2T<+NNw}ZIk=4w($#DTRb1N#iOWgd@F04*qyaa*^SyZ zr*ox0J7phg%XT;EC-0QqWe?d?qP8gqQCmL0w?u7IcrV_YDMwh__~oo^`aaecqP7sV zg{Un=Z6Rt4QCo=GLev(bwh*<2sBQcR);4}OYnyV6wM~s>ZBzTPwkfTwZAu$!n>3iU zoz%kGCLCpL<5#k_2}fDml=G}@YB*~fznis9xx(6}++uB0E!H+3_tIjHbFsFmomtyd zKh`$27i$~O`-3jT?_h1?IeHJruVHQDxf0qF&;845@vW?Fd>d;U&;83|@!Y?BF=Y+M z?WB{eZQ>c!7U{9J>HCaWqrbf*^%h3&p``w-aO_r8_+kQbPhPi!=*dMR-}sv42zLRFtmy@IN;-A($*J7ss-L-v%oLm_bys>)hmD3fHeoFr3Zs>FQ| zaTihHqe2W{4{tjaWn6Oh;y;RNnKds*fv!7Wa2j72N8dR_dz7| zWrY)WqP8a!_n@{PChq5b5V47@aN;5CKwXSIhzhqS9z}&OC7wWqTN1{h!Yzp-QQ;$T zIjHc7q&}$d@x;@pa9f&%3b!SW;k^%uXHn;`5-+08*AuUy&R@k{L!FN%@~ZubBwn?D zJI#+3j&q^HND>uBGDZR_{7T9g&d*7WtZ+1U{;$RL1XFMNc7>}abF@rdUV^cVWlU%6w@|18?dvVH0RGaN?(ofzgyUQN3r^Fae@ZFkz)k(VdFDZnhI{FGnby7G-by6%xby6}%by5aLbu`b(7h-s)?uD3M9M#cn z9Mv)XII5%1b5zF+;;4=p!ciT4g`+z9mPU05M|I*r)qF5&jyzFw2-ZBIGi#pUV$EYqS@XzatT~>Kn&VN_9RK<#Y95`(nkV?7<}W9g zqUJ9o=b+|ncawhdPT5`dkUb@8p3o09=kt3@)I2_oHBUOvnkNim&Cy29BimSWw4?1O zlM7LE8Y|;uyiAaZGD#-ONis#IO4K}ZHftW`$C@V$Va*d(u;vM+ta(BJYo0KYHIJ!h z&Ep5N=F#`D=8@-F^XU6n^Mo<1dBRfGJc?IR&Lo7e<_X~(=LxZ_d6dPPCnU4x2^p+; zLJn)5z`f_=kw;kbsLrf;)F9S8@(OF7(8!uc-eS$80$B5?k*s-?#hORCSo8Q>ta-dY zYaTNKHAkMPIr1@*IL;ILvgUD%So84nta;Q3);z8=YaTU@HIM7Xn#cLE=FyF;d3YOZ zj_0H1coa2{7{i)Jmb2z@{ZR9&#F?o1?6^UwIosW&pS)9cmpx=piJHd^LCyL6-V!yB z&1cOMxNG`mTmWm1HfkPzg*8Vz+OABTgPPM=87Jdqf=rZ2GFeWNDKb@}=HUxj^N4<| zdE7|WJZ>#(9>?p5KgNw=&ErB?^C;GIl?_ixB!}_z`P5Q|@ zWp~*__LQi94C~M5_m-%CwCW!}pY@MX{n1AKLsfsYqwTrzt5AO$E8}FmOpu8(NhZrl zGDW6J)IU`94^#bPRR0*&KSuSBQT=07{|MDTTJ;ZC{Xe-Y~+ruv7e{$Z+rnCc&<`iH6hVIB2f z%KAsE{t>J{vPJ!om66QKN2&4?RQWJfK1!7jQ{|&ndHg%m82RC<`~+1V&qw9)C@Mcu zl@C?rqx!MtQG>V}95sZy!IAu}vNxjwSg{FLOw1{-Xprj`%=_n-~rKF>jbkrF0 zvYvTGUX|A*uGrueZb)3~KpR&-;Qz_Lq)jmxsVR8|-eX~8LX1p^t02%z;`%t+xPA?N zWGCq>Z<84LQ6bjtYIl)$NX!Lc>I;$T3!&-@6K+|*@KMePq3RJ)>Jd@u5fjxTqSPaz z)FYzQBPOaxM5#wasYgVqM?|Sd#IEIx5X;|}xDdO6GeYcU_6Ud`F;P8YqI$$c^@xe; z5fjxTCaOnFRF8<=#vT!=9)T*)W-o}C&-p*3jr0FRf6o6AF3$fGgE{|4_;LP^=*(Ua zTFv=Cq?PkOo{wIDN6`z$kL3J6p_uc3L@)LN%%bQ85rfzZ!g-g*n-N3U3qsDb7esSk z`f|uT^98g|n~Nm&6Vb+=DSSy{HXh&0{FmBGC1&P`0M5)2Ty<}WsOHQZG1A0)z*n7- zzsWZFyF4rZkX)ObHvg38~|3T4&r}6^gD=t2hr~!`W-~SgXnh<{SKnvLG(L_eh1O-BF3=ap^bhA(eGeac-rbF z{Ur8SaiqKKDY3hTBfVsAd6(=f@0R`KJ+i+XAP35O6YmA6K7b@sXbrfY#8p( zv*CoHJR62w;RwL{Y~`aI2NQLXCjI1{vb*dd zd&*w2x9r2$H&HQ&ia}HiqGAvggQyrBAW<>2Q89>$K~xN4M-cu)4wm=JA@Tt^R1TB= z5__`vssQ<*43tP0$4AJAFC1BFJRWg{*W7Rfzw;V#i*s0H zDX+PWGkDDno|NcCcs^z_h?xww$=~H!iJ1(?F_S^eWDqkM#7qV;lflb+pP0$;ZZVUE zjloRTJraMp$QT?MfSJrUqz`7YUXd=$WPXvIF_Za4`f(=1^D&b}R$I6f71@h3S=boN zWW6K%aVEoYhtWB55N9&9y`;-Xi5$Y23`cxqC+W-Y#hDDpF^56SVGwf|#2gmBjB^;o z90u={n8VP<90oCmLCj$ga~Q;kfEW=FBLZSXK#U03Ut&a{jS&GcA|OTt{JX@6c-)WI zDxq6PYVm$Xaz4Xz=F4TQ1?Mol&sHAcj0K$1BLW= zj^zyCe2l%6*CEFloRh+r@oMBa!}!q%vagJri)$e?%lgdF4_?xoFC*qz%d=-BquPoGI zme{eOK4z&bljX8PR?2ESEwqbSBWq=ytd|Y4(ViaK-E6WOLi?D_a+aMD+TVQIt_{5p zS2bfphnn-z4m20r--qopU$W1I?!tA>J)xs8R{jyb$b7|)2pwy#u!}<{;Pcp=&`4bW zoD^nY^pu6ho11KRc#ipz9Uq=yeysKuyE?qk{6u~#x5%%gTfXdeDu zp~<1eSbyBay7wg7wdOB&Q)n|rE#LVC{VW&d6?=B*Ec2@UXV_MZzy6_fG5#JLUyJc~ zf9L{?zk5R$WBd&XU54=&FxrhV*EP6>V{SsQWf}hC7Gtyx4SgMd?ft3H)wrMY#N+$0 zZU_opXLYil4Bd#e0$vRx?A}oR25M&LcHG;^=k(-v#})bmp?h(KzA^Lwt_goTcDMDI zofLZ5dK}Meu|ni{IYCa8p)$;#8G6zRx2KOUw<2URYI?>>v9m+ZS*du<8&;Z3M;+U( zEVU=IMplm95aw&;$~=2Un5Q*W?E?GBu&!2-{d8DQYnr`1tglsVzZo{rsS#N!U z_6%#Q+-^UGwf$%Ii(&JyrWqNA>znqLuqD<3`=4RUt;6=bu$9&i_H$utte@zX5tguZjUF~&vCEAcVP!TW@Ao0;_-~VD(slYBKzmC zR$NQ>32Vdt@0GFpJYKTf!_MQ*t%YG%JpP4oz06}NlEKcu;TvvwaHZwK3hDZz3ve&) z)8U;R9`@{TKirG^%=iP?HJVA0yOR#UA2pV6VWr)U)I&O?Q{t*Pj(bU$^p-vnf5Qfk`pVm6XL-BqBJYr0WjE<3@08tT57|@pmVM-1 zvah^b_LKL>{_+9)*PuNPZ^|%kSk8`GY(vf0Qlq zCwWXBmnY=U@}&Glw#rlTv^*n!lWp>Mc~<@*FH@8Kq}gOYX*StUnoahQW|KXn*<=rC z#=o9L*+ZI5_K;?iJ*3%W4{63;C3+5aDd}T!oQ$?FjK7FoDjF-}WV}p}i84th%Skdt zrpf}n+BAG}Ib^zJi7b_6vRqckN?9eVWsR(rb+TSINcmC56kcEqvMa7N90lYqim5s$z$@k{6)6PQ?lJYIevwO zdpv@+TKHQ*aIm~z4v`PYp?c;pwf$v)d{73;hvW$PupB8Lk+@qJuOB3XCH}4xj*Q{Y zWj!j#%E#n5`M3;`Pf$GDgPAI2kV!WTH%x$#RlRk*P9GrppYO zDYIm@oFa2%uFR8DWxg!1+aD>k3iY!W$zmO!rgn+cShh5lEsbTXLdPp*wXBh~vQE~^ z2H7N=<#ahiK4Jek{wrT)rw-$k*iS@(sCCzA0D9x8!R1wp=6Mk!$6f5 z?{{kD;|%0`aBBVI)cVKyu#SwBkH}FnNCwL>@-d!Io%m~d;}1DC&p1QW*4*OM+=BHi zelDzMspb}}YT4G@f>kZsnp>QjTb!C(oSIvlnp>QjTd=m}Gc~t3HMcl5w>UMoV9m=% zHMd~R%eLkgr{)&tWF65AgH?6TDjB2%O z?485zGiuew@1l;{j9rhSA?4Wh*gtBUv0KN#lzZe?at6c|!gyPs(3pt2`x7%U|u!M&}r3)c#Gj$=~H!`G-7b zzcXs5ahYwbY7dOsgH-qJ^QlD_gb*;(FhuOGD^*K>XzbqLpU{&>)hJK?U4 zI*L7`e~mg}PP4Ovk6|@ROJu1mljX8PR>~?_Eo)@0tdsS!L1Gta)M>0ni$|TsE)t!t z<1^$Fa;AJz&XP~br|s-f*UUL;KZEu~bFSLY%6alRIbY9Qp!V}}p|#qyECqlsUSe`LdlddJxvC^c6cLD9c=~_N#IQjyTNM)PCLGK5VG@hT1Fbf}m#e zO|@6)_204+hXtCe)qY#9k?+X0u+@B5?RD}!xn906H^>j=%aC9%MVd+u%qim5s z$z$@kJYly4Z^aeAW5Io~@0Jy`4*PC}L0!yNJ$g!>QH?IBY8Pd@y*X&RdBuJrs5^Gz zzIrGNJ8}0sbO1YXfullrO>gKR?!*Q4!A@MCp#Ip2>;GUScH+7P-N&6c|4Qt{`34Qe zPF(k(Kh`xNQM}SaI{w!Sa4NL_Q#g@;zI_)b^JF@F!*Zm2M2?a{ zGFalbtnnSkunJgl)3Flw{RL%N?%M#))ONhHHA?~t-xXTjaE=znz++~S2?y?NZvv8Ls+PKRys2F>TIYG79TcqhY zQf_7F(M*}8tZW^bB6IM}x!7~0d2kl?9NEs-qXqW*paocWGl3#mtmD(vE|HqG zuw}gK4-5TU5Gu<9YO1?f7)-44#9f- z#nEH19%p-@d_gXfFUrO8CAmZ{l`qR>_Nt(bSe3KaEtjv#74kLtx_m>fv_B2nf<0C~ zf0cYou9k1hHS!&~R=z9O$@k=X`M%sBKad;chjNqrNN$!N+e-sRSzFZpL~fOz%5C;< zLA$Uvr`zpWLx*66&h`%bZ$W#l&+St|2e3}3|B<`o7jn1!Qtpvo$-VaJpu^bzrTZjz zyjrkwr`+*s!M-r%j#rDt9j_LPJ67o!qj7e_e)QYH{EgwO!Jb&fV+>*y|F2+Q>`HGA?uu*j*ZpT;S9*VNPwYy6 z8Qd4U(sd8#U|0IP;DH`X?T-T@u`6u^55}(ao?-kOV+V)#!>)8ius?RC%YsK>SGpoN z7`xIX5P$t6cpR?hpAQbj_54f0(YT&}dRU~x$Nptlpre!YwVw=5#HR<^S>7)3mpOye z@achegW0?WH#7iu0bCk3%7Nbq49>^z&E>+!jVMh5z~>&UAMm+{ub>$opL_V4 zxQ`x3(x8RUJy<#5a}Ukr`T?JN_~>M*86KZ|uztYj9v;u-$^oB#ux7xO0IbLG8HmR< zpW`zSW_WxC!Yqo?`k<{p39oe|z75mVusrx7)y{>p`Uk?)!lFbPfF_?)yMHK;pg+w9&`m2$^LV z_vp9m22~r=bOhJW@o0%Gm1VMAR!Cg6#&fDAuC${)$MEsbGoCf3`WG9|8zcS8@o#Jw z`qvsuvD#`jFr&a%5v%XID#<_UB)>7#k`{mZH#f)N&3p$%1{|5!)1hwluiM1C*x&;Oq5A7Sx%BEGF7I@beSPDWtN<5821KS*=kRbIWkw~$*D457T^rE%qmp7 zNEXX!vP70bf2&NE%L*OmEQEVtWR0wqb+TSI$R^n=r^^}g2{_Q=%rnq>QsUlR|JSXj z)SfM$mUHAYa;|(<&XfOygRKSfdAU%&AQ#COFZk($D1Q_MiUi@OhE`NA8ke$ldZwxkr8__o7#v z%8zmtdM_wtDRLH?*-+9H3F$K-K&LjEjI%3m};oWjr8*E+4A^jEddaE8S7 z9@-{?*s- zJB{A{dvWh%SN{XJce1DdVfW%3(jlGFQ+i34^p-xd zlk}Ch$@EAqyJTN^x9lhHk@#(b0Y{8M#@K<$#^2?A z@*l>qff>gAY7ddMIh3LN!{THJDLi9w4J_ykV;lp~Jk@68aN(RYbIa-d9 zkIJ#~G5NR*k>lkAy`PCPREEiL86hKOl#G@!GFHaPc$pv*Ws*#mlVplam1#0vX2?vL zWpo>`34cO{8Rp1bS*UkgB#R|_A~Hcwgy@M7JrSZOLi9w4o(RzsA^IRh?}PPvg$CIu zn`E<`E@#LmVojemMh-ZS&%SIwBj?Izkl2l;6rj@;iB0elL&6ALLPqvoyX7&e9NPX^68l z#912REDdp%h9@P?(rCBJQxfND9QhR&qtC4%t<9lYa6}*tryjKpAf0y^kf5^e| zemO)wAcx9f(q9gj0rEi^C?Ap|_c&XIHFvvQt%PR^GL z&)B%rUzazLcS*7kZ;OW`Yx;G+j5P3 zN3NCcO8gaV{4DRu_40kWL4F`N>NP)97@{#$OBpUEBabGcLg zNA8ke$ldZwxkr8__u9V??2P*cn8|+mjl_K0^R$Wi7JhG^A6SU{2Ix`wqim5s$z$@k z{6)6PQ}S2ke@6Z$+vM-^to%cslYh$d@}j&X+wGe@wqj?9{rrl&DzC}w@`k)AZ^{3Y ze@UBK_!TgSf43T1(nC6=Q+nDrdThe}5TE0c-qJ^QlD_gbdAsZ)?~q;jo~>@uPu?lJ z%bxOYvX|^F?~;Ax-Ljv&NA{Nk$7P5dFDJ-} zGE|1ia2X*ZWt5DTF)~)h$#|I{6J?T2mXl>U+%za-j@q;UoOObxe)i|Lfn@NabGUPeYp_# zU{77z=TjVEltNc`M zw{LdaXklH~wb8=54q{ygv95zy*Fmi7Al7vd>pF;e24bFpSl2mb&3@JIWm-ws@- zr$5PK^0>rr1mgJ5@}&GlM>nnZ)U(6?9E`!fb05v{w&U-xT23<>Y+~@9?$SKxlc3DkNY&R zv%Fp2X(SKqg< z2jdk2u9x@^Zj=`1k!~rX@v(j^52zFNR z`q){?8yM>h;LpYVl>uwGzXBhUBjm$!qRxW5nUgH?`wwK<&*HdbTQR^c0`3PbSpyS;kHX_+}-YWYyW&@}WDdGm;2jDnVz-nqB0+HbahJs|r3HmDTG6&#Y#WAZBWWSwLNjs8=r?Ij;8C zI^I=Bexi=Ip=qS*h?{ybL;Tu-+RssMCF5iomXB)T{5RV!CG1$E_KVbu@82|3U#CWa zvttyfce!}nG#=6Om5g(VzN!!PVR}YKFZE(yvb^@liPTfy-Id9<)$#fp^{9GBK4dqHC`OHjxVInG1O}|^;Ytpt8_&D%8UKba@^99@1SV}tNkSP?8vsG zn*ZYwSCSsp&*D7GwsQ+Kj1g=*ex_cBsH@}E_&!aeqY6LhxPBrZK5Dr>r9PLbx2oC6 zV%y2@ZW_zkcI={_>UEAmYY-%3blkI?M|^J5P6qYt zcqezW?NYWrS9Sa`>ZNR*>)7@_r6Wq!b0ylQ@sN%vRcA*J@53WLyX1B1y_LE+7EFUZ z+;W`I5xswBSG7;dWqOqT$20=ugFNChkQ(09+oDds6Vou+_L(A8WuF6Vceu*2`!cMi7s9rBk2( zgQme*)N;H9afu6>1`~4_dgq4nbmi!{#<-WN?4@iS%F5^%3w!Y#%j-0Cs`@^EvF$oa zJ;v$LSZErv*f!N~E%q_oX2c_&%+PW&H~jenwmm+ej(4d`BgOR_+dd1RVcf^ID;1i? z3bq~W+m`n>Y9!H4e$>?onue0})=%Nxz~i28P_J%sCw1+SVbp^^9sV=}+n(=HR}1y} zM(tMW{E>R{Jy@P=s5g%o#t>*43DELZO+8d!>lYqzOs1|W)Ko3JI-_m;|CP+6YLADO ztC)H>OO8{cVn|BX7AQ&$1?%!N)vweY@Qk5*BKj<~AX?&P5(dNoUZ z%n{Bb*84g>ojRYFi>ND)IyO=tTkSe(Zl+%PDz7XZ{}MX2LbJSHLEAL+^LnYKUdqZ# z@6(~4V{isGjZDeoromoq@*fhyd+6w^{D~~b>(p~Kbtz%5K(@X9L!IT+BSc3Esi*p< zk9vsf7anmw3{9hqZO1{WXF7jn+f(n;sa|UQ#3NojVj8TJX>c|(J2H2+^HEQK>Ue}Y zw^Qpj9pTqHHKr_AJ&%}e)Pv77joEBlo2b`%>i949&`)9flWotxsPQgz8h5j8YA$tr zjkbqj@`&di9iK@ZZ&FJ?g_D_Eog7j>sqv-S%F|mtu+uXQ|=bd=K^?%dv=ht5U8G^;0-9 zbX>3V|7V^VJmOMit!r#s!>IWxbp}ySA05}v=-r8Jk7LwvQO8*$%SS)8LqDTSwR5po zSe@Rdu1nNYSj#}!a*Yq66wxRcL>Xn_E6U@0h z?rfwM-<#7=^2P=p@vfjgO**c+Yus6`%RKI>M?Ifn+o`_cEK+-!R3@$gY`a)BLp{?l zID=W9JnvY}Flybe_H-Rr@AaO|w$IB_z0vy@w!QgXOe0P04`hGpGhDtxy>fJ1J;YLV zJOg>e6(LLId_5;XszzSFv)!puNB&MdIrc5bpK5=it|!*fy26W1f!p zmioTlL)BIqo;TR`s?w1MrQW&6V75IL$tQK3nOoNFYRAz|OLSxs_4o&Ms3NX#wR`Az ztJ=X3zcj(Nw~vgbKI-}2Kj?^(_ZrJ~rxYFO_+B09CFqE1L)%SYmCqyD#;W-ksTYs`q+o z+eU8KL&v?9 zs`Wn}_a3I>P>GcI@YLtN|R?b!35LR;IyO+B8@ZQ={0_YRByObl~}49Je~TsLNaJKGdmtd40~d zkFxcCM{Ugsoz&A@UV6TCJ;k>74n0Tt`y`=lYK>(1%+WLF>Cs5FAE(}i+FRAu$Z|cV z_5|wHKwX;Uy!6VQM)Qb|lJxmlZ3~*_86(8Nzr!|U`?t z{9C;L%G64qEw^{>f3Cy9Bh5|brni=ui~22{ROzn1yPW^^P3-34f2C(D@;p!Tzq`iV zHKwEGx6Si(=$PC!4TbmD-(Nq*uwT7+9 zvmaa5Z)wEAhy7#bW!B%hlK*v|>)Pa<)v2`0qV!5D+UjCO`yRb*30ixty*@50I&OBt zj6$ndNblgJ`oQ_t-tKd)=pNNpbmw^fKmYR``2W~?`|upEZ1Mk2o=~Gos;WjY>S(G) zjXFjhW7K3E$1!R!YSgGP#u%d*^%6uvM4F}%kw&T!X&M9}5fSkg5fKqdp2(ZLKM|1< zDG@|OB!2eyv(`D+HP`k1{{HC4?#{mNXYaMvUVH6*?|a{`)phOSw6ybFr|3>qa?Ps? zep%xd_*Qz)QImXZi)`I(i@Lb}WtVf1bC64;Z4v*a*ir@$`t8_>Rd3H;Iwt*jrv)zk z-fwc*^iJmAia%MkBXG!z_;|13-;NKM_C?o|bA7w-T;#OM$eOx7oBMp< z;E}v1#B#DNvaIS%{(WJtRUgrRv#MkD-=yk;`tMoton2n1TmriZE&giLtAcHd`M-Gi zjw>ZyoZ9(UPk^pYQj8f>NnM?mW!>`Bvt#_o|?GoKwF!Mt&X)lGeOlM^uNx| z)v^-rY;4ZfYc?0w(ciFz+V)s`Z0)ue)&Uo1mzS&~R##gO^Q^kY*57R5+JXE|h3zev zXB%W%s?%-nn#gKD7rtUwy^Hf`tE(e9iMFCTkke;#s+0fs>S(=ssa_qSR|o6WzIyc} zy?TybJzuYuul80~d&t%N(sZu=y`xpHo6U9A)l;}`t>d|kZ%S8>x6Lq3)y}rr=6Ur< z+o$G1buXJIbhBR8nh%!Wb~Nd4572w`)ZgwbzwOAijo}yiNDUm-QUgbw)WA_FHE^_B z-MQM^QO^|*9Bo_??r7#$pE+LG{)6Kxt88Prx~px5re`TEv&mikELYll*Kvj# z|HiS%_Lljc4Hn$vJNU@{3 zGg3U`@U^Wl?T&snb|pBqJJmtCL*u*=WB@W()qy~Q6@0eo%?;>7stm0k7bjNz`z1J~K?=6-WTQ#3| z5pPxn^DbhGLr(ZBaV+Cq#Iugc{MtUpV%|bbc0}@<-#WxPcRJ=`^{%Fkmi9OE9ivoM zW094oV*;{zf=tGkTE|Qq=R7&WY-7zmB>5q=pM=D2IOf=9kv|`aU3W}HVmBQTwx#3* z+diX=mu;DOg#1_2s$sSb=Ak118P+%!S-qV9ZdsK{^7g7@y1c#W7*9K&JC-2H+Yb7= zxly?iNj`AQLy`|F188|=)h_w^qr*qO6NsFD#jj1|EzB0jFj}7PSVKD>I%ccouAsHG z$ov#i8$)Z+6s^7Lm}0#yc2zls+TV3dw#}qeH`^bSXx%ZIHZ(e1Zji{>U_uqH8@LcXyM^~Q635Qz! zsv3C;C;X-LhGRI-*}6IU@tp0aRn2^1eYm5y%W-R}V~EQMWZveItUPJG?HJ^8${JY3 z*Ma0dJcWDD;cE4ayTups*=(Fv&w#=|Egm2FLz!+ndkJO$5rWAf&7)e${jYE zr{%Xqr&Sf5Cdu8bpGu9&SK3Bczp0w4^%|@7nkn_F@Y8yYl6sM*UggnRuYOvuAyO}L zs8?mU)@zZ}t7@j!Ymn5dB2eo!LF+YE>xFevui;WJ{z|>LgVbxL)@zc~t1{er)5aK6 z<@vVXDBYD4%~MK#r0MC(SzJ|N``EI|#@YU&IiH!<$~@}cSlKFmsS=r5 zohxfPEtbDp{cN0CSQ(H1Ypl$rH~+qJk@)+v;dsG%`Wo+tR(jF1*HtdH4JUsQ{rgTO z{hPGQVnIj>A`m@3#EsaPo{@Hsw|_2 zJ}MhTA1JHLqz}|ro}mw9S9GBdWK~Ac2P!Iq=>s*DzT$Bz1OB(B&RWxfQqzhQscEU7 z*0hV%w4zvQT9Ga_ElrV{R^&-dOE-zvE=`u2Ry>!QRzw`c)y4%_;SzzB%Rc zRp_b;k!)>681>yz5n&Cm=JU&Z*9k7Luzu2vlohAsRQr;_)>ou!tuWFfI60}KqRL9w zzuwWDCMnN19>{k>)}$v$KIh_C*TRU1g>96d$UAYa6-(r8xQZ!G&&l`TRF#)2raN2Y z%w~s^RgviI0_PEjy;`x*xto-C_8=#pG4At<)J}{cB}3)h>uX`w0I}S%X0eBISEL+S zeujSab$J^7N;r^GYs5Cn+Qc@>MDCI0nIiXVN5nSDQbq39R*2lM#fWW`HHvMN)r)PE zE)d%&s}kENcM;pTHcxD$ytmj!316-?kILmcYmcri5!)#5Dz;JfLTsaSn%G9UeEYJZ ztVC?1EL&`&ypUS+YsF3aYol#^dAQuoHpR4-&*PWx!I|cVav#PPp;+iZYVrJ94Yg=2 zpKY73C70_}tIe%z71ZTsaRYT}E?+`jnu`M%d44P(#>jKGd<-Lxq~F%G8$BSxu2K0O2;7OO(m_if#!bsDx?`& z^4vDuw3qwY@LQ#B;;~8xipMJLD;}$~iw*yDEgoMWkAbePz6c_5zYrSi#m(!1qz8KYh)A4k+uQXWFo;waz6c;#AFNYv7~d>K;yu{_cx zjuyzMSyjH?<)qcEd^F=#m+}SF`d0a1yfDA@KXoth(z=h8x|f~Ox=)b0mt{-cOO{IA zOBPApOYmpbOC?jK?!^zK?&MSV!c$WB;smLCQMJ^)WRldqWTn)-EMMwg79@4YZ&3F# zAE|p;iPXKUO6x9PYVTZ@D0MHZmAaStOWn&-rS4^(QuneL>RwPfPU>EqK;3(orqcP; zeL`sxb$?duf<@mc>x@N5mM)?l%daJfMHgma(f3NLv8(5$nb^{y(xuqXkELg@pQmL* zv7ck5QqyOJe%NwTu@|;{q|^iZxn8&)`)Mqd6Ehl1o3Q1$QZE_Dumqx>;$Z9{qclLG z#iE7O{ak6VoKsO6M&0+7mSN*piifzIGB-;ju$eDQqiI1<=|O8CC(_f`@%bS_Dio_uW? zy@B7g{!OHOHA$pg(i7j=TsQ&W*;djGi8q!Ez<1s*nT+oger#HwvgVMg@@wCrgGL)9(myAYFRVBPFDEFadiN$TS2@MrBh=#5W z6b)VLE*dKE6Acwth=#6?7Y!8<$(Z|$LFnn~5z$kAgXpRFuITAn7ts^>=;>N7(Nl4& z=&AU*=qWEj^h6GNDo7AL<;9AguDOYx@{&bQ#ScYKSN%m##iK<}1=B=N#Wm87YtEvl z{0PxgQG@8I_?GA?ZAyAuABqM$@w9;yPN^a&?ugAzbT8 z%N}1FY#T;;1np}rZn3>@+OBn@l?}xQX=QVsM8A(2B{?CuV1h*PR~OS(E6?L_o#_9*1ho__>+ zv=qmyJY2Df`^9BM^ey>O^iH|+M#hhB((|r~Kjv<<3=6c6zye$Hi?P7_Mbogr`-Ow3 z%iXK}u)sU^!C0X1V^e2u#rEzMcgOam%yKQin*Jnn)$3OavAw%_RoGsGy_?t`)`EoK zAf#D??bR1O#P%fpwx$`q3&+a14OwL=x`iDIu|uxH4k303KQPtBvWC!x!4W; zQG9)4S3$)Ba z{VSH4J51(QxdUZQhBWh&yb3gtTii!1lN?XIYNPxXGONv#?_SnJBHi3+(XKNfeEz z#qou*F6YH~O3%yfOp7-a=3$LLVoPJq^TH5XY%eUv4x5U4Npw(Hfu%hxsK(On6!~CR zcZ(KcS9c3*XpLO8+UzTuh%GsahS7o_ie#vEGsS zaAA_H&=koU-k%H4(EqL%%D9zLDC5?~!Uo!Rzi0&QYb_cry)CDW_T4M!CH<_(!k%vw z^_0Fv{%liIn2Y{y7WJX69XU&AtK6A28~a1r`luiXZ(?PK(0d+{?`dx2r%3OySFp<3 zH`i4xvoHyptSO>DF#@#XU#fCWVV{no0kkY5Zy_sC&4q=mJvA1V5ntRa5}%n|SdFcg zWfRwt(;17cvddTGPZ!qFR%-IUZFQEm7D{~jXZ$2>Ehv_@7F0-E3nWUtUpP_PS~x}9 z+D+P;KSbJE*hSh}=%#J$CT%T{X!~a2FllSHm$a2TOIux}t@+W?)+;g6R`O}<)qH7d zZlkm{XRx-_S=w4SP}|yF+M1m!Z7r1Vj%Cb&BPrp6zQDp^4L}ec-5f%AFROAp* zW%)`(l`}yiD$+z$c`GHNy0T9qsw?g?XM=4pSt6=D`7(`_C0~ZJF4s#$MGg^Fb~B-r zb-B@*fAV}KqRMiSh$^eAoK9kQlej962-Es`!6MeCh7?4zHuY}7Qas(nf(SgF@O^7i zzPxEaqF^P`I-BPu5m=V5L||9kX|X%^sON0pj_hgHpG|wt7|sT6&u!vt;Et>q`4VBi zpS9JrW$&~224S|l6+w-%Xm|VNNQ*BK@>kk@InZk24s&Vuvnwl|UNsTGncxY zC0~w$^6TmHJ*dmOI47?*Kg71%Jk5)u-#@x6Ua2Ox2~Ah!??SIP^CS5}3a#k$S1I3V zmC$+@KYS6t*TbY`xntE2uZUHzxtf7hH)RFlp;9hKVB1ggdt=*MuV!Q0%~uk!?S%Y( z5+US{#KLc18IOfO&OV5@D!kf^x2n%sfQ8@Acg3o2U7ji4Dmz+YgDd&?tRJ{)nfO)l zS=jSx6Mv;IR(&(yMZ9iSPkdJ6)ff1z#$0ThKJA9K?&o*Kw(n;K;ivjsEx}LaTy3M3 zjcnk->wjjo3Ez`JZ;@jFNU=o+9ne znjr1Y%awLt4wZIi?vr+39U$$_E0lI;oso859W3q687uA1nk4PMk|6EQYnOIsFOha% z70-1mbFQ@eO02Xyt4!L>RkYh)EA7rJlXhnfl6GJ1Chfk=+Yhw6v$Q*>tF$|3xU~Cf zPign1Fll#QwYJ;(SG&ol-It=J-I=ST-FbD=?z{$RcU}|iuDaYs+MPMq8fLkh^1KyT zczeb!v2eJ+v}M#_;q7_;Sa@6B7{1$;mp2kSZ@MJk+I)H`TYC1D`B=F86${Tu5DU+& z#=`GkUXO+Q3@rR`&J--XYdqw*Q?DLgQxp3b1d?(=gsD%!;VXQ)6C@Br{UeM+XE%)$eV`@_U0|@#1|GXt)Ul4 zONUFmn0pG{HxW4uH0QJDNW7R)W*cVi*;%(Co+=|!tj;4+g-_>9oi$r=U?oC&%d6t3!i_U9=FldUz#k>7c(xS7T%I6f zx&0vaK|QTs$XK5HoW4+IUx8F=?auUqQhOJALAiY%vUzrq6#&Lse~F^AYmrL5y*pBQ zbfpHV+_o=~m^D2>#yWd1kqzk?Cg0u{*)(ToAe$WfKxA{x?j|FiUFLUBx#v>z@Zw^L z&eAiH#C`ihkwkhheWc^kK_qdX-}2->!({Z!h+_16(>_w7*qlKU#X?4X`xvBBZI`u> zJo|X0Qe>ZqR4VMVv5#x^Aem2Ol*mYE_e3gH_9@tkf@8c_za>rsZx9ro9O<}Gt^7!!zE7iu48-P6iBUfV9skvcDr93wR zsm#xfMk*I`4-&t6<;LSD{*jwxb2njjKdk3(xo2#@H|uiKkkC$hA0+hW+)UfYCL%W% zIr-%lBB#*YGUW7`-5ohC%B{wRhUM1T7F+M;Hef@ebDNOd{oEF8XlZUclKY$8!n=H8 z?`&KB;+ef0lG|YKiR9$|8)@YLd8^7!+@SoxvMxxB6lEWZv<})w;A3902jXKA?J3w$ zuATE|kZ2Y%tFec;Bx08K2E;FWoLZBE{7l|>WQ3OO>Ks(B6;V|Moy&{ zT(O~uEb*k#xt_?Zn!gSwqV_>%cP`Z+vv$UyF{b271~RM5kadBw+#s=`w29(Xvvwh& zI7W?GrY?6nvN@Ty0@);I)QO*^rz|w}7Z)Olx~y?X;`ZgPNa9we_}M3EBc(SH^KURW zvOSPQ0{I)w4;On$Z@SQ%Si#C2BOV3zC#Rpw&n)Yb^sf^aT#(I$Tz_PfnSKjPip>p| z`Dv!CsvWx$gM=E=M|ky6fTGiP%%x#U)6n_HtM3>AQ=wv8S-y zDdLMScCkjVUi*fo`zUGO`8l?}ntoH+PjmVk-yAPivJ-Q{SjiT?Wm?ZqLSs+PSKIzg zOAc1Pt#nhqqa327WjVe!EP&enTyx&jJO4t{Bb1{}OHQ!u18pyHAT?p-kh${|({aAt zHWRC=vHd|w>_yIM^W@@SWL%xvh(s%M0<3?x@GF<5Sl9$~H*FUdFp51*kFx$u&M?NW z`>81slV&et{Ax?J7{8vSK9|V!Tsz-kXiW=d6l+colK7M|cknmFr>61T8K=&8nncF0 zr|0Tq{9&(1Zlx5LU_<@{&4%9-Cy%av#?dp+OUv$8kYc0oTEXL!rS zh7TL)(#h(a9q2-RvP0O@VP)?U8%@n37I^7OJGR)89Vv6QoG!?wAu|ctRAleNs?xG! z8P|{iXVX$2zD7rVtbfPCez@X{g*{52hJ^{cU_-00Fd=8brcKAfB>xp_9#@Q_a>rh&YDj|)o`h+ODE&V zikCPub-b)|$AGX4-zD?4~)dD7!0rm^&9b+g{bb_Ef&6 z?4`u!xfZ_+`zW!kGk7mezp2Ew$mwtXdN~l=3b`1MZ3*8pH!ingRgEblZU3ev2P@xJ zx+&jLGLz!|ME93NY(LjDR>eJkp=s8*`0Hr%!{sdemb4D7^4AYdTV@!x^*AjW+j^9? z65DFNoQG}QI+uoR33-xs`60ITTvrF1)0(g_DY;y);z?SnJon`)YZMk%k~vQ->~tS2 ztU1*S3%hx$TISfN2ViHZnNfJMmeWIJ#(B2TVgx4t4je4A>(jG{!5^PbGqbU(=jUr|%(_$OU|V&>!#lZyCnvK%J#z{RD`iaVWSY;k z5xKWy3?g!Wd?Ay_z4~kek$d&|SS+vNj96anxshUd=ejV$mZq>eZ|Ke;gX;Zo=J1IG-g1^3` zWK<>1s0zC(UsiTgzM|}|?4fj3GQv`x5f(DSLPl7~2n%~F8DU8?!a_z>*iYGCIY9X{ zPGa)rmK}FHLp&y^=S)Nwe|{dBYp>hBxF5Z^#?okT<*` zZ+JuA@P@qM4gW58Bq^FUCAB78)e^J)* z>&s;2DS4u-EhP_=wWZ{djNre?SitFsND28-D{D04=Fy8wSUc!A6+o<8c2c6;D>|Y* zyBI`#TY9=D@ojPPV&YrxWLfnbdvQGR?PbdRhuM0nEAj2=CyPbj+B-7#m0+0@rzlf^6`t$lFNwgnorLmwrfv%PHgw&RCi*# zhi6j6N1k*iwtJe?huH3E${eD)meXlOb**RDNK}`eL{vw4Rx$AMc*^#ZvFr})z~lRw zj+1_P$Ghog_*O+ldYVLd$%7=qJL^w`XPrJM5nggXiSWpg2yYD$9y4DgeK)-tNjInG z;tOx5dg28u(q)fp(aE8#Uv!+9NSudEi1Vl$aUOO1e`R@Mm&o$Cv&b^B#=_1Mi$#_v zVnvo07KkiU^F)>xc8M%0k1S6u5LsSeB~!~F%TvolmWk;i%akc1%L}tamS+cxEH7k< zEKmB1EKdZAEKhY4SsqUmStb+Rn1`oVh%8UEiYycJM3!f}i7Zd85?P)ZBC@=&SY(-; z@h@2>&-<4wlgEfGFDw&TURWivOezyuo*65$JT+HjnN%pUBnMfZ8zQp2uwG<&LH2Sq zU)UtFJU3iqdBG1^rd+T@mdD#fmM3CGmWg>H%d_47Us)zi5m_EPC9*sgDzZH0FS1OU zC$dcIC9*u{BeG1JBC@1BvOL*KWSKTyWJwuhd2*o0^4J=Y<(U^E%d{>c%hMGi%QR1s zW%6i|Wzu+&WpcX6GI5c}@`Q)T^3))aWzrFm)cN3F0xFF7g;9F6ImYf5m}y2N0#oUFlCTLHwj~jZrV;nSqsdQ5Q1G&H2<7$nVjK5{Yq+Gf&g<^eO&Iq?0f~BAw&Ck=%pR!AQ<8)f36(r}`kd zC&wotx&0?3(s`OVj7aCnu~=DiOjt?0bK|`3ggz5UymRK5MZDuU-;a2wJ(YOJbR3%{ z@y_Y467M8Tq8B$L3}KHwX&X{GKS<)8^Ic`mmavk3-H<#9`JGDjmv|>N$hHR?iYDH9 zm>NpF^B^^xc&92gig?FyR-V4yI5wAf=h5*7;+;ncqwuC>$IFO!>W@qB&Q9!0q|=r# z0a<1pkH=qD9FHc_sW~n?<#ryAkoC%heiH8>s}G!0>50!d7g~5hd66Ev*f~RaNtvm< ztjtngQD!T1l)21I=Q!JydCIG%<=lMEg%%bl3zbF6V&yeuiLz8#rYu)hC@Yl?@q%(b z5#)R#$oWL@hO$=4`9$Q@2{}c`I+x2SLc-1FTGD)*uVoz_jJ!+EEkcSnkI9~u2j{Ah zVpBpdMycAAHJl10d-I0l7#V+Gw<;huGPiqY8#+{GI zla$tEUv?nhO6Nm|9v48A z^kbat_&EQ72p4W?#ka%zBdum{j;9zNtXQS%30<{oQv&a zyjLnw(%q!d>`a!wvId{Ll$hvVvIj9yT|$d(x!!^ObaCt(=2v`(hOWoCx^y*7aa~Df?L;$Jx%j&gtPVof1<7Gu5V9@^nN!Es%eo+cW!3_jwLoSqkXZ|4z5?-r$pDEumH&66oPW>RURaOKk$%-IkMG&$g2w4$?tO&w)nK90hwLsGE z={;EsB+XhNWKMm?gE_UdZjAo*SC(}cJ)71O3uI=^uS_uylVo3E)A2FPtZ&CJm3>x_ zFVi3HN*qkgNF4kiE}NOPlw@YjRqQuB+J_kT_K}CSHG0prO5QSxZ?eWA%V)9ck);q> z9*PiILVC)fGe}hOUr}~f(q9fGi9{)bL?IG|NE9Mbh(sY0g-A3uOC(Afi9#ewPJi>o z>7hvU#OZNJR5*}3$0O0FhjfjVl1LOHQHVq#5`{<Zs01&Gq2lT!hB_K9 zF;sGy#85{@{wsz$ocFI7>Ts;YP|4L2LnYTq3>7;`VyL4>B!-I1l^7~^g2YhdbTf@7 zu?{5vks1vI4i-+`Zd0y^gtB$-EpWh_APwbWF)V^rX~G~vb(Z}IUAE=>uoM3dt#UN znsv+j6}R zd$SHS)0fXD2T5P%uk>Z;sn@bY>(DOz^sS@G_-P^0R!pRV&x5yd&wDS zpghh^tURX1nt;4-97EogwSR>TO~aQ>!%?2rXqwTMobJjVrsl+ah&>QZS-;S>Em#QX^R<-1|G#eM*~87J^Accjhk$nO*#rc`iwI3^;-M@e_d!g z4(vh?a_7~iJl;*tY&@`7&TNFV3*s9gQN;1lF09}}R&e3#=3Z=yM7{f?(8K*B5$NIG ziD2~5bif@w+>6bU2=0WhL~xL(4HC6MqBcmY6X(c!;E9!JKzQ6di19!JbteME!|Y$7 z8mQWzh174yHp!Fxz4gerVQ-8)!{1wrEG0eK6drFumQwOROxxakB8q4GE!JJy_Ql)A z(Z0^MUn@T}H^^bFG$x!FL&y^*NQ@DaD$Z%`t%uNPIly@$m;5#m65TVx?;teEfkn@$u19#m65mW0i7E;yx!lePSf5l#+f; z*-QDlvbXXLWgn%C#EDLQHT|ZN@!{A^86OUGm+@hLZy6sBgvS;mK$Y5y7@_BZ@%eAu5U<3mE5j1LJfWPI4S zP{xNtSu#ExsF(3!-#i%~$YC5fk}SUem^^8|d#s=M{$pLm_a8~Y?kkSDitkUT7vI0v zPkjIWQLIKH2N?mP=g0^U`~RbjyIw=zP(}kzCQn(J;{l z<Ks(MD97Xd`wq+Sqk$Alle*tS{P-^lQpq%GZ^>m2W8fDAC5D zL1;tDzo|qUM>9nm`+Y}?TkMCFP$4s{i6#Hp1LqK!jct^Z_vXg@qu z;>rVc%!vw*h7%j+92ibKDC}>RCdf1QMMt9;2ZRI7@uR)OcO9I|=o@e}hA|=R=n>YE z3*yES@yS)gP5I$jtSHweMA$}}FODWM9t%gC#|caEa`KB~Oa?P1W>4`JzApdhDSTS> z-tPD`p}UejQqha?Y4r(V%$p>Cx^jkzN|?h~QT6>I#tPvqerv4Eq@p~DxxYV}%2;vm zXa=*Y9})uaKXUCtM&E4Nd2v*B_T5g1#{W2umN0tAudG1NRknYUKf$(A`MGj6GrOs_ zHAHSRZEKYtn!irDUb(?kAC)~n(MRjq?bGXMBRYvV+Kf&jkIH;1F-G@a9DRXiJQ7@3 zKM6?a%6jwe1XtFZs}p)NpQ4`lE9%)v@7Zai+}Zk1p6+HHPO{uhbL2efA^U2jhd}1= zdur$#jeC|^Yw=f2(nBl;B-0-0BQyU!3G%%6y9lDrhx>Q~kyg&Zj_c#Sc=nPJFHhQ9 z4yVbJUP@xkM?zSIdieb?r+u1pK#5Jq<6%h0%2VGxHLOlGQt~Dxk6_H1Ry(vDxd4Os8J`wA0iE@{x!Fv+qX)tAoi=eN0x@Q+_T2J>T%bHfyCV4Wv zCstOrzCR)>TT$|^e#ib6R#)yF^rde+Ju;WotS6BZS;uOOKgc>(W26tG`27R2bG#*9 z-kg2>ojfIe@SU&hkNK`i_Q&j({fSm2&juKCcs8KQ^&cp9KSUcz$AIvl4XM2{(3c{Wmo~j_XPJfkp%JXF8KMDOjWu=8NI9i{L?wv1M zip#=-J&x5~9&wA&QWI-6SWo0Md{dk3Mbwu2MVOviWr z_?gFh`#FVKRzw%{^Ejdx`e}<>hR=B#A*=t7z7KJ-Q_>Be^Eg6xcI+LCeja^Sf_~cf zuR%X;d)l2InT~z2POauy+$Pz(95;ehou6En`R-6k%@nKxMCHnD@5J2<~e&Hd2!!_#m-;rM!-+FJ~{ZO9U?w#+vOG`1)r3SWoh@p8xnFZX!w zSZfMi?%{4PnN{!UC9`VCta{H#nN>sP)8sIphRmlS=aU>_jfgRCjLfPj!>k%ItA@;~ zA+u`8tQs<_hCihjjh9(9X=c@sXRPG(HyVKS>G{hr>F`7~+f({QwD+#P^ll{SuH7AbrApU0HQ*$m-J z@T(a$;Y;Nz zMrQu{ncioak|>UQ{!{Z;DnD1QHc!7BEb}7%x>o6-`RkPHl^b9<{`216416H>@1*&i zHp*YE-=V#Iu{wv@bwuVc@MV+C9F16?G_gJ;)`vaJwOD`R>!{cuq~6R7Zh*OUQ06c~ z-ZhWSN0u!IJ&3!d%=^S7p^Q<@tVjJ)IogE8h9gV)Mc)31jS(sCieXlh6I+EWAH~Sp zp3q$>vfRZO_3YpfW;KUnOOUJlb-I!Wdd~uhpds&!kUw9`EHG`mmLN+hxsd3ntL(Ck zY-3z{!hM#R*6?JBi@Ea(^Jw=x<~Co1%bO#2zs+>%YHIdc%q4|LVE-AJ?LZ^|nPI@! zX^YG&8^iOM&pg@{&V1%!Y$7rBW_%#$)W>FUPCdUUUkQY6=6>uc=J{O&%9uOVmC zQ<5E+T*Z0xq(89we48e{E-HalL+iV)#M;@h+47uqPYq94o4!qAz3`>)S1_j0M)XM= zt&h;*^>0IE9va5J8|BNU`Or+!Avv@F(gMg^rHAIA!-sq1-Q>DMQ_*4h9@+CJe9KfF z>PpXP+hw82TZe|DNhvwn9?nB<1~%GC+9=c7FKJUpk;oe zbXPJzJv7$#pIT;;^1qammH(}rVjhR*+J2|$smhP^+G(2py^{GXzdpmX?9Zl0HHPKW zqlEM*@;^3@_tns&+P`tN{ZY%$QGTMFtNfF4p7K+@&wTw|Mi%P7fcwZ9$`1sKFC~pHh4@m~!`Sy^pwH?9 z5$Ln>Ks5Ty+7*F5g>RXRo$~(G(>)8(XVrl)^eH7rn~eR>(Wm?h`rJ85-mBX)Pc*sH z)rKbFho)v{rf8DCqDhD*A@3jVmObws-=>Q`DM{TS`h@5cqE9%*6n)=M^hp|hLiEWU z&?jm13DGAxyuG)J{h#K3n5<6-(I@%5OCBa~N6BxYPs*cDh(00ugy<8ZPsrPT+-JW2 zF8U+~O>!UPN*XT!@vb|2i+A10?qE&xh9oUwr3rcBWBDsS7UE+eu?SZ&gZ?H_JS|t@ zX(67Lzv5{j9u;m-^6ucScG2X{N#bdD_7+dOv$r*!5$wsHo{U;TzQ`Cl4PE8zFGN>D zb_VTiL|0Y2#ET~EY-22vlEY2=cf;|P9h4tSmIwAcRz2`z~-BtP+Im;MnXCdR}Z|>rUJ9hiyhi`t@ z4?isIW?H^^$m*%EyOO=nq+d0UcD``wseDcOI??+Y-eXPPU(I{0=VIg?YDvGXY2Iqv zU&p(v!eOjTHMk5{en2mgS;d3>P0T7D?{DGRY-vO=v1`tLd26C6#v*pj-ot4Rra0V^ zwdP^ zL?zL~pY4<8yV$o0$;pNAJROB8yV>(SF?cCwGPUS{s0;)g?q zir4+dOS~>5{@dRLD`?t39V?LZ+nRpIJc=xKc~{B1U}51{gb>fnRd{BIXNH`VweLB$ z@L)e@Xqcujc^kcHpF37?d*5XA&z;eK^ZqISqxr8Fh~~F170tsg=2^&aG%sW{j=n3J zCm+p2G!J{2ie3HCyp-vwd`;O)`MR<FJ<~DY3uedG%q=CD$zVS{mt~f!_a(4 za5pqBe9IIBHKX&o;7#aU$`4k)t#nhqqhy~te|^_@M9)I!@{2#$9CXf|(fRg8=v>N- zP>wb?B72L@X*D{h3_6D&ngTQMd!Do7mLnGzpd$a`0I0YE@b5 zz2oWs_T7bO{(9H~G|&Cf{O;&ws`=aBgrNDSJKWK{u#05{kCi>o@MY5)k%{IdpQp5t zr?img0a5E^=QCw`Dp@xt-AjodBi-9PiFz*kpGo&oqWNtRvj3T!H2Dg^yY+tELNMgROF^TXge^xyb(lJxl~d1I#`Cd>GP3U(&za_`aC)4oI9iQuU1N*Cx<={N1NJ+ zdD7=;H9F_7=p54Lzd4A`?*uNEJ|CGUeSVLeOL2R<=v+vj=PLSq;Cj(~h-h9)F4wE* z^UQ(i^C3e;^V}27Lo`pM&5rS}hNJn~AQv=$GKdjzjG*>za1#29^BtK4$bEftFs^LTOay-W#C;j z&;8MS&<-!_W5#*MH~!MMw+EoldwW)(v6ek+(8jIZUC~C(HqnOgEpu;&yxr15`*;U6 z;yIF*k|Rz19#3Y)!qMj19v|c?_Z(|kUm{o2u_IL?ye|(*ga>(N`^&M&xaq6e63>xC zJO_#AAn_a|o`an06*x+wI?_aSaGH{Mj`R#uxyN5d;N3%H1co1*`vI+JrZsSoL~@jw zqx?ikBuCDll=GCIn(944Xit8Hc#gj=fF7I`Dd|PZzbO|h|Dh!6=C4HElqBjV%@L(~n4x*<_F}C>vVD1L=l$rJ(eV=dFB$;W4$T@XfCHs^Er^?Li zOAm?UzdR_B{Feu?v@ zVCC^wVN;#?_Q~#S_K>~4V;`%K;{sDzg&em#pOweoh02;E*Rns%ZFdRl%#pjRoQIg^ z-L zyanAOVv(#!2W7F!iOkvAhJ;z`l=KE^yEU8sf5(5UjQOE+WXy-Gc=`{L6;JrG>F{ro zF`sGMWZjcA>z)v+@t-8?o}^j#gm`{( z`kOz62jFW$w`bvNgadimQO5jR+hxpe*g9WUKPk`ZCuH>#vib>G{e-N3LdN`X*>xbl z%Ni&-_zO8gK%|T_X!wK0L5bnW< zAY^Sfe5%CxetJ4_*b)4H!MD@#|5tZ#hJv{n?uAF-ZutNG;l47r-x_WG5i6(<4#W!D zLS|wGLdO3syzQpsZ8vgWQS!DMX-?Ji)sdevjQo(1A2RYoMt;~E-zEB&G$TJ`-Fus# zjQpe-`627xNzRD}Y!5}{PU$s~PIamRlVeYdkMJ%{=w#0()W7G6SXRP3Nwz zO+tHje8!=f#Gp;08A>w84m%@#k6*#daZfa`S!Z2arc0zi4)ujZ3jV7^C#2B{L?`gK z=3dwYi5*B!QW86mCU$_t4iJq6r%CKUda4rbk)Ea`f*_41;S6&;EDcQ-Yzab>!daS6 zY`=Y>=rf>Dq6tb8O~6l-L=&WmCLqxSWL+pMQ*_SnGLn*m=HYU!EvFW4TO_*=H!pJO z$~tc@D?-B8&7H4Cpy#{3E$I37*3qoM-rqWo)%fO+g{;OmhcwIVV9RV-3xI>n`LIH) zATz8CE0FZtntsQ!d`8IZ01h=5c6P@Kgd_ASW(Qn5O8J4w^bf%2FKsdrzZCcc{V0FZz}s~PJc6o6{i8_&zoglDCC5b z&|K_mQ)nUHLCWJHAs!OCDT$m(4^h5r{u){)HpnmjTyx&jJF^SnizSQ>Qj&-ojyBD~ z)3H!#^%#@2xsf%?TfVidS(f{!@ocAV%QV&|h3-n;4)h*to5G!EvhFDP)0Hz!l5ZlG z6&G5KWeI1QTYj<%ocx9CQcKx#*EV0T;>?FF3s_B*YdK*DE;Tm;m&yJE?@_EN*6--b zs&1QqrtCbR4(vRD>^y+08R3&;O^NcXDM5A~kk8Ho$Swi6!PNN38PX@WaPqfF_pN7j z@S?Ar!yN0~!dq`Yc+bHKwuQ(BNYX9LDTJdV4+x@8U1WSpZjx*C5S~#m#qRCPJE6 z95NGu^hwA}1b${&n_TfP(#93$v7d{~HOOCW%C_o?6L(`|fu8)cF+0!dz zKO>*5V&C+(xiZVxxKez_#tGJ^?0b0pyu)X*yrbf?*lDS`voVx; zN6Ur@PGRQBrWH=#kiU#MNEOcEcMN5Q^3xrBP{};mGLd^7D`^{@o%`su{gIOF<9@m+iuntzV4uWe?m)b_F@x{Z zw{EO;4w9O)PvQxmn>kHRHv1%=ZY*IgvvWr`=Q{J_mM-!>3^g(QVxY`xHn`e+@N`3M zTSRWmaoRT4vVY={SFpVO?&T}HD11gR(`nk#lbMdSDOC1S_$-s%W;^;Y->Kge&V1+Q zmnF=1F8YkdUN-L-AkTjx?@$LNu+yS_$6%Lu+A^EDNq66V?7Hyu&SKXE8fQL&{F#p+ zXXYblf%llGr!^nX66c#Ok?7_ii9}%+%Uai4)*#`_=FE1B`H18bk3!;6*u$i3@63qv zETFrrL{gHKNXSYg?4^8N+1s4jK0sC?$w$WUr>1q?8Ci)W{id>?=5Vg%_E~tVYujZF zS~w6%)ZnvDZ|^B+Rj;-j8L4FxoHG`tzi3LMx2HX?2%TwD<_(Vo1O7l$$@^1IP%x&O3u>^ z2qgxQ`~~Kbw-;lI{B@zZyZ*VnDYS7DqsiS(x9}JD0zB~)NnUccX3h4YL?u7?OH?B4 z#!BG`o>K`qrvvs-zHV-9?u+ke{z^`67Y;OgH@v`82nR8iO_H4g(9M)@a3(4hj?llp zuN$~&&9Lc**=+h?JL`-F|YlJ z-!fUt*z}OGiPkYT(K^;GMwteGPxkLVUb~d{uZ8X=VNDetr)G^m9_LZOV(~a@z3@1t z-afKpblXt;&Yo?v@H^MH4Q5x|8UK0g8+{Zo3m^1oeQ$iwb&sJ|x|Y1d_}PdjDqSo4 zMo+98@5Jd8+orJ(E7Ef!d*%u~0@*Vc5ipm%pVdBX_@>96^3H4B`gr`&)wOf*MD>1! zc%s{z%J4*WKDDwY@>M9F==!$Fc%tg9OYlTd>lWgPj&H2S6J@Na#a}%1SmxZ_dc}7f zUZBz=1TVlHoPEvNwR7n6CF>*T^A3+y^!ZyGyVB=7*7LrnY24J0ue>CCtg^Y76TZpx z_9yFS($BAX_F^r=+E`}`HC1cV*e!Q*+X(u6o41ec8*^`itZv+2yU6yfx#tmL3pb8U zgV;-VW!q?5g#LAp`N5}zRgdO16Y&C%e3!Yn;7t~=^R(Z(73{Z*^JX8giQ6`g_eQ_> z^L6=&iQc?V*0R=m$kQ#KaCy1~@oQ_nWPibi#mrMqtX)J@a=}NUlFW5-zFDkKo6K1@ zx?&5*Ho4$UinqDT*;F38GX4RX7fJw=XI=;lk(6$-UpfEcaXXD4?Iq_XDpuO@zy~6NP|~A(aIw{BsE>N zMph_be{(Zn2)>|r%`|+0kT=NI`V+Z4T0M%$rS7W%^mDoDJuN@d)OyS$ei5=J`qg0i zw)_es*QywaVz}p6)3J7#tdBxs8S-f{WIbYQq{K3mA(nyeN@k*8&5|gFGK{g1Ch362*`vih)Ei+<_>DG*JvBiXn&jD?Wdw`Ek87QH+o%hJ2!!4ZVp_pR5X( zD2DQLl%FW)Dv4sqCyIfenkMeU$j)-SNpu5QN%dGM^X9b^CAxtgn!irDUbz9TAnIxISV_Ed z-)jOst7U7X%%oSh5br!#Gg)kKO|sfxq02zhi)DrdhcV|K%JXy~-kBU`PjHm-1It=H zRy@~cJ>8!>{ED%j=PX!j5Y~Eos~=X0En$5R*KqDNHCZFk5Y)QK%Im5a?9P=qER4~o zV0|~%L4*TMiZ>_AS=K+FvroF=i&Ly$Nd9nB<1Z&SR(K9!K4M?bx4KNl`eZEVihq(s z!z-~M^K2#GSXE9iC9CThFNB=N11Fls^$EQ1A!Q~h|4TVp`QOUlDW@tw(yOLvnzO>W zYP#mkFz2YlOmlwiVys3u%T!X6*(SyR4DWkL$vMhTlyjATQqEI;YA$VT<$Vt+xxn1< zh+%}18ZI<9n5QhV`futiHuBF9v5|H1{h)^RJsG*~d&_x}H~bf29UU8{GY_g=*_U~c z@N*^BfgG3(dCp-r)cUzRVHbMnU)L$uD>s-b&*50d*|p0U`+iuvim~th`gF!VVKzVmzU!W& zh^pRRmBq+tc}`$tv^*!_`|hj`mGSYPMByS^#z$n!+zlx)cZ1gFGIv{h27lMQ@*)22 zj^9%J-2;yr@psGp#NWaGrhaoDnW?Stk(nA~KizuSbMbV=Jp5h5mQnaSxe9+rd7c5S zjh1IXaJ0$yleJa(75p84rME0!ul_Dq{N2iN;_sGk5`PDIOLh})Drp(~9VPL1kbQRR zGUV;M<;mjpC`n&}cs+>MgLpm2sZ}dm#p{vA>p{F8cfjkB#_K`69y#o`^IDAkHc~fs z+d;e@`FOo`C0KFW@<8!=l*jAAPn38)(s(_H*Mpy$C)|fKu=riP9yxeD?t|BZ3pEGd zM?Ss};`<!59zlF1f5lfqd?ox3)9igj;=6x(5ZSeCmJ^)s z!{d^Je|)@Ug80Yf>%~7pt-tuk<;mFT%T|}wu6RkumVDM2J1_UZS6=x%5ML=AX!dy~ z%ljqETf}27t!8CM$_z7=TT1YoQf9chx}^%gS+Ip~bl|reN$d_{cM!XS*d4^~;HT!|mPWBS(hE%Odd6A3 z^FmX$r5Ri6v!zX}%yXPrnQw2r=k2Au|7`Afcf-oAZ+U^=yuN%XepC3l63aq*oEG52 zJLcxuvPAr*&_n;aPPtyW!IZ9hh~GTpJq^EEaZwg;izG1zzns04coj-RM``XCN!8C6<#d@aD&E%|{FP?N+KAu$4?`m57T9A6u zAnfjvmkXY>*}Es!C+S~kT0ChG5#{}5F<4*HrVKo3idPbzv}xH3Jn74uqVS}5mN&`F z)XNordVO^S{7wkU?5uoA*+uyiWmhHhS+4D-d_~z^*+c27WZa<);|^rpfxVQkD|;*7 zQ1(&2sqClhuNuy!l=2%TI_v*${uSCu`LuPJ*e*)z^x z+1CM?3Bo?gzREY1zm_{Yv6}+^M(HlU==57`ZH*H?9ZG*%<0SoQjg$1JHBQo>);LLj zTH`cD`8!kpkIqh0l^-dmnVa16_sZ$Y8OoW;KPYD@S-+>`Y~>%7bCjPb=bDDiYn=Y1 zoTubFW#r6P@~oKj0wptO(tnrwJ8!poZ1&-~v2cenMESKcRJl_bru;^^OBt@*txUD7 zzh^t2S6)zF#Kw!AGnAKludGf+h(aP@A07>-bO+W zJntpHlc&(~AO`UMTy}k&S|cZ%+**5(7$DNC7!O;yd>#8-|Gru4HGr{xsYe21z1(dKkziMR>+}ti@vY~)Y8dOk@VZ50y5(=viA;qbngw2W z@vy)9hX)>3$jQ;42Qk)rebz{ny1?rpQL2=jr2H@CWaWP=r&1M$4?db{CyM|%&$^WO6wY5UB~^&W%g z{e$;-#&>BWCs}(>#0&d*pTZ0Kd4}SJC5;zeR>=51W@B%>@E6`w@WMh5{p&jAdgTV= z>pdMWyuw?)4^_EtAYSxVUiv(g7$9i{-)%Fw-chXXT=$N_|JHmS?vlugQ7ZoT-@G&M zzpoNAa0d71x@%?W2s|&+#`7X=JTJ1s^FqrD&->$|2t2RQrgTwuQg&9pr0k;niL$Hm zWo0+zE6VQ59!gi`tID3r*Oa}KuPb{i*~Q1N_fhigG1C2%e1VMg0Oik=1C>0}CV!B* z{kK*4+18C+@Uz06%GZ>=l&>p$E8kG|QTA27src%8!+^m48&uQGTNQ zyVj@FvKFQ|l?gfZtNF7{ocblapuA{WKMQfrP+n4IDlaRulvkA5${giYy*6K2pe$4t z{U7e`JKn15?)Uv%Wv;R}BGN=eM1n@KA!6?!NE7KzL_`z>MI=ZFq9Is6O?)UrI_qoq=?m2(l^Y4A}dc7TvHP_s8j5&T| zj9J#4xD$)87)!7ecVQWp<36_SE8LF<@E{(t>Qz;@v0svYH3%L#sQL1d@uNLo9(PLf zWv4Zt8Mngo1p6}<;{G?yzo2R02&G#nl zRNt*up}t%7A3PI8^t1OaK@?*c#}E^k#1y763$w8i>KH)l(J_G3F@S7>IucM?M*>nu z0QKi>+ReGt?%l4Vy&FG+(KKBtn^T0O6X4o8CU`sq3TVZQF z7jI!bx8WVw)$3PF|JV4|YU#E+_Q1QaC-%bL*e7T_qhq8m-i`e-Qy=)ZTDt9z15hJ} zax`+t!8inm;xHVJBXB(1QpnMU4FjKh6|QTc?Pzn{YFJiaEFix8gS39%Mh#Q?mt|i+P!25ABQQV*%=MO(?z)Qo(m87BY87hfs^?JGsV5x|^|ta! z$Ah1%e?RcR67}ykhH(rrfk{kZ8nZAP8{rw)7oiqGej*+1Lv8 z+!y7y!Sk>!>KQP~Z-=@|R_XSc`cbRY>W__UrB-is2ZYoe5K?zQ$QGzy!zisgAf)bq zkh=Fl>fQ%=3+vH4;mQnvcrqunrRR1pf zWj5a*RjarCaR3g)K{yzP;7}Zf!*K+TXM5DYkE(G0KB_|f`#1MbR%^8%q5AjxkGg-C z>ffdMcd7nes(+X2-=+F@ss3H6e;++YZPTiMm+If8`gf`RT^6DGccs<8OZD$k{kv5E zF4ezF_3zp?_3u*syHx)!)xS&i@1rWzzpHNDzbpM4_2V6N?c?;AzUt)zXC)Z9jDZX-3fk(%2`&26OSHd1pN zskx2R+(v3{BQ>{?n%hXtZKUQlQga)rxsBA^Mrv*&HMfzP+epoAq~4+y2)RHmdF0?$`tG!k*X*dt;x>v3qB!?c2MtUuM(Pp=$fKKMufwI0y&h5FCob za5#=You7H&3O|us>imqn4|PwE()Z&Sd;m2U8nr~nkip2tI)=0_<3jv3E<&|FeYzN5 z!6mp9HTTe`nn#U1>UosZJW49g9{#uEOe)T#;!G;eq~c5}&ZOc@D$bDvAvJzTjUQ6uht&8XHGW8qA5!Co)c7Gaen^cUQsalz_#riZNR1z|edeoS?v(DE^vHE#fMaUNX3U#d`Pt{srZnJ52^T&iVvyykQ*{z4Zle7VL!r;Glw6VqWG|z za5H|2Ik*M4;x^PdmI)Jmjz#Jmi_FXHAMJ>-1z3om;Z7{VVl2T@+=XRWjvAR%YK=@% zBa_s~BsDTgjZ9KeHFSZZN_FM&Y3O!cx&MCH8eO?RJyLh*WKQ3Ery}g=@X3y_VPhO& zQq}%YD2eW2oa5 zr9;&5iqbk>kvd+HI$n`kn2kDKQO+6I7Uc#t&9FJPz?P_*(5E_Hk>{e0 zSCm$LNFA?8^*d7ij#R%R+h<-MmlHgnIXK%5J3&}{yNlqn3TQ|Z^E1L7T1QxV0#DZsB6-4^&kIyFs}Z?cE=ug7xu(n z*caurZzs!`0gVlf7{x|>!;vgK1LvSb#!{Imr$FiL|_R?0BWa@{k(Xp4* zu~+4|6FT;?FXKY|H7>&6;9`6Qm!R%@(YId34Vh0LS*KpaeuN)qJ{wW2Uc_#~&G;$i z;1=A9+i-gjPFkg2#O9)&QU1_a^&(c!D3^LhxzsbtrJhkP^^9_xrTz-{;{iN~hw!jVit_vx6kYvSQY5kK|v7c*FjRh4wCvskh~0g;9b}gdtq2D3g;Wz?E;=MQu@52XhEIw$}_f$_vsPC~&u^Bc;{eoEeE%9t@g{|>i zyv6mP*tK_HSJ#hXcmKdyie1|s^^|U<@4}wg3wvXqOx4h~8vCuDt|$9tJ{Y)Lv1|LI zu52nj5C`F49D+k}7!Jn~I5KnifhM{JV@ILxCQ@3zrT7v;7lXKc4*#ZtdBx z)a+Mk_S^r6XTMUjU#Z!z)a+Mk_A52}m74uZ&3>gu_j?9#vj+1 z{rNp};`;T+=-cDxWcCbfqcQvQ;oEgD_LuVf%ge zEfv{Pku4S3Qjskc*?pdIWJ^W1RAdjG?#PykY^lhWifpOKmWpht$d-z1smPX!Y^lhW zitM4&|6h@<`d4J17}r~o{k3Mxx>I`O!;0(!{rV}g_YP>K$lf!cks^EFfToJ<&-+YO zWLJ;ttjM;V@G9(ritKUMJF*9@aAXh3C$eu;WPj0T%>NVFY7dHR)p@W^WA>o|Z4}uy zhH(s0Z?;x`5>uE)-TS6|-TNjR;ThN%&%`Ep7B z@qBED7hwC$mQjl|=Iy-Okzt!+Gi;76uqB?2t*|woi?^_z+wczT>h&u!itk;h$gth9 z2i}D}u^0BnKAEGvY7`mvZq%4Jq^HL`sWDG#%##}Pq{cj{F;8mDlN$4+#yqJpPioAQ zBh^Qb^*JM{b4Kz$)Hx%ibp7visR5&i~s zH@lX31()Dbd=)ojs)z4aoY{}??zT7nWf;R%X5*lB2ip*lMi7TC79;dPkq`#(mV%SGXS!;6Xfu zhg}1)SmyJd!(;Kx?z^v#{oM7eS+QzZaa=v*QQs9ms7}#bJ!V#1N26or_zpU$qfx1& zQF&Ho&zQOKrq~Sid(PfE3eOxEwAXRpXPx5yV4oF=`>)4LRNQ}kPnpkijd{!Gxn%oH z>6iujJ!i?7#X2r``a;rrPxqMD^czmAb75Li=fb4Ug~=N-2gbbXJL`r_)xAxX_tg08 zSnp!>7A3v!)ZOxjhHTWi<*&x9(6RfGF{|UlG8Kcj$0ud#hVPF*imNlly{Bnrpjh+F zU`S8R|5YcN|J$cslfggKqx`wwQ1vJ_hH=!dbd;m}{iW{rmnlqR7G`53JOdl!nb-u+ z!lu{^n_~-XiDzRgJQv%bo;#<%+ZNBqc6b3U%@mAk8@!6ITb(`D@|wGTJ!*#H*!IT( zI1mTnU>t%&aTpHA5jdW;y_z{bzh=Xu2>b(xiABw|pIF7)PcrT8^`*1Xl z!3R+13ACOEaU4E`5937C`^ZK;svgmPgt|UDYN~oNtLvju*GHwUk4jx1mAXDEb$wLo z`l!_PQK{>rQrAbNu8+#fAn3j{T7}hEgSA+P^|%|q^j4{7QoD^k@3bOS?KY(TLuxyu zwnJ(=q_#t9JEXQlYCEL1LuxyuwnJ(=q_#t9JEXQlYCEL1LuxyuwnJ(=q_#t9JEXQl zYCEL1!~5An>O7>*L+U(ygnVi^eA?v-j_Uki_HFBRe$d7+jv*#6i78BD7G`53JOdl! znb-u+!lu{^n_~-XiDzRgJQpcG4Gu(+q zSd1lDio383%drA0u?nlP25V7I6jyE3<8J&M_uv<}7x&>;xE~MTK|F-t+KA2_cUv3L zxnrqw$5LmFrOp~loi&y^Yb8mFbkY1t(6%bt2_5jMqU*c@A6OFSD}VQV}W zb-m`cH4$B}k-A<*Wz`kIf1t1M!X4c##`KCb+l;jz^-n|niH?JK8abs#PN{RaQs;1`&f!X(!<9ORD|HT6>Kv}r zIb5l8xUwAgQR=U7KOVq?cnA->M6qWa9XgY%{ir#s>MEqS3A;Eyq|XWIbHeM$xgKx8 z8&SV4(vmmht$4dzyG9u85zPhlS2P!tkt;L{JUDQkW`Q<_aSTy!Sx~;-vLI8KMm^<3 zIeN;8)LX-(-Wn$L)-b8JhRG&)7B`tqNtqvR>Ku6#A45I!PdSg{6np}w z;!jY|=g=~GK8Ku+KgAjNGn|P};VgU_pTVEwZ2Sf4P0d=Ho=_&A#}{xezKHYiC7h27 z@MTpKwTBr zl5_DzoQJ={1*p3Tw2ba1kh+^d>TUw5y9uQ3CXl+DKsq93`ECNKy9uQ3YP)5O#t^Hs zl2Y#~zInIC5W5NWu41M2u40*kTTt&RR*v3PEHy8?=>?4#R`W8cd70F_Oln>xH7}Ez zmr2dbq~>K(^D?PY?xn82r1o?l!3}eKAA#JD2k;;s!ozZwjwsYC>WD)7-FFk* zFh}?QAH89^?);bGrON56aX|fHsP=YKXB}^7$x!W0Y1){MI-Elr(@}@hv@soZI87VV zQHRsCF&%X{O&iluhtsq%9d$TO8`DvT)3h-ibvR8M(@}@hv@soZI87VVQHRs_BP~rw z9nPVpg=%Bk+DFKtb%l?*?BQcL86U?f_yqn0r{R-09e;|t$3bhDX(Lm8kHfV`eUF3G zJXI=!rRJ$p_c%z+R;6aEQnOX5*{alRRcf{>HCvUMtxBC$lDfx1>K+HFdmNK+HFdmQ9>sCyigJ|A_?Nok#PlI=5ldbJ5I4}#7&2Up;gsPjz9 z(Rn7>8L!4`@LHUNs!J`Sx|FI*sk)S^OR2h)Q&DxPv}#c<&7AHsUFT%%>zG6S7Tk*4 za69h6T+G9KEWkqi40mD?7GnvP;w~)1a;!idk87)RJT7%SE_FOEbv!P0JT7%SF4c3( z&v6fafqPMrr%x4mQjsSQ;6Xfu-&!4woapnWjz+A0@gen#52;^#Nd4kN>K7kUzxa^) z#fQ`{KBRu}A#d@%(`?=IU} zeU@oXWc%X)9EgK(Fb=_?I1Gp52prE}SF5;TenhQ8s#Qp}3aM5h)heV~g;cAMjr=85g2^{rQpC@O69xe}`}4@9_`#4lcuY@sIc({t1`kpK%5L z1y|x>>wdNw2#W!;WP6lGHJB^6&%@g)^sQt>4fUsBN|6-`pn)a`imR>(X9x4Xg8zQddCl*yt;uQddBwu7FBi0hPJ} zDs=@^>I$gT6;P?`op*H5T*K;mr_}XMsq39m*E^-IcS>FFl)Bz2b-h#SdZ*O&PO0mi zaxYu>CGPVc_gq8n#{+l}582o=@@C&{bnVgD^Zu6S8p>Z11YNr5cZ&P^%#NRdjZx3u zQO;SJBYj?oH^pYy!fVhKkHg*O`tIL8^K~_}bDu?VwSg{I=qkt2tDF1EXrHCw)&7fN z7rX|q#q02Tya8{-oA72-+tYXTtE)b5YVM<6F{IUnw7QU17t+!~T3Sd;3u$Q~EiI&_ zg|xJgmKM^|LRwl#OABdfAuTPWrG>P#kd_wG(n4BVNJ|T8X(25wq@{(lw2+n--tSrs zX>TFzEu_7LYH8h$hmY_nZ7gy`$MA35ewmKpZ4BcWVgi$x!Zc=KHa5aDP|wxYdd|cq zcosIrX4o8CpnhGf<V(d<+AFaWUWJ|UYP<&ZmOCvu z2_HqhlR-IpbEkY9r{EJf6{q2JT$=glvMV%Cv9Dte`CD)+Zo}=k19LGC^RWO6@iW|s zMOcg_ScTx%Ij(hM6+>870E8LF<@E{(-Z>`U}cG~0f zt}?_VrZA0Jn2n9_3~Y>Dz3=p^c{?eyx!YKsF|m)~WPBW_;1f6%e}dESNt}*9#TocB zoQY52EPNWD!Jp%7`~}XzXYqM_0q5e2I1gXK`M3aI#)bH5=HqVDbgsm{j&I=a@J;+Z z{sG^?WqkKt{3E`Hf5PSXXIz1Q!Ik(|T!nwb)vWXHxCYh^#44=D8mz_7aSwih z$1`7ci$>4ZI50D+Hx|i`cqv|nm*W*Ic_ryi*cq?JE_e-Ii`U`x=y7Id^hVTELzKQ5 z^#&cKZ*{qJUdG;zcVJiShIe9j?16V-Pwa)gu@CmeyRjeMgZ*&;4#Yt?7>D3c9EQVj z1dhaeaTMywG1}(QI0hfUvG^d4!-w!;9G|K0`a^UAWq$-Gl0S*G-g}`sA(nO4M$sK5 zmZMLQKNY9plQ_~1I{XK&$A6;EUUdq5_DbsPmHasKZMT$W zly(zt#!oQ^x8PRXhTAhIFJGd2eQYl3e3sUx^I1~ov!u>vNuAG}saKDHWbuoml3@08Z3yYUO!$zIytm$b}%?rox9;eI@T z2k{Ud_CD1$6peH`7p4(P=fV^nx{{*r>dBbLZk^>P;NH|Dwj{H%TjSVoGv8k_MNh*# ze$`e#4Y%VOKMnVW8Jfdx?ABa!SlbxS#IrKny0z9E);2>uYopsme%6N6vooZgozbnG z-<7KL4eH-6(~~j^x^<3gHrnlaJt^aF*A(hLo}=Avjo+C$aQ&%x_sr(&kH>qEzAJOA z+nw=VnXR|Ziucz3?H%uv`Qno4@xGaZH`c}P&U}4UzUIYs-TKGx$?UylX1sr9f48A= z9p7KpC9WqvcAFd@oT==%K0Y+_<;^2?KjAA^73--SwffeE%-7vUh0LaOElKsGYe|+l zm98Z@tvB7v8}LTF32#RIx>cWc^&ZiAL)EIz8#+xNsPl%-p%2t~L#OEjb>7fv`aqpG zbecX;=M9~v57c=>r|AQA-q30KK%F;qnm$nH4V|VB)Okav=>v7%&}sTWoi}v)exxVV zc|+&W6Y9L7#;I_;>n)t%y3#pBe@ed>((i?jx^D>|!^!wKPQfSeCpZnC#Oe4`{Fz&z zuCLjrX~}13hW_mZ3p7KwF^pq~dUCY#lbFIZW??oq!ZT2BSl9ArViPRQqny1f@4(HT119edzi*b{qUZ`7|) zx-8T^s8+v1k^M5uuG^z?cD6qbz=1dj2jdVNior+hi+{xT@K3lL|BNf}FSru_imUK%xSDnT9oOLdxE4Rab@&flkN?CC_%Hkr zKSDhVT4nhdKfz7789&7w+=5$i8|Gm?7GOybwAy7(xAy za&!bN;~1ijfR&TNG-hEoHo`OTOl*Q@VN+~|&9Mcx#IxnzXe-j^U~4=V)f4LPswb4{ z38i{Msh&`_#~)z_yb#qB>QnWE@)Ef>+7U0s%kXl%0g~?@RzJK4 z^>%0F48Van2=#Vn`SMyKL5d=jVQPjLpiU)>vh3f-gbjXsU; zQTO_adD}Ko^{7%ks(cooL-nZ2QI9HriF5HqoQE%IJqM%n@mE1`ewlxZFJH!msNPjM zi%`9*(u?sGT!KsSReYUodjo%mZ{qLqE&Kz%jql(x{10?byEpnrd=LMG%kj^+0{?<5 z@vpcF-P7)kuExLP8hjtu;yU~XuE&4khRor%D|Gy1KSCWpUEI~jPg2KEQpZnH$4^qn zPg2KEQpZnH$4@dZ^YwWZI$E*?ScsqDPAtM=EWuLTg=JWdmD;w0zMoiDV-40~9oFM+ z{2ce-7jm!9>8l-nNh{swzTM~a<$gSX2k{Ud_TJPnljaODwXX9H#^$)q#MH*xZO|J; zj$GVR$5M&~pX+b8!B75gyV~!CX!nVqJ#b-9KP_8n^-)Ue3BcFR(r+B>OAlnioWZmY2S(`x9&o;}~KBlbFIZW??oq!ZWZjo{3HHENqI+ zusOECmUuR{!gEo-6Vvvbhi&nEY=;+M`^>K{ITBnH1kDx%KgNqu?V{P@;8N0;;pLg? z7Rz;Qz+Q>^9i7tp9i8lqSK~E!EoubQdNhK`8&D&d(rSZJBbdAeZ^hg2cDw_-q1vF9 z?2bL;g5WOfiM_Bl_QAe*H}=DOus;sKfj9^U;}9H*!*Do`z>#<_j>7x!0UV1D;sm)k zcmyZnBx*y?g;rfXhLiDeoPtl_RGfy>aRzHpyVE+=?&MQA3)KddqY+7JM3Nejr1}i` z96q0^XgNH1fwX!H<05ukAd5uLeqWxzuuGj^`6wP_oRNkC-v(+sbB9&{d!O8*LzaG-jfwriB(vQHCT&v zSdY8$bKHYp;9lH^U*Uc{fCupqeoc9g;5T>-kK?y2^Bw7v_&uJ&)A$4a5BxV~Y$QPa zN9icWGG8~lF%riRlbFIZW??oq!ZWZjHp%>==`@`$u}!fVHpdp&63@m~*c#8pHjX3x zKEbxd^RXT3y$#B5k3Yf=cp+YdKgNsk64d+U^)0<$US5Wm;}v)%cEYQ$GhU5d@EW`p zufrSgM!X4c##`Lm>wJm51G{<@(D{-TEw0!365Abn;9b}gdtqcDi3(5TReymCi5E z*{3U=R_}wClkst!f=}R7)SG~{%rtxwr{hm?2L23Z;!`*apT=kK=QtaGfphR#d>&uG zx%eW^!mB&q>j9g(^^B6W2{>gtHp)e)(yBT`pKq^^!gT^*6SIwEy-MC$5@T+KTFj%)CJ zT#FyzI{XK&$A97m{1<+RAK^y)7(c;HxEVji9NdCiaU14gJ{Dj}rs}-?x+ZGBq~7-7 zSGXS!;6XfuhtWM-mG0%RN6|f7RYX@tn@;vw5T#F``?soy`?soy`?soy`?soy`?o5+ zsnEKAtBSaPtBSaPt5TO{-M>{u-M>}&EQpL_h)GOg8nZAP8{rvvCN{yduqigf=GX#T z;@Lsa>ZH$t=r5jwt?^tu58LAT*bXni_V^?0fEVIL_+z|;^>@Te@iM#|uW%hkuO!_G zJLA>Z1+T$t@jAR7Z@?SzCcGJM!CSrGbr!_lj(SqJ(p|9|-ih6@2i}D}u@~wMrTUhx z^vJ$=H}=DOus;sKfj9^U;}9H*!*Do`z>%n@OKAP0@ID-kWAFhSix1*BdJO#fKcclwBK;_P;W5(min?a3nAJ67`vmz@aT-2})A6S`1Am53;VgU_pFuq%ujw>h zGq%6LXYo1IuPBxO0{#-`;)^&BUt&AwiV%%-zatcSgMDV>LI0iNU0uDs)v;7 zA*FgqsUA|Qhm`6erFuxI9#ZQ1vDEcrsq4p5*N>&HA4^?7mb!i{b^TcC`mx-Q`FpET zI{#rm!jCf_HO<%g54#CB+uG>5pTkqQN2O4#o?`_Z}-ei zXEik6(6v&vf1TCPU(~fyr-^-?)o>26ud^CX6Z<-=;WV+Yvl>nl`#P)PG_kL<8cq}Y zI;-I{v9GflP80h&tKl@Uud^CX6Z<-=;WV+Yvl>p{kMsaKtKl4a0H4ojR;BY9&LPr$ zKBLu1T_07H>H4VEdq(BsI0c`;pWrln5~t%&QFnJ;x?0yqt@csdPl9OHu-SU-SJp>( zuT5W$AIL~>6<&)E;7}P&@3vVT@z>=1)`pw$CUVxuNHD_b*kgFohTUw$YmTNKA!oHs z_QhW0++iclY-)&2&$7w6n1}sjB)Aj1<6SbEe8EQB+sM^8!KND9WUh<^J+TKlls7Bt zbnMSI{LH5QhfO50VR_0Mr|iiZ=O;VZ=q8)a*u+R~ZSY)l@5` zqfzQ1+ux1Gj>{mR{?lxKnmw3$$)6?{*p#(_LvbH*%+-B2fkUtpRDk79ZH;pK*)0#4xKF6kNZQ>Cdrxf8K z($DbenYh$uy-b=lCpsvtXBo<9YPk)6ZsTd2#CQwxZ;+ATK0bY%PoK82YFtVFPi)HT z4}$BRPVTUAYCC+6oL}0+c$*@2k}XLumXV+rI#Pn*5*dxHlaatR76z9&C(%$RZu!wf zLyIjT-N`1m+tjaRB%sYC_%!8q8wqapr}1qz%(dzFNPj~5EE}VYSzkJx`n^pS^64;} z_!IsI`2%dK6nEKhKlw%E^tB1^@gTU)>F|D=V*Sx3PG?aYu|a$~(U*{f7&J{;w17%Ih}mgrd}n-H5mzRC#SiMhc?~I zM$fk475E^sJ;_&;4uW%?6K`Y_?488bPDhEmtcLnI;GC@2Y_gqAceC*_a@;B+f!lL5 zJZ=-zWJr`Hw~)WWCRtm8+D@_l)Eeh!#cj&%N4Ir3oum}W=bTP8wTVisv*B2q)ll2+ z<)U$-Eb$F#>Meeu)3J|in6b$rHu%ye=9BMM5ecZ1)GE@CIzLGarhn#i)_$8HLK9vx z2&jPsZ8bj8pQiq16A#$rCUSmh(|5{9Kx<6VDpL96@ZAJ8pE%P?CjM&Ueef)DYHe~L z=?VBF8$LyTj!os+bc*yF$nxpNPKWIGM6uH`dW`5hr1`taJDrZQud`^4$(Nmzq&0>I zolZ<9hpkFDU(aVHXSR$4qh%0WgID4CG8%i=hP0Wa|E_+zfmhG+#Ayb;&hpv+)5)2IazE^I;os!_`Z#9vB_CTJ*Vh(!}ToltWCL3hy?CQ zg5XXWjXrL(*uU8woKAm5&J>$Xon+lcy1R{g8-m~#q@AZ6gF(~cCqZKQ~iu$gnhU&u)ChSTvJ+(&*>o1_gS zdpI4NW79v7f1XWHlSyhOUPnI5r06}8?7_rg=cn$n@vHcjTXHltPey|APN#>H9z&XP zrOHUVT#>+^MpFyPsUp3A^lbDVk1&UcH1uYvBIhUEt3-l(S!SjT0{X-BgU-pi&!*m% zLBO_Uw|7o9b&|E-=`6N9>AxN@vPaWDa(>79pRs9bIG_%L$bo>jJq?W=xv5PN!c-Z$l*LCxd`C7JjXCG`v$rf~3<4_Hx3X20=p`>*M^C`<6&> zkJQt*f=^Sl$wX&zF0e_LHyZv6X&d3=nUd^*^Mc{atCXKi*m>wBACZ#_&G7Zb0<8+uNhuUkHcSWn56WmFD!yY-q z=|}~(waK5`*spD3g-z2A)83oWhS^2Z>kon}oRh^~$iCR=_*9$x8R^+J?2RAWba$I- zWaIVZA49e|UE*|b6dT&ZADxb|{c*kX`kJxBBT zw6zWIBmX6vBu?X$EA=or-|#708)KPNwsIm3^ZYb9G1_7DSuYt|WuxEmtv}c#+mjgQ zbex_c`3ax?1^39H;kYkJG=*c76JeGTO;B$s_ECaX5pL9{(a=3uBp`A_YA?Or`Po0Q zY08!UkY%Wo#Qn}s7Td%ZHoS{;8R;i&`gzhHkpE+;A2vE2WBcPTJDu_xA`NFDQsvIc zns37wZS=T}pND6X&)>?TCKF$hzt?6{mh9`DPOyB|2$uh;P0t{IrA@ZCSuJcho&4u) z(xr>0dfNCKHf;D-jnnBB)ZBC~$Hc3PziI+J0DB2J; z>BQ4E<^D68UTL#iVJ{g338xc^Vwr!Zw(eR%(KGY_z zB7LJxK4i1z*(lo|yTj?&O*TA%e?uZA!SeB4&dG|{4rTOIw$?P&GMFq4YRUvANkZqY=+Y@mq?$JF0@(v z^)x*|-0>R*w4&Hlr6a*|FOzJ{l6f{g*QUND-7sR(wBfbCD*!l(4-$%eh`vPV;H zZ;^ny%{u0sEcSA0KFhDRNwz2J0X`jVvuQtB#A)gqmaOK}AKBz(Hr9iD$3-L<>vWd) zOC-48>10Qn-ecoe+XU?`q+AVUj8LD^?*269?GK62kP$g#n?vSrVZ*o|dxRy4i!^^F zSmm5xfQ@eE(}~y=o7iYi8|g2@fO<<&>QqCUp)CdfW*PQou-579zuLsFNMB)7)LtUv zbnJN>{)9C9D9X1Y-j6}hK}OT{HhHg&zeCPo(hYlOAfJx1sTBG2V_994jx?MPk9lp; zc)3kXwP8a<^VgFzofBI_eg~WMzKF&yl=|_b(_t^0o`>G!K|@Omw>l?Tg?sSVGH95q zM=4SAE$3%#u_;QC)s38+Y+|2{ced$|Y<54Swv)@8PX57W71>nGCK^gq;B=VeQ(8{^ zkkj#V$o~iFhIYP<9A+%pe|I|jT0U({{%{#IoQp`_>YP}ujW=w=MNWs`BU>0}zsDPD z>{Iev*%n+Uuj%rp%iAUX&;DIMYg(K8nm2ss zjJ45@(TtQj-q<^Z&?zb2~N#OvSi-9GiX z(Ug2UI$oCX?Hs>HuzsiCBUrzZHLZ_dqx8nGqtqKQBma>(QNP%C71gifdpAa3QhIYN zCO?eDt^W4$`q%X9=7aTb>DSFi>fdEMR>xQA)7A0!y4g4{nlVr=5+lAmEWI!NzP$8Es}741vg;SAE?kHDPyH$l)`4m@6`WQ7X0(x)k4{(4Cwea8 z6ZOl(+mxQ>E8q3)f{TJK`kM;_Z)sb$sBJ{)x-OAlX1=c<6Ir5t@?7L?eL6$+^I9W*7EbBXOVNB_D6^4#ri8FX%REjBJ{VSGxe>h(bu#M*GBa_!uo#E56Rh>Iac2_ z_N=!veuF-JR8LjXHmH<+>N|yc2U&f`aH_X3__0e_x7?+yyWXX&yWXX&?dMX~&2%a2 z-gGJJ7P*vlvt7!%r7mUNoi3$vR7x$WQr4Ealy&V~N-d*O*5KO?cx$^8I`!E&Lys?bBSxGyTr8Gv|T)V<0u6@fTt{vwR*UoZ@m7@~ZPH~BA7r4Z= zueroE2VK_M(JpK4aF?}qad5HsYt2rVwPuVXta^pZS~JFFt)A|(*1YYq*5tUXHS1m0 zn)h7RnvE`N&7&@>a#U6=>9PhcYfVp=Rm-TX)yG}d>fiK{28#2u>t9lRV2t3K*-S5J1itDbYYt5><))x|D% z^;Va=dacV{o$qp2&vdz!qjGCWmAksJ%UwOx<<>GTca(Cwz(Ms%|EGGX%UwO+<*r`l za#xRXxvMK&Zsn-l)ze(=>g6tX^;Va=I^X54R?n`S7hLY@H(hS!sNB_yT<+>sE_d|? zm%F;L%UwO&<*uITa#ybnI=b9d`&{m->Fnu7!An8U>ZU6FtJSSs`l{(JedT8sv#>6N3>SH0%a zSFLmDt2VpzRV`ims<|$G)f|_;>O=irNTsjb@6uP!a9Jy7xU3ZmT-M6S2l8qD;v42m2bPOl}lXK z%9So_<-;y(K&7@v}4EROH9c z$sDR&9Y0q&@9Mouhbu?xy-NElCQvKe_3V^`mE-hFLhHJj9{*eBYaO0-%+yy-46jmM zuG5`&DofDGEu*~9Eu(z2TSob4w~VrBZW-loxn-1ZrhMz%GRog|%P9ZQEu(y*TZVGf zGPI=1TJ~?ZjPf3C6Iw=XqU@;KMA=cdiSoH_6XmnqCdwDPO_cX_n<(GzHlZA~iSlu7 z6XmZ_Lz-22J#G`_tKBBbr@Kv*FL0Ysj@m@|9Jh(`w=`pGQE@^uwsR|f(2VW8ik+IV zwXP_OpBJ>K)S2MmoQj%wyWmF^d*T=1Rlx-n`{P#!*%e1zXXU(KuI%4#J>^r}dderd z-ITwpX90J~d{w?zE#`Pd6YA-p+RBmg`N6Z9(`5^(vxAX@N;z09rm`Zg7IUoP_Q>`6 z^ij2&;_{DsaC$JxWX-I*FgU!bytLWO)tQHjC_wfw+vstvAoeC?X7+S*v&LACW=`4Z1)ccm0%$I9PUl%(e`If%B(zT_u zYOC}a$69%(h-)jQR`_-KwW_V}cTI4_m9BA}?MkVIZ7aXobynKZ5vP1bT zQmV7<Od#bkfmG@KhRhB=a=&Rc`O3}Aho|CC9o37}qD<7=r z`@DRfqOZFA4aME=@)@c*tGFv`shaz$Y@h0^th}w_?#uE=RdXsAb(RXc1g$ftO9!gX z{#lmqIxAb{Ix8twogFQktUB9Q-qLkeHp+EY*57qje8P2BHd}ReqHL|}OgXn^wv@e~ zI(x5dS>yrL^aj=3v9gn{Ieq$;)>h}5Q(84w(%UsxHs3W@QtX=BwZ=84_*Ko7%y!Kc zpHR(hR^+PYlvd3tf2Ov!iEBvaN1sWs6jEUz9CX&6St! zRn66u_E633DZN-VS5@|jYOba{u9~ac)krm0QC6&)t1GKe%~{o4d6sJK%d&%RZDnt& z=Ju2=SIwz(t~u4uHLA0dC5d2+w&gYV+NI4^;_r8DSHE?#q>=iqQ*vqMbkWhs+uG`7 z>b*`BA6D;ma@TJ6US$dQURtKkYl!S)$#1;Q$cfC!(ycyPDr=;prC`_L$SEyzqmP-2 zPP$hsE_AO}T_CK;yKZ5uR%vn`mSoRaMvkC?#bfaQT;Z$beVd! zlUj0S=G&5{?$wGLYP5%YHJt+4n912ijTSG6&DN(L;kBKr-DA6^$JB3>Hjl^T{CGTb zeAjW+@$OwWI)00@9KZ6GOl?US?awp7!Nu#`pOqy-J*T1QsC%`NM!~hNzoH!1U(ra{ zUvaVPuXLX4uPE2`CslttHOtSOE*ah=DPk$SGxXoo^t)|T<7{LTIl*KTIl-Qxxz=ArSG}^iq^aSin_Y~O6I!$N>{r6 zO4qyoir#kpm5g)!X-U;z(N5Rj&O*1yqQrmIUrC``WKloYU(ra{Us0~>uk>2iUvaVP zucWW*uXK&;ujG{LZ|8Q+Ec6#$i%P2&ipSqm4hY!R=nqwY>nKW`F`gF z?XjapJ!tVKwXeP{IjDVgq_~4)ytIk-)pwpIxEblK_8e5VnD|s`nef8N4*W738`|n&rAHCv z=zPm(H@p5ymg#KA{*vW7+i|pHl_L6!lB0^{vXZrmWzWm~SKIie-Zk7I2$r~#D_a!sb+kL+CZI7yj zx2g_~DY~_H3-+rHPLw>SI{3W!xa*+!l-p~`RMo-hk}+zFXO!rqb&xLc@z-}HkGjp3 zB%)^o$r7JW7@?@t)9FjzQ$3w3nWq}k+KAS9s-fpgT6t_OJQ$m;ZFtxtY{7oj*4HI% zRZm4F9pceUK}pAWEc4OMH`Kz`mvmAKTU4?zp4MMkqQ3BG$+hbL4wk&F{_mUOLiK+~ zN^aJBaDQIX)lbBe*9EOU>q0B+=&`u?MzzJQB|YP}Y7PC|B0npQugDzU*-7V*k|l%H zIy;q&^sKJ%sQO6NiCb#PRMo4>tKags);3O2?S5FiR%6V`!ZNqZf;Jvk3K@m-7kT_G zUgfc*V2WyYfALV&?v~;g)F+(yY^FyM<-2yAT+eGImz1Pv5 zztqvK9E~?xgMNuvywrQ3csX_7Ms>fxcz*0R{wo@FP8Zg=b>=U1>nxyk7EM*HA1R*T z^LfQ@`g~sTWbJ`(iWg~i=$!M(QPdai^gBe0m#NhrDxMbC+^2YE{BK&%cD3C@#j{n@ zLBVj<^r?cms%h0v_@Hv8cuZ0a-{P9ie^fQSv9P^I^TNd*&GQqU0TsUG8BhWJK+!dQ&EAcg9~Ek@o;hAP z&ihB-)%>Vvxc7|G+B12xv}YC;jrKm#dNj%xcJU~m|FHK)-fHiS{Dk(#w!*&Z4Q?tL z=lzg(vG;>M)qcpE<+@jn>pr2n-(GmTd)C5tRqN#i%RNgf?BQ8bVPCcK%A$$!CR(zo z=SoFWJXgve>A6zDG|!ca=6bGFIM;Kf!q?Q|cNI<7^C0b2a+X^8?!xJwJ;`oCucA5X zH@+&^>)BIb8_%8!Cwlf&INJ5CTD?OxeLAnXd)C~yU5mMIN5-k&?dm>Z$2#>1-{&}TOX>(Fx2um}a&^qcGT*Yc6Zjo!RMYy^ z=gU**QLU>-wVfS2sww?%%vd#48$tb#(lOMSr?gtC)X0~+(MLr)JGw7Yj`|9jh1saF zP&sFyj+ycrN6#d!`c}Sf`I3{S*Uza@V+r$zADDFn7Q9 zgWCO$X>Rv<9o_Ep-f+9$(b(-i?>)Es+=*`Sc}v{l^X9p&Z|~%`o;Sm7J#UQL`i{nK z>v^5r*7Ik&txL6a*)OxUaE*S&Zu{c^9EgK(Fb=_?I1Gnp))rLhEqng1q9yN9M~nWt zq9t#Q+lA7KmhI2EAIrPe(V{I;wB+6FXxTB@(XxHLqh&{9M@wE;*Y5UCjuYi8PPRYi zI8lz`M9U~n@_IT>@>V)da#uS}^7=VW@&-Fj@}6^q>=^3^k&2MqS&oprk&ckOg^rNC zw;d08&o~~W_dpxRL+)YX;XTJg-c-edO6Pb`ZFkik__AQR`}nPEG>)Ds_(XG$)7w6A zgcRJVJ@9S8QeREj`h%|{Y<*9AV{gGUjjShg$2xwr%$Iz6SiSOE@1<=Ay??glYux>Q z$4>2^V+Fkpp*Qe1$$;=@=*| zRt&T%m>gc^?`kHa?a_WvZ73>KMn#3n7WEwhn^4a7#@s4^-)_|Yi}If z(LQ)Gb85#p&j*7*`{C4%sz6tAww!d`Z*8NxKcYTOXA-s?RNe2*@2gt>B!4&Uc(2>B zmefo=zf`UH$c_))nw6v0EY+Ix2YOWAvdnFIOKa8oK`p6g2j}~2<+odhyJc@_?UpU| zwA5`qRo{p6hez9Hj^~e7tsmb$)NOssNw;+^qqZ*RWWL*er_MZlm#_2g+Ro8#_gj{! z*7xPNbgge4?OEj3>8kYu+w0W&zuV^4|LwNHalI)q@A|mjl(?fxF>okZ+b?#rLlXVEsOlFZ{=%7ly{NG>HI3~pD*%SYn(ow?=xJ- z@@FYFj^xkL{%M*&R}u34j`rbg`c`Z0AJtp(vTATK`)Tp8!Pos*`8dc;ewULuPR#IA{O71C-DpI40 zaxzv`9OUUDI%)k+UI&jV%GanO)sCe`m7F;qRq_%ZRq`5nR8f8wYE)5Lql(n1A~mYy z6na!qTBC~8sFK&z5tuX4qe{+OZVNdddQ{2T>`^6WontRYvjly2u}2lro}g ztw439Q6-tz%6m0up!cfO{>)kJxXOt;qVn2$RLPm{Q6)#SDc4@mL;L5u9V^|N<=*P? zB&V~ZD7Urxv|~Aqz2A1MR}>xIu|`pJdb5ucPHtJ?D9UZ@{iaX9%$)c%S4UDuw+vJ) zoy_g5$T^*Rk&hN~QW5omo5yKS9?QL6@pE#=M)g|XZT5N2Z?+)++5{eYx`;)?|!cyO#5)h+uDce9cA8!n}>TJ?#S^z+~Mnc z$=u#P-rYXVduhi`?Vs;*+G+oMmwTD^&sn+cbkwW$XrE{eJ_6ZMqq;wq+h5V>WfYBD zwnxw52{&7&1v-|qN9`)6Ek zRDGRlUVBM(qP?U#(bK*ZMVq+(yNT<+o7y^Bwlgw(!d2i+Tm|05Rp3oC94(u;3cQJ{ zz?--VtQa;k0ofb)wHaTdMB!p~9QaLT6BxOmPM5nYlEfR7hjeQ%lnPJQfWeM47vd3^FN$Pa{ zUhmiMpXcM_n)|-p_jP@)_viioTpO~tAbSh4w=5YMdkby$7G!VXz3eSZJO;hQW6(=H z2ED{%&`Uf9y~JbCOFRb6684rQ&0}v_o)>$|k{(e>FJBzB%95&4tCVH4|6IAejQyw1 z@|EmAmoHzJ!9KIRJmU(z*4MDDmU#SnaRGbOxyv`l9<{`y*h@T$z2uS9oouV)MQc($ zQmLXxV$NrAd2Fk)*eiZ39>ZR7yl7*LC&#?Zk}5AbR+Pj3aJ+bW#JA!#5#M-C zd3;XH{P0Rl@pO%`Wpx~zNb zSB2|ierfTfh=Il5L=0Tki!Ii^=+%gU#cN`gsjwg-VbR7^2WFX0EV+nTrm%T(qO>5g zyCv@u2@furACYiLMf< z7)6&76@Ob?HL|!x)7k!eN|&d-pZvDy9D2d_vM!uGa!OfiV&m^c=hGJ|N~_Zs_LlXC z*tldWePPGqiS&gZ%G$H{9$&mM^5vov#K!-WuIA`qyq5^M=ns0tuce3R3n!PZr!SmS zdX&C!`l9yqg;Pr7UOZ=%?w~K6U6jQ(uetaSwsy_Zm{I*{@n|As#nSEUlNXdd!M5dd z*tUEE+xC>w6YO>IS7wsS9wAN^l%^?6><%#?lLp zF3q49gw2zuOD56_ek_?nm36daI(=bHshNK15&FUd`D5q{TTAAWTXrnTVXpD#f;zF! zmlVFX3CL2mh=n<<*zG1F$KuJ6&{{E7U^o!;t+sQmvm+T_*98|KOxyBz# z4o9RYi1G74$uauK(vm;u9lHxw(mM{8UPSMxC{2&dv-kyi$F9;cddJSvmGq8BN-NVl zo-K{D1fDCc#kM_OL^YKlKV@C#Xh;HI$LR7>Gf=%^3o<`o->PjR{DTF z=2o`VvC@{6c#cKsakkiw(l%tI%S$^}`j~8^OQlbepO#k1AmWsEi+wnsI*mS9PQKcy zbQt++r_!Dom6ImqpQm7-h|&c|IJ+yWw1RCuq_jWV{H4+tGHy!R@%kpo^hFsN&5{?F zlI0{TONY}7elC5JUa*xsc4qQn>4)U2P04TPC4EaLWz0|dl}@Ec>?xheQMO^FF<1I# z31{ad155Mh9c)>82kVo5!TO|Mux3*C(=UE5o{{m%N$uh2KkFPW4Eoa>vjk ze$C%UUpTsS4}IbI(h4HNf0rJK3~6x%TmE>-2)6vM`IFO5PmUE_koFgT)3K<;^Q*I+ z_b%n=QF3%~A0ompCD~~o(02y2&5yI>!&I%@sGyG(JKLHQIn&{iDltYZ{gW;KOGz9X z{)J4qoa`|p;sW{bj^sq{F5*JGH!|TWX-E9bV`LSxW4z$=kEc#6sZNZzprjr#;;fRh zh!NFG&L>8kT~eDE@z+J3sg`rp>~}En;=H1f#EZX_TuPt%r66Wd>J-!H4n#E4_LyVBo`Z%!Zl zrG%rbtfd$ce$6c*I=olXlqazKSTev|cQBFS=aR`${p2=hZusYd`a})Z8~yQk?k>iN zpO$1)dO!KK@J92kc&^H!l2-97l|}Ty_&(A5$c-x{dlvO%1o>gnt&AW=C7oh!xTJf` z4dczp+7gcGv-ZYEMVfmO(WF|*Tf~tUa>vqB*y8jQ)<1EC^-O zb>hg+*{g{oVe@2qA=!1Zz4$Hq&QD8QMBmAoO5gczX^$9LipSG~K3RGRJ!o6;`$UuM z!Z>pA)6&-Trk@r)9nqxt(cRaIClgJ^<{pY@vNT0B z`M5ZXXtH(b1@y9?^P{5LvFKHL){3R)(5Lp~Od^`>UfP8|wPR_ch$agU)2FsAjU#qj z^Dm-LjmzCZpPE%roj$cVdjwnl@Y4DbP4c?1JvZfyV|(%m#F2$d&tdDW%j?0`+mf?~ zt+%T<&K&r?a2*k(BELToWNywLBFLP=>)CFB*729Jc3A#l;!q zFAJX_rkuZU3^C=ar8S5tUllJWrhK-vF)?L)!OIa-vIj@zSG+nhKct7|OpTf;w-d2u zPTm+|&7#6`Voh;wF|lUK(hH&z&f7@DnNhr+ezB%_E3sx@?!JgM#Yc%Xg?y7)$(a1> zi8Ws?ZBMKz$nQz4DPP*0So3Ld9G!l@_zYAY=?GydcJ)!b@TdD!iOtv!GZ_=3;&3*Ec?wE%L?blSXP)5V_DAj z7|RL^V=OByq4)h()IY|u!f#?MD_j#}*`oC^mK6+-Xi~T_#a7m7{+Z zRg1B#s8)<+Md!v?R&-H}Wkq#jEGw!TV_8vy7|WB1BgDq%ulBu{j?xE z`VoKKOYc~p+Lx@&ADsG857BqF(vSYTa8Jyl<<&{+zcRG3Q}hJe|KP$3w*T(=Q`!C#=Iv)h zi%&S7s#AD~k?Z>Ty`$2Z+l!IwvIQ}6U04`rfc{^>VK&Lxg(qU<%AL;0RlSHyDHy*_ zq6ZyV(2Gp>=LPE-zb-1O%E-m%(5GrqgPf84oLiL~cW2Q#=@%sPiY}m+?aJXSviP>? zJ(3MYkEB1SkFvF!qz_1bo7XyK1d1-9-z_Vm?qqMfJR)GxtMt1aMfDl2_UESPaX%L| zqR0JI)R@@!d-f#}`-+;=<926Hq_6$HU_E{9r=k{>{>>-Pr?2fTdX+J&a!y3L-*Y3< z9V>h*`r5*Gqp#&WMx;9_r%kTcWL(MIA^|SWXAkdYEA{c zjPDcGQ%+w-z*mcmTt#EJ-y7SG{>4^f9AxWoZ#LEveT^+hUt^u{gmy+lE&*UPoV7Sc zLu52uSU*O?{9!Q~7Tg%4;heG22MgLoAIyI@M#F-$Vl>Pj9;4yH!Waz;n#O3D-7Df+ z{+rPsc_pLa+~YACW{;22u;Ba{4f8*Y(Qs~g#I^h*F&gHNi_ws=jL~pDM<B2R5ap4y5$wH0}4EArIVMU7%?o&QLTt#dZgzve8wJjT}9akk#t{E8S` z=Viy(nms%6QO zTl43~*gCs=jIFar#MoMp5o2q9UW}~;_2_-$7xa(bH|uzet@(>%Y{m14ymexPoijE@ z*tsn;E=rxTs4?dUhF7O*E^3}}P4d4*En?)IHzG#fg==EuojX59-h%4UTjwp0kvG4L zKD#D=WsJOYH%Da5-xDJ*-;L@)k5CGJcxBIY!?6@)&s+^pBBu z-eWQH7PO=1vVCLZonM2yy3<4FWJM1}j;<}ZBzow~ee}>nd6QE;lH++<^w2H2!|0p) zSk9Q6H=Dls+q`mm<-hW>qgUn?M1P!JL4RC7yG`2Z$%edQdLo}jPyBV}`4JgsOr;Ng zm%lOXh2)cY4d{uV%$ZE~zjAh7+6T$5+4Jd@U-RA%Qzz%orEgZue~Z5P`P?4#&Ea{= ziJ0T_C#DsWYfMQi!RcwGT9%B-?n738yrE=X^QmvA52@PSwZR1Sr+Zzb{` z$-g}Pd3tD^t3N6KoAklSrhLxTWZhPyqWCty5f#PvbKW8@Z_dwQU)-4An0;|A->M)f z;S&mz`|_C&jNeO)UYj4s5szdKV^8`izjZ|F{Pyfgt8;r(U9er)pT5n#mFnWR{4SMv z0%-pJ*sF4f#rY63y0KU7&5w+A*PLSZs{hQI%U-o-c17$}`Hv9CtLBbluiBa0k-h52 zd5hVrCgiqculgka2`Z3}^4DZ!Ca>qViE}AtR*7gnqYeAh!FFZ`_s&$u|Lh? zY?f4N`ZuvB;YG>t{DD*~;kCSL5Bt#1`9s-<@@7?KA6h)GjD6@({;Sk8GxA5qnJqKo zIL^BK(dM#$?S^LxvOV*fAmK7F!~{51O1KtjNZtoz!pi3Po*V4 z%m zdXZkNwUh4`M7@7}PF`B|WOvT{j5lEot*N#2Og&4_)^qe+Jx`gRT(FPv>A$mjrTxQp zGi@IKSK2Mf-gzgbaj#aqO>fsbw1wWOE%mNsTh3sP>4$e~>*Sl95oGaU8*QuY^j>YR z9kip~r=7I3KI->jT+I16ZA~im)uFVt`kk)VP09Y*BZ)UZ&zMW3IX-7`+MZY*bI4g! z(w~XHrvJ1%XsMRzGF_e=n>H-{YyC!7_?atpm9Exrb&amob^4vI*A2Q+ zH|Z9>bFa7qF>cfC`n{Ix54uCicv*Wt>V7?-6?#xPj-557(u=WmD!m>{INp0Wr+Y+; ztg&(a6>Xx$SNj-uQ(sMH+zspN)!I;RA-Al|NO^G9W9;Q8<-DBPH2Nns1U)D7`S@Ql zU(g}EzE|2jt`<$r=*4xTAvr0MAz-Ew%V@LZF(O*86h{xRik_+*Jzxn(53rhY^nf-; z50E(|tf4ivmJ$=ii zOme2@%X~4BeR}WgZ1(BB^GCB!N4uuh(lhleJzLLF*7ux8($2Mgo>HAo8OAd@`M=n| zcg^U|{vGXR+C07q`}fiL+u6Uv+w^w5LtE&b+EVXIrTB)e^lojPIj^=8f=4*j2Qfdm`Riuk`i5BZpEzwdf(`CBc{_}Ne!2DflD_pWtSLtg1`mGY< zr_@a&#^VP5+COchZc2Wge>iQk?Jc@hx9N8MUKta<`Y>&$?OnQCk77AlFm)+&ViTf6TP-$L)ymU@@+Y&ibPv*EC{-lJ`_t@3O*{>roAu)TKB zj(VSV($3mNyJ|PRU%TrA{+1rLAJm@ukoMAt^%3pu>;I{oxxsgM-1d{&hb8^fpHd@0 zdOw#xtr7e=NPo-@90sXt6HU5-rs-U8XD8*55F95w23MdZ5i!54c9x>N@>S*Xst| zs1Y~5Nhcd(Yi`wTN;brj@3mZiP_iMG@3yz>;kWcp--~(a`&eE#@*~`@2ed*DD*4fr zJ#oC~i;0!!X)}7ob0lfM7M~Cq)0Dayb#QaWr5Z8kaGdjj#2l8_(_2#Y=l?-|w0A)| z`B7?0AMzt|1M(wAUY@MLx5_j=WJY}GpJ`0s$qM{0JXs+$CS)2D;(3uQF(zah6XJQ1 zEHNfz8WS>&37N(Os;u~{F(K2KkZDZFG$v#k6Eckn)L`*SV*)i;w2cYzyhz%{giK>X zrZFM&p;#L{i$1>dKYfi6AkzpC&x@QgER#5Zee@~qtHc488wcX4k+g{eNE|@o01^j~ z2ry+&hk25iI=CYOuqj7BA%-lUZwT*YHgs`=(XBNdCocSecbjF`lR;J zr?jv3)2Fq+4p8FCXE~`4lfo}6QXlEZ$)<@Xr9QDuKA2UOD)E)2TBgf%xqhwR=n7q_ zt8}$~t7~+vuG8;yy>8Hrx=A9p!()8UiaN1xKZ+E1U>XSBaQs{`~o z9jMRi3pz*#>ku8PFX}LTNnh4i^feu>BlHa&siX8weM{fg(aN4O;bZdXkUa&*>ZdwR zKhp{NxlZ!Ell2RoqF?G%{YtZRnoidlI#XxqY(I03&eeH3Ul(Y$F4P>&)jZAD0$rqq zTBM70i5BZpEzwdf(`C9sSL!PLF4_KNMcR7ZkgT3|7Ws0xS-0p`-KN|1d)=wKbhjQ& zHfODi?0Dj_^s|#+r=6dEnO?3}XkEQhd2%AJzsh!fZJ^gEXZ!LA*J>lZPMMu%`3-ub z-lUE7W^EF`j_f#Ws?D^y-lDhaZF;-jp)K@IZK-!@E4^D=>pj{=+iE+#SKDg`?Wp%@ zC+)0Vw5xX0`?b6F&An5!|J27^{GzN)Y3aD81z=o>mx|CMZ+R+~BS z@NFHf@94Ywp1!Xi=on@0nsNx#O$Lj>0sGsX3{f|!8FLa82 zsZ;eU&C+Q)U1#V_ou#vNj?UG2I$yJOq2_3=<|UQTF zRLgXku1HF<_K*dKt8}$~t7~+vuG8;yy>8Hrx=DAtt@qe}_S(DlMcm-YV&Q&0pcQ&h z55;z=G&DwSGFkRVj!N+Qj4H{&tcr|NwQ4eV(qkE?X*E4v&q(HF9m%MnHMLH%BIQO3A1@- z8Q&+fp_;An%(7T=onEgu=#6@lHr6KEEJjbVY4#?vX?_>kbhM2jWYe+47(zB3ZDR=8 zbhM2jWYf_$hLBB1+ZaMN9c^O>*>tpxA!O6hHinQ*N81=eHXUta2-$SBjUi;y(SAUU zBxKXE#7IIOO`Io>jwQy5Ok)MlDPv#eIb~rVeM$5sQpVQ}Ki)X$NrtM1o zC6#*jg%o+vdyjF)osc{T$%BwQ2+4zxJP65vkPHaP?2*hK$?UO@lG)QHvqv&}B(q0y zdL)}is^xdT))$Jm`h`x>FLkPZrCB;nr|S%zsk3yppE*b8>O7sV3p867YL4b=p5|+TF496R z(#5(&i*>1%XsMRzGF_o7b(OA8j*Xk2_FeMZgg?m#!wt#N2^WzMhMRSZZq;qNUBB0z zx=VNKUe|y0*o?I3u^DO6V>8mC$7ZBOkIhJn9-EPNNdK#c^@#qgN0Vdkb&6|AcqP3W zf7Rpqo1W0$^$-1@{;5frp3*esA1tY)8JejlYh^t}tLUj(RZr8?wYr|6HMFMI(lhle zJv%w^(bhQAnE#iki0A5gN+rq?Dp8~oMJiFG62;m|B}$u06sbg!N)-RDm-+dZ>lIp8 zuhe?64#~CSJJi<(dW|;JYqgPHr`%nG*WaKwDp#Jcq_N(tO``vhYlls>nR0(DmT=q% zIc|g;H$sjZA;*o7<3`AFBjmUd-leVdZf&jiXd7*-?et!4uN}0b-lv_kvv$$0+D-4* z?%G2i)Smi~_R@#-5hd4tuNAp=tcQOpSCRA0AGiI4+u}*v##l0L;vN}y*w5u$#lw61 zE7$wd9-z0lkAL-obvH$II!jv&{fAZPa==Y8O7I$XIrl_ewe4IQbY{GM;> zzmgMUc94CCZ|i7%N8i==^nK-=Q(nonF8HB-q?~O!p@Qr?dfHh1RLALO$~6M~b%JtV z7TTZdB<0F7mQ2iJeTR#5 zMRI)1`Q*{zDqXGL>Ka|E>-0NauN!ouZc;LVcVCFvWF!+nG65tLKr#U&6F@Qnj7%US zJu-oe^xba#J@%!&_PTu$tI5H`{dzzv^q?M!ty5_zpYT{*?fG5?nK(U`Oq_RRR85Xd zI70RvR@2k<3}X0kvhT2_)=4VHtS0Xc>n0Tw&L!8z%xmJ4GaKr)+DNa{>-7e`QEyV_ zv3OS#WtQ~)x#ZD&EAr@Q8z0D{V~O#BJUZIO2lD7>8z0D{qiuX3kB+wSfjm0e#s~7~ zXd55MqoZwnAdil=@qs)#+QtX+=x7@s$fKkEfEp#pqhpCtg7bzLH_4_MCF9J}_cF+- z;KBaxNpFX4g^;zYtU0(m35)H=eAg2z8M6XEO9cMMX-Z9Q< zK*lj-97D!2JV_bNXfv82qZu-qA)^^GnjxbZR#V0_+Kg$)n1+mL$e4zVX~>v{jA?kb zGN#dH9K-XK(Tw&5%4kNL(F`xv+R5<`zF|K7!07(Wr-xT*eZ5*6=rww+HqytHrz!I; zo~DdEO&NKbGV(NKKa|E>-0NauN!ouZqm)VMYrlU-LBs&=S1?If6yJeQ+MfZ-J@LFKI-Mv zKHER)em$TSdQg7}IeX-{*K2Y1NLWK_YAroe&(gE?96eXh)0<;Ea;`|&Oq<8{<$8=m zV~%sCN7zQ&YCFAG+iM5ysP}0n?W~XbX^$uS#{5a`5nr-24%ZR-hK|%x`li06Z|i8~UWg-WQIUk7=ve(! z$LVJ}K|j|?zV|=L{xMTI(<6T47dl11)Tzo@$NV)*r|EQ^p)+-s&i0e%=vq5=ZT+P#bEzm{E9l7{jMY>p*Xt6HU5-rs-U8c*E-^Q$qbJ|B%OIzWRmAXn-Cp-V0 zL5;F`%w}qoa6_`|-_@y6!cEDtG4rWZqP<18>Nef3-z(Q9zxfE~c*HAr>25um92^sO z5IALI8D|NfK4vDf*vCg!FpC{tu2*PXy;AGNyU48LHTAWDUZY%L&+==vkzS|Q>kWFN z-lUC{J&Sj7HZXTyN1^^)|g-@6Z-{r?%9)w3Xhit@R#lqiwaF-mC4k zgLc&Ww3Bw$F4|SQ>HXSWIV=6&Rk$i8zW;;TQyZ|4&T<%`i{P<@9F#cfsWA+^&=%pywQc6I{MgH{Zz;4XF6Ud zDA%g-%FlI@{zoV47dl11)T#QFX6ZDYu3V+gr*V}!&eGXBN9XE1ov+!tP;)d_^O99# z7L!wli?k?NH>Qjk?Qn?}>rySzQZ3VEx`OwvBohx;>1zE}*XUYZr{C#%-JlzFlkR3q zRiy8+|LnDo?Te^FHXZKQ16rX6^-ydVX0#bY<2;SmJ63u*q82$dJ)E4H_mWeu8dFYA z9ahuR^$grYP94_NI?0wX739=m-DEx1(-}#{``wwdJ}~A8v(@}x%vOiyLd;f2`#QZ| zZ_pd{CT*-ul>36dUW+q87?GK?W`7~q=G&5MN85-&t{qE^806Z~He!%#N85-&t{rV7 z2Dx^$jTq$G(Kcd`Ye(CNL9QKbBL=y4w2c_#+R-**kZVWVh(WF$ZLUs2BL=y4w2c_# z+QffyZQ?drb}TnCkY%$S$+Fo)$+E*f`jqz7h!16%oC}W;8OkyvGL&UTWGExc4xf)L zOO}n?2`ep`J8C88{fAt)gc+KtC+W#rSx?a_da80BU+*Ir2vB6mvs^o=7n_gqP|ST32t^JG6z~sV(&`ZKZc> zYrRL?Xj^Tk_iB6XpdIx-?WCQxi+0s+dcQu9O1(HW)gzS}H9z&BOM2=<$~CQ{^2pi4 zPn5IIUT>5}r7|cdjY6v|IHyjrGgF6*&8?`vDYCx`Pz}Ct4Ll2V+ zhi$a2w$ppHy>`%!dY^XE&e|pU?WL+|UA3Fuuicfq>XB{j63{9$Lgm# zPCwHL`ngW>f0?Xb=oI}@r|MUlrPFk}&QRhvYhaen_A}?`T%D)$b%AEnTW6pCsQdMRRw%~? z=))WvK#mRIVLhTh>(NwdaLYJ0z+aE)uXcvsUUMI z$eaqEtd&#ezWhSmVTvWprr@c{Yzl2=Q;>NSjOcVM?l6K8osOkPbUKzE(dk%vM5klv z5uJ|3Q33pmMszxs9?|Jo+>r#CM?vOMka-kj9tCUb-}DmwKaI$LERG5w_x2t#JH1}4 zCF%*bGWA5rIic80n=4mvv4ktQkSn;5E4Yv=xR5KjkSn;5E4c73ZKZc>YrRL?Xj^Tk z_iB6XpdIx-?WCQxi+0s+dcStp9{Qm6)Q7Z}KCF-EqpX3#^v7&J;o30bQ8%zxQa6Op zM1M|yG5MACGfZDn?h?w9SM@a=uCMC|eM3j;C_n#A{lv9BRzKBo`k9W`2|7_f*Gc*x zovdHz6#Y`C>Q|bj({#Gd(3v_*XX_lDtMhceX6r)D(Og}TO1<=I`bu4;tMyx5qic1Y zey8hogKkt}1)sCUt*}+M>302Ii4tt1A9RQA#A7kvjm&JZjrO_)-A^mIpH^}|jdP_3 z&#W|CtviBu|gFdm?#ywA~ZQ)1&R4NS+>T_eApaXuBtpr$^g8 zkvu)x?uq2-(RNQHPmi{HB6)hWA5iy4^7L5Z{z#tA*g>Ao7!h;ggN~7{$6wtO$Mq@_dvveAg`-QnlA}w=(Iw>Q5^{7259%*^EVe?NZPIrlndXs! zwaGNYC$*10rG2%ZKCRrTkXQECXO-*bS@N6?)aUgD9i)SGhz`{kb(p@SFY7D%nhw_y z`i73wQTnF7rElwKeaGMRuD+-5>jz4v$*+!{Fp*62r{{)|X@=Z)2gmBCI!-^+3HrHC z@_(7EU+5J5Qm5)ynx)fpy3SCZ7s48wrL+CaIXYM8>3m(F*}713G*|O9Ukh}Ra`!`i zOOYXQi%E?x;I>D4FKZ{p0+TV?%l}w;OIq zjtuKa)){U}j`e?mtTWnMbgORD?fSizCnxy)9m#+BA9uQBm+sc+)f40XJV@4wWSvOX zi4{uLNt>(_$vTm&6OZW6dNeuO@7%~bSx(l8WSw|ie^au~XXS1WQ)+Le{ulQy|0l6zv+RF!8Ri`M|b)jjtrFcUz7}#_Ib*XbZhlTk2ifO7GUzdXKizw%Shb)%My!JL-MfNjqy7 z?W*1Me(kQ@*?CAeX3yh0Kd3$RA?>9P>m$n9DgBNxdmd}!pZb`~8RJ+&7R+{GjH7K# zCKo1dkPC;5aV%$yL!N%v{{`mIqy4Nh-qC(e2kP@WNC)c>9jeTs4;mkH=*S#8GKY@L zp(AtX$Q(K{hmOplBXj7;96FLy^KHqg@n6aD!Cjb758u|&`i{P<@9F#cfsWA+^&{n; z;DbJ9K0SKfSp8JT>1R4#C+I}|Tqo&&bh3V-Q}j!ns$XfAPSfc+LucwN<;+07311Y)jo&EyN^%%_J1$x*fo*G$qbOr`p#b5DsA&kkU2JzS#2 zx>QTFRLgXku1J1)rap7);VNCN+_#e@Yjmxy)9-Y>ZqSXoNy+;Ao)c%e;5H@er~SQ_ z>kqm^cjCmz`jI?@U$nJXd8LR?xStwA-j*Zk%#O)+D0C-`)C_^$nK+U$5sQpVNW* zyuP4=l(`Q+pFIPI>Wex|iGnPDSzpoDbhwVtH*};j-tv0JTYO93*3n80#BZbqLTVtS z2105eqy|E2Afy`R6F$~YbgX`=3m(F*}713G*|O9Ukh}RGP?2|igd9q(PCYyC0eRwx=dH-N?oNJSXZ;t zHtJ?&%w!2;CT>&4Oxlc@$Owsykhoj-V)v+K@JFSZp?yFr^q?Nn|LS2qqCe|VoE_B+ z@1>f-U-h{DrYH1w{X_q!e`*q@r!-CZ2TLkxhGy!?T3Js~>KI;ms#ew0^mMJR)G@rK zhStBU-G|E8Dd|0#70pZ0gX3@?p4z2FsE zSFhB1I6M6++x4}9UZV~5T5Y7)>GgVp-l#WeW4&3ML~lv|hc?w_+FWnZTlF@*UGLBq zdZ)J3yR?;qL1>c zyQlZ|xBpWgbNS=8pYX3w+V11>r?jv3bNSQSU!TgPI1|D%)j3!S20>QwzovvitH*BLrfXX$L6qjPnh&ev>Rs5zRedC9@PZ&9^`iE>9|+$>Q#J;WxS>IrL~lYL{@8uGVjLjjq*o z`kk)V4Z2Y`>6YY|hc8Lrs@rtCey=>Mj=%n(JT;B>PJUhYs9kV3UYfqg-ndr}#c!{~ zH5?CLQi*Fg@WsdzqDJY}y%K%tp>^B|Z|CD9GtN%-Ke{#JFUhXHeL3o|^YPI<{crEX z12Zm6c076`M;*5Gf10BX;pM!ycHH{~>$~J?Z5VRzmu-CqWHyNZi+jJ2r*TAqj40EH zk$Fq}kD0gX9cq-wG)iQ)bje+6l*lwnWVUvRQ6keQk!h63G)iO|B{Gc?nMR3BqeP}r zBGV|5X_Uw`N@N-(GK~_MMu|+LMCJqjFUE>YV@0O1BC}Vl!>BfTblfLkH&bV9 z>D`JtBdnwu%JUjoa+02`mGu;@qNi$AJx#0W=~`XS&>C7(Yw4MKmY%KW=y`g+{#7r~ z3zgo%x1x7o?PSOOe{k2D-#U$pD{p%*k1KESDy^?qYXiMTuhmA{Jo&Bb3vvA|?_%vD zYZqC&$l68LF0yvdo|AX2qY^Kel&ro|+^gf^3E4)o_*E_U@-l;A1Ey*u8)y?7 zaFCKO&?a9%@&zPcK=K77uH(!4ioT}9b%eg5BXyL%sc-4qI$A%#OQWLdofQ=o#(tEQ zMizi%0Z0~rWC1uqKi5e(K5epop;PorovL4HmQK^@IzwmbES-&6X>)Y0&eQq2K(lqB z=4h_wX}%WdA}!P+U93yASeI%eTx>T&%|Pbg0$;x&Kh|MX8y z!t|7;DbILdNhQtDOg&jE>nU1APt~f*Q;T@-=~`XS(6jlTS#cjqyj-u)x_YJ7!-i3v z@S6JCKqK;Jr8m@TwUJ(@*Xs>>qu!*A^=56N|Int|Oq=U1daK^1x9c6+Lhsa;dY87+ zyS266qiwXUw$ppHy>`%!dY^XE&e}!0YB#-KyK4`9PL8=`ZQaSTFq*+pp?t zI$U4Z5&DLX)PG@iR3!Mej@EbdU42jA*AH}zeyAVmC)hB3tbVHF^fMi=6Lg|}u9NgX zI$6KaDf*>O)rd|F(<3@HOrNIHb%xH=h)xaDXKO^KhUs&4p3c{7U8oV48m8we&)n;M zBNa)wNVy+(Z|}#A+>aZ%A2)J8ZsdO4$o;sH`*9=p#&SdwJJ+GWt^tf^mIJ~ zyT_SSSX0kTHSFDsrvudQ{aD6dQdjq;zDafKJ%F>SI`$sSSyf$nzf66z>!J44H(?## z^;X8;^-{e|FHc?Edjxe)Sl1<2;`+GrC9l8Ac6~qLYHh%h;*4vwp-Zk!Wj=UOJVp9} zdhxup?jL5*m%7f(pfBA&l)D2p?frgS&)j=?T+fWxY9o!PxIFWEy+I=?F3-G4BPuSB z>zT1>ErqY&W?Hu`{+~JSNrKR z+Fzg50UEJxcGO$Fv!mX^A@*o^yrzcM)LMF`o~38&IeMO+uRM2|_gP337)=su`9ZkJ;wCfX5Z?&lw z^%l|}vA$ld^hcJ^zmT4V^ekjMw5gn8ecv}esxFqWj*)eYtYc&yBkLGh$N03ej%l-Y zad@gq*Qa@E`NLfY^1SlvyADl_N%ijfYSdv}M@Ain#B3}~rCP3wYK(W4YMCz6<@&XL zqbqc!uF}=|t*+6vx=z2-^}0bf>L%T+TXd^#)9w1bmg^6?LwD*fCARU+_vqeKs#~?F z$yoBE?$-lap$GMsFsiXTk3}_xnR>ET)>E{Ko~l*#G_9u9lOMXBOEnhqMCTvz_I$Nj#F|OmXpQc=Q_zxn5Q|bj z({#Gd(3v_*XZx9Rbgs_R`MN-}b)n{HuI6dJ7U&{n@8;VU>0(`?#ky44_jwKbK9=b+ zUGBG9VSA;rzw;XQbk_EF$?siDI72UFzwXu`YAj@rM)qiAk4E-rWRFJnXxyp0bhjS$ zGubQYX~*=}RH|)3oJYr!-}FT4FI}3(d2}rKL;t6LY7(ZWQUkh5Ks(PB9uGRGnJ)3V^Cu%IbT(8i&dZpHj-x4(zuc@yMlxWYA zhI*|w((Ckky+Lo(o3ydstWESE+EklqbG=1x)!X!Ty+d2*o!V0G(pGx6w$^*JjkeWx zdat(E4%$)g(@xr1yJ%PKruS=ieIS+U+#tP&?FY4|KBT?$VSPj&Wqr1f<3Oyvf9hlU zxIU?UT<1?|U+w2|q7d)uug~fLeNG4J^EyZe>ku8PFJj$vA`iZ#FVp9zM5V=&SM@a= zuCMC|eM3j;zpz16TKKk()_3$>eNW%l4|I%vs2}Mk{<^XHsgBdnbi7W`iTb%t(*NjW z{X(bcmpWCyQsY%rTJ+@UIzwmbES;^yD_%2K=jnXS)`d!>;x)ONm+bF4ok}ZQq(#Yp zyUyj}T*x<=RPI{i-9>jvGZJQIpf z*kUi;s@rtCey`>FgYM9s#)sW(qdHM(@q72$-WL&)GuFcWdO$1mpdN}X8ZX@K@+ zwlBwHam)$py8Oyis&&(-y?D)4w(I-JS8IdFW~27PhAz1lD@X0sGCgXqwgpjpb>o?_ zd_s?SdRwc;RA7JaRx2v7Hr1j6L*9jrl=srUUT;uhCQEM8#@a;x5&tiB7`Ya87`af? zVO<}II;>lrsKaRAVY`LisV(&`ZKZc>YrRL?Xj^Tk_iB6XpdHl+8Fd){V`nu&MjghI zZfb;#I*cU`Xb*i*dulIi5Oo-@c}ySIC$MhRVc18X(!SbHpZ1qNW4pgTs{=IRVcn?4 z`1}`?7)X@v$}yxBtt+T$wzQtgwKaQN|H-vA;Z<5+uhs^7jb5vbw0m;=E{-ADenOwr zKKhjQ)qeW4_SXUWTC%@cJSF->(`@eA^lP*BTmc?_qJ^o{9izG0CcYI{w<1@!B3HNK za{XGr(G|K302I%k>A{p*wY#?$$lZS*~{t zht`AFjG&~%6f`c(Nndmo~G5bdb0kW3@VtghStP337*4Dr2CHj9_NB^#u>ScPlUZHjMO0B0?X??v~ z8|XF4^T_!=*J>lZPOne)-F|7>4YqI8o3yds9Q{14NwTlyIaFiOZkilw(T$oc+P5TI z?mUZXEWAx`*E_U@-l;8>$kF`VsK$`UfvuB8_f(-83)^U0ZKwBYd+ned^*-&SowcXm zmPl|jN4b(i%`f8ms_;qeqfcpH?Wa%cGumID)dBjP4%FxM1s$Y=b%+kt7j>Avq%Z3$ z`kD^c5&DLX)KU7TzNK&LXnn`u^{&3B@9PKpZzUVzw||rzXwj4FtHMuotbVHF^fR5H zpX((5m&y8tPSG!Qs(z(eI!&kR44tX7bhe*4N9XE1ov#ZtTNi4M=4zhiYk@A(LM_t8 zxdL7cKu#=>Mq@_&?m?gj)3E zI;ya#HdC%oX2~te^~tnv)7$k9ZJ~E+OT9~5>D}5|@6k5eR@>>l+FmJbyV>^AJm@ukoMAt^$~s4b>7=Gz|*E!KaaWmaoc2GY>_8z8+*Bq zifGJrRAE1tKdsNiUdDA)@d?lB0DVpe>hn5C2P<>pynd*@n4GxhEUu#pU(#3fReeo| z>+3o~-_Vgd%5VFoa$mA$jk%601<8MJ&Zahvc2Tmjbsn{8xI~L}sg`J|mgzEGo=P>Y zh}smt(G^K)>&4Wj;VNCN-|8A&tLyYTU9TH-qi)ijZoyr;+rF{K{o z$_o4FQ`%R#H#y6nQSMDnn|qTZ_a;Z~O^)0lty$x^LmCm_(YOO~J?@aEjJLEIZ;|m9 zPg2HP+Kjizc#Dj;$ass4x5#*l)s#_}Hlr>w>LQ~qGU_6uF4FIiQ5VlvMqS#Bw0OQU z-qL2gMaElXyhX-a;y&~%sS+B@?Meb;UtXE`>;%li?{fX4C(dKGQEK{z=q`h3f)^Bu$uGCe! zTEEpbx>ncece-9T=tkY7n{|tB)or?6zgMoM=bQYXJ9MY+Qtn00U-#%f{ZaSp0j!<_C>ZYvVD>5 zi{w*CK855{NIr$+Q%Fq0OO=>J`*J0s(5|b*B--_qm_)n2UabxE8f~c8Y9qZ)Z_pd{ zCT*-YN8h8?47t+_Hjh3@t$FZQl=+w^w5LtE&b+EVXIr5g5(S`+Wq*2$_SJE%3o zHriI(>Al)sJ7`C}PdjO6?dkXI#kXpiMjpg>d))RD`lK@7#9yD%zS>Wp)@QW8KC1)t zIUT6XI`Ik2I^iH4tV49DzNo|WC4E_6QT8C-J6uQT8#+=)>6`kNzOAG69er2d)A#iQ z{kMLoAL+;XiH_Azb)0^t6ZCVPq?7dvouXgrRQ*b`bec}r89GyE>1>^&b9J81*9Drb z3pGb`HBa-kKo@DD7U^PLqQ$yYOSDwWbeXQumAXnff9b|}a!g3Hx$0x8%#b*P#2F;c zAaMqXGf134q6!jKkf?%t{mgy(qwd!OTA>H^kp5Q>>k<7~kK($hFX<=8^jAHuzv&76 zUH{Pk>7SZ}=_yT9{=t$;nxUC`vR2kpw2GdpRrNHjMjn*GnVj)C)%6Ump*6LZo~dUk z_2tzKqQ2x^f6;UGJpHR)pcg9fkXK%;we@d$i4qU_EAbHjuGE)&KJ_J1Un2D-QeR>{ zUK7u1j5hTpQePtVB~o7^^(9hYBK0LwUn2D-QePtVB~o7^^(8j(fB%Oz)n?jUZ_!)z zHoaZ%&=z{9w$!_{mENtb^&V}bZMB`=tL?RecGUZ{lXliF+Eu&h{n}l7=!4o*AJSg> zus))XDwQR_oyrm)a|t;b?I&zgS<>!f`zh_KRF*8KvP5c1q^3k_N~ESlYD%Q0L~2T; zrbKE=q^86d{j_2FlD?v^>T5b&U)K@(hK|%x`lkLX`RUqT)Rf`dI$Gb+clAAeUq8?> z`k`{yB;NZ8pT=_=^-~?EpXqp=pcD0TouvQK$@+y((JytXex+GDO{eP&ov9IT*TvbS z81Z&p9OJ}@x9id)-mXiJNV_gQBJH~Lh_vhC7$@c>L%U7XSR&9NpYKU zHYsh+CPmIBMb0Kg&L+j3{JNHLeH&7D(tq~Y-mCi}=2Lfu`}KfU=s`Uc+n2hN3^4AQ zUC(>;)oU3?I`Q>*4Rz?SIhl;mRccj_* zagRQhL=5~e?$LME%T%Qm&1zGX9%$B(sx;dFXS+`FbF)jSN5jiqa)s7S4mPVt4H{mT z9J=-~s!yciQt8ZLCd{yZB#y5w#{6S{#q8 z*D{Vr;;njz8q?xxJeC{NGL31O#7YS9B<ls=@Yicb$Q_s?~^&CA<&)2`|1$v=gq!(-LWcektQ-4dox%kr5 zCHj9_CppS*`MX}Km!(n{<#9KVSYB7JbIJ93gWjk&X=A-v8AW;TKeVYfQ~DRn>0fx8 z^87VEpXVzf&sRd8uY@g?=PS|X`AW$1m5}EvA67qZ{kpMI*kx+^_Z)m_~MK1M!HqBVmCtr_@tavb>&ayZ5N2P3!F-#?ShQ93q;!mqU{3zO->`xc0q%-3q;!mqU{3F zc7bTMK(tyQS}hQ*7Km00{4a@C3o=?Q5Um!7Rtx-){D}OR{DhoKeoD?GKO^UppOZVt zo#ZZZ54o4zPaYr-l84B{guI!&MStCO4c>Yb-b((J97^6srjV&*hCbYOfdw0k zRLUZ=8Ka2QAd7WJM(wR5GHVxvJqBTqLD*vu_85dc21`j;Wyr9~AgnS7s|?;rN*#fNBK|V=7MLtbFLrx^0rT^#1=SiuX5qV=0l)4#_HzvWC zNvV$!OX_1p-k1cXCPpl&i4pk*4Jb7+Vo6PmSW*)smej}h$Xc$Vo9xxSW+t^)&lYiav}L8`8BzO{D%BqsngcU3|?>z`2)F@{E=Kot|!aM z3bK-{W@=-$8%pCQax=Mw#B4X{w~^b)9pp}OH@SxtixIJYqP&mXPaYr-l84B{hHOi=Ba_MYWC!we5@U3Pk1;xUCW$dRWQ@_lvn35>j1C!Nbg(Ol zF*;<7(ZLHyjL{)qNOmVNMu!Gtbns#lV|2)uki8i4rDSjNGO`bOIoX%Ig1nNvioBY< zhU`aPOI}A_PxdGOL=GTtAP15+l7q-UlY_~d$RXs-`XV{6E?hcdQ?jCnJiCt_?Z#Ecmb^M~8ck@>?Q<`09IKMZ32 zFo^lXAm$H)m_H0+{xG;muj{%2V{74J@@u`d{RE7yh2M}%$#2PJOhOrDL=@RGK3F&bXkZZN(bv%JI4_;$?Vu2p!q@;_Y< z;N8mqbp2W8{dV0d^M20@%e-I6?J1wGcXyuTopwc`ly8pXbV)-t)p3Jd83#kA-H>rGWZDfG2ScXakZ~|% z+6@^8L#Ex3aWG`s4H*YRrrnTnFl5>d83#kA-H>rGWLggy2ScXykoO}`Tj$G^9FcJ_ z^t2K(4sO#!#=&jc$~YK=l>lKSKv)T|guI;`M&3aVC+{ThV+r{RW8a#lM=#6WEKqlc1fGi}7$YQdDEG37L!%0|igaa!M-b2EcLxwE}Vaq|- za_|B2K@v3>no%Uag50FD8cq38at!$x`8WwH4*#&?;NQt{Kg0eS$>481mEP z1o9bjqJA{F1!hnSpC@0S{zY;U`4Tyqe3_g=zCunV|3$t^zDB-I{+paezClhW-y~;{ zZ;@}4?~wl?XOZuc|0QRW?~`-L56BP6kI0Y7Psq9Cr{p~HGjcxpIk|wj_650+{E}Qm zenl=Ozb2QE-;hhmZ^>okcjR(%1-X*^o?Jz)CfATZkZZ{w$#vvLCT zIC^30Ihczs^pOEFNQTHTnM5`qk0TqBEu|J_%eER_L)>D~Q!vL9Esa zVzpKfD=IcUf|=fjPP!U*0)zv|8_0p=jpQKm&*Wh8CK6xb#ZmYYFPO$KGxXu+K?`jQ zQaFpu=2>XFFavyMaxcsP7Z#9(WD!|RmXM|7?c^}>4stkoCwUinH+c_vFFAs|kG!9J zfP9dAh#W~iOg=)6CdZJEk&lyqBgc~C$bXRI$$#pfPF!w1K|V=7MLtbFLrx^0)o&#C z#SC!a^W+QUi{vEoCGutR6>=)i@+$cn`8xS;avJ#tIh}lyoI$=t&LrO^-(i^lA!m{A zlJAlKC1;cGlXJ)q$PdYn$dAcS$hqXFC`!R9xsF~0%40l*dh?N2!t&HVT(Xm9uSrXgyjLNnesK{CUP^mh1^PRBe#<~ z$erYFau2yz&rWVFqkNR@eI%?9WLO~(RtSU@0%3(fSRoKr2!s^^VTGCpWt0!X3W2af zphLQ(j|`AOGDL>SB(ec{9CI zQ^{83X=H1%4cV4#M<$c)$qwY{_J{k_9QPMdr8T`OmAUt@-ngyc{$mayn?)vyo$VYeC9;~Wm;Fv@rJ#{9B*L>^`+!6@(yx1c_(=nc{h14IfA^8yq|nPPir?Gv%7^4 zkq?uj$VbT0WCnVdqtLQWU%-C5d%3aTM0k1Xq)Ie=vD4?iL8~{ve3=2SL0) z2;%)g5bqCycz+Nqr**1eURSdGRIxNxv)tB*wa1fq;bw9Rxs}{TZkPPUlX#59a7O|C z7Ti&Q9m5Q8)Ov>_5j>Aa2rkBtCoaUAdBO(zU#HH%vw2}d@_4ebp4MnPp3MtSAX}@( zClACM#zNd_Xx>onF@PQPn@zgAr|a)dwDI&^WLP=Kos??4K;9|_JL`8^mf?QG_a`jH zlX#KOp^PuwHqCO+RVO!_>~_`bPV(ceVkfz)i?7{6e*urZQ13W(7M|JfIe7?H)LV1n z63iQ4dO~l^8~>rj6uecelDp&iJz|aL_d<>|@%&!o3(4-}MPv{1VzMWBiS)s+f`|CO zO!{eRcnPiD* zg3nUEm6U6+)J^2yKs)#n`Ge4XjQT#HQWt`Dpc!bX+e8jd6o%5MUrPNolph6^Is>$n z#LiNegLXhdvjbzO=_hiK*BN}Cnqi_*2PpqrWcv}y4~lFh3)KwJQg?_PJXvT-I@p14 zs0mUYFZ6vt&3w>Oj>v%|p|6JW7vykZNK(}fOB^lLMP&afGFKQ}EDZT5A14e9pgf3d z09xu&>Q5kS^l7k*$Sc=8>z`fb1`>KEE(X*}aPz6Zfx0_O}&=Bu|y8>r6~26?Ps z%A2MBL_a6dbv0d|5Qcb0=XGjKPEq4F7L8j@KUa}Ug#Oco0fy$!p}d0nA1SvKhFAgv zOo70BYM55Rr|9Q#q3?E3sf$DoFb4w?zNO9)P4J&W|0{Hrnq{e5$eTf>NaXVn*C0AvK$5FEww3L)&r87||l-NBx zoJuYgCNXyYT_U@ekp)7BrO08)bng{SI7{fS7ACz*`Bcz~rtr6-v7e$JF9-v7(hu_@_^8OC z-PEug2If(g8f2+UNU6D&Vod!ni)%QIn(-osBn@39XSB`r1jW@3@LcX3k=@yJHQ_UD zgJsZI>Q&K)ipbNb$tC|vW|I$*a-Eiv9I=Csk<&rE*e7ymzA!LE82&_PKMyL^LuB8d zgu&;8e&$_>r6qI|^*55H{LqpFSBWNYi!fx$2IC%-5^D#>(*M(-rOu+9M45RP`WrPY zfuXxa4vZwfq<*2$|0d;YgpNs{|BCENdRpp5&{00ogoaWxigJ$7uc*0)Orc(4XDP`q zJMakQ9-v}tjz>O_ZQmW_RiLFLWo%yqVQ3LGauz#D>b@N`vHnl}KLdq6xhlM_LYXlN+#qsDuFCe)ngvEfwiMGm@DKXjAq-ytI?5F}R18{b z40)Gm?01B|j%0gk3PDTBHCswbf}_MjT1ryMQ9;nQX#sq+9_~iEifpMVq{+u0#iuKw z*}f3DK$!G*l2;gdlCr5ge-=61P3U841XzXx`J(Z65C++P1y~Xsro6jN^r5Bna~Am@ zVSp(d&J;QLiqJQV`c{-Tk$r?A*54pYY~W<-w+Q{~NNL&eqM^tk#v+MUD9AI0CyL(J zQ0OlaDk()iS~g|Mu*))O?G@Lg2Zav(hp(p0e6*NiE<6 z(ASvypM=)E!r)s%=P8n|4s$Sx`I7Vn^Msn861qz0<246|P=6!YhU_N{ zGL@WCYJLDM#q{x=C>sAo^s|Jn4Ta&aso~Xz#)#}@()9}Je-eg{7y22C&`j!Akjv=G zIEF@2!%`Ibi1MH4$}9B0O3gpWYsn|++KBQ%p+Ac#{O^ezdX}zj=}JGL%_1kAPEDaO%=!}iJLR*5K|je< z@}C0P!l#=>|F|&BdJ+Ds$RU>b@I2~25c*kK0t-bBUI$vns)v}5VWw(G^tQwF@!chI zXs0mHPUvq<4X-M6lE}WZ zPt8@J9pH6_`0Kap~VxVjR8 zWyVdxRMGfNI`oJ8akFg7|yago( zEhV+UQpqICfO-hB&EA)7ok;l!p~dhmMR_D>sg`t261rokX+oYrHYUv#wt#F0M^GcR z$>Qk4inb%CiEDsX!q-D&_Z*>O&iW)zEsm$`;02=hKTKC%PqgPbQ#1jIyB+v|JR%H^ zBAIJ;vd9i|!Dft@KDJsQdaD~~@!o~)JV?16c{Z6!N-eXM=xz5PHRH%r$u?vw&<;N; z46KNmT~xPT>VK=E4`~I>SlcKnCFWddV5%P+0{$4D!hHVSLM~7TOZB~Q@1{4 zpWfnKlXt!GN5y3QbxMA0FETwQz9zXjFa9| zr0_qXeqZ%#HPfRwXk3efdRIQa43t0D=#O#CPem{2A-6Mcby=jXzBV;td`4CxO?v4~ zC^x%qxAok@MS4TgjQCLW&PZ5KC}CcUUY8a3)yvbaOz^WXx3jmex8=4l{_^MO9}3cp z-n(AUSN!(&p#CE%rUT@3CI?<_xSk;=>EI#zEV!UBhyj<557wWAAy}YC7_I*w_{eAvi zPp8)u_tX1x?xNe_Jf?~Mq8Mpm%9Y+(x{7YwO7VT${Cw}YV{cJUYw~YNbT5o6i@DMA zo9H997hj&eIK`(^%;zslf8>~74zYgM`0K6Thf=?_E}G+|E{G)MK$l6m%4Z%>w-UO_ zxUyDJbVT&>)ARGY=}3yn%MbE5>-$n4)D`(X_1eO){`Pjftyo;8_Y_p=k1{aFFIEQ3 z(Kvo|HtVGRys$UJn{m6@FE4DVtMgmc`ujA0L9P5jesZmhvgFC$7G<%!$hr#GJ-aAN ze_b?4&o9EtHANM=rf9BSUA%_w6HA8ZN%`yai}?xmnswq(G3E?PorspngT{X9{l!zg z^O&u9L0)RY-rS<(n?>f^>Y>gr+G z)omrY4CC|2A-ykWRNavD%lRqsI{kg>hWLFiU*dHrcd_zPd%f{KVzOTDwY@Q&C+T(R zgLGBOc=`8qvemnDP1)Ro^0cSm0PH661^QjZwAcEl;#xZcH~KZlrA*M3#a$_v<+j$l zQ*FIBy}z`t`lpl^bal!Uv89k-7e94>dV6HAr_;NOX6XZ^C{O064-|_X9_{UWTaEK5 zl|4ZAajt~6$@4?E1E?RXQ)cLGrTg@@BG%)0_hZXwLl5RR(1(g`uN}}{9oZ}OFIL_Y z!$A4D)6`jgB)`ADFJ+cFKC1z?u{2weH(wvhhc5A_->=h;+3WRlq>gyDyUsC3;`(QY zQadEN*4@i$pm!A>s;f2oiG456PkbLjWPK=VD_}RpkMy-*OHgN+W=Hi~jGJd?yuOyV zpI8rS&$%Wmp{z)o>bb?XXxn*Sod2g-A2jBEYzcK7c_=?=M}B8pa#-32&t9QiAIKZ0 zD+@6XHvg!&n{!C|q|DJdDRX6CmzOTnLsAwPH%W&Sd>N9Py5$|2_GG^z#L~J)N#Ve{xMK>{aZizsZIDG(XsJ+HYw`&>xy(wBGf4z7n^3 zWj=F5w0ch=+ceLIUY;7%>rxxyKbqghMQ<(UHDUX+-fsFLJpN$ez*xQULP=#CWA

zC#JR*S;LO$2U9y^oG~12Kr~KKo3TB$tN5w=)8FTG5}n>&GF#(Z`T$m3j{fv!b4}j9 zx5as)c0i6hdhF}@!&tJH{wcMuu1xJ0)BW#}>T4H%?M$@6wPRw>p4AbdUSc#-=@-@>ax^ZaqfobpRpe4{TVa$=E7ne6ITY!S+P2EZ99r_ zU!6X2?8C%i_b$;qh(W8HqaNYWWtT{%HV;;`_P(PgdUhV$g z;xv6nT5q$De%q+LvvpRgu}in-cS2p9CvJLe>O$iuM*U0yZI=E^>Jpuox?GPcYM`Gk znxfw-9IihroQ*Z_r^S39OOmph(4H_)y!QQAN1;w`h`D)X?dUQP^Aj%}-LH&yt*=}p zy2oE{qDySgtBWzsFG;#OwMyJ!Tes>hsj#E5bHKJA(A!fvHld%rX|5NRo}1B6-D_xi=Ov%O>Dx4O89c3zK8JD*{cruE=D<_{=;MBsHimB7 z;Z{}D2V>(bV;gX9YggWIkuiQg5IZI&>9x`E^_DcOhmiJ^K2R`PTw#}Y;{GS@CuMYI z*n5g&^=Er-EME>4)XK3?jii4<8@(y-WBo%h)S-O9m>`seP{X%j05p?B7{9AZ6*A#rQ7tJ ztMyx^E!DHq%DgbVt$v|^eG*-W`xmdFFZ?j>sg1{&`HjM2J-hH7{e!dVDR7jm45U(Dv|{TN5=%f^Z` z<~~L&>_kc5Z_*ikvZbhhhm3119NE`D7A~r9e68oj-3vIBj%&zE$Y(s4C`~+D-Mz%~ z)OGzM`+9S_pY?iw@mRejy@7;*`&s?;l=LQb!$aSERISdB{>ZG{iQc|0PH(QamQFzb zv9|n+oBpY2v0k0t%JWCv*z{!d@#j)rmflIfmi?f}^3!Y5yXcAO-Sn)?MMfh$I^KGJ zV%o+0B8**;S7jez^-R#0$NJSunS0EM?qUzoj>|pf%Di#n1{=9Re^|OM-XHZ9#mi$l zj62@Ux!T*;!_xccsseoZU+O^aJo-PFj@4@lC+ijIwf?AotrY1awiV~vihBL2+ygQ{ z{Uh!Il)pWf(|V^33e8=vMP zBaR1)yXzId;hDhe>9^3^ET$)hc_44#Ay&@Jr+>>pH zvbbB0MW4~w?0E%CBy8$-#61((B>G?eaSUBu_nt>lTsc3M9f@7PiHr1|(I)(uaGwcj z#B}1dnJXz7sJ}=@gO7G|E1o%)6~ZP*uO0W1^$w(kl#2@74?k4b?qIz9Lo8qAT^+I@-3yA1!q`asTK&{kxwDc=4C5 z{F`&YeoZr>OWxN0!`=M!{`6IPUg34(rZ?dp%f<-mR&;E-x1g)ujyi&IuC77*d@$C2 znxk{a>a}t9VjZ3@?T)w0D5o3czLZz4%so2233g>q`cXFByQbI{*HmWg1;)T>G5v3m zB>vucVJr5hSKwY1-bg$4^B8?7H>k7IH%sVfKhpF~>9pY{EzvG*&W-4wvTeO6eV^V` z_qkBXeEmuW+ZrBUov~c6D`+jU_k(?{Z3FavbKg~0m4uN-w(;X#uXjY^R{vo7%|@Gn z{EYqdnv90JJOk?BB z8peFrm0&x!6yW&Z_k*49EJx^_#rNo%*!7uE;?qg4DWkg=x;~Q83*(U5IO_W|`s%G1 zyB?PEEYC>H5Am4q$?;pgA^MDJY0PGdJ8brNU6wH*-Y@z;Gh+K$$@I57(j%rzl<<73 zsn{&Nw76HI%P;TAJPGl^IRdy}OZiao0eLoD_efn`*gIaQr)3QG_IdASb4D8OEyItQ zi^8$(4%`D>l@am$$yU$GD8v|&{RF);f1my;c8>+=FidaBxJS<|s6D?vBID=8ALfX3 zNz_PQ72G2_)XxpvZYWCVkLlW+6W60{bNsz-j6bHyt#=rh^#|#{2bTy{@#6C(Bl^tJ^OUEy}s~tY}}xXwm%EANp<4>nwaO_Kf7$kCu5e z;)`=enJ++dHRHq`S8 z-MWGY>9$DjnHKcIbE?<%68u*eVl_(ioi=#)LgMRO-0|O=KCS*P{|MT-EPXJsZ-8f2 zJ3ZfebF7Z)FEe8ISmqYA;5C$G;G1(9^Yp6xDf)|JjibHL%vR6I-^XKjWi0euV24qE z%=PNo1&DLWQ}pwxnX^H8RrWD$Bkb04{ZW3Y{tA7r1x0sB+2B3#ZN>&DRnQRsuqkza zraT+1v=h<(#g7?lqB_RY$fp0`g`jt2Y!Dgk>pXoZ1NA%J)=D_0UvSjD-|w;?)CV%Q zdg0W!)w?ow*VW@0R1@64n_gFc?0p#{7OsSCKw4%Ti1{mJ*^2EO31$6CPlq`N9~v3$ zZEh@6BDP_a@$F(+_0gD{a})a+Mj@ zZ)G;}j?+IC9@6_Wo9Trz9`Sx91C12lrh9HjZ}mRR!#SLPi{6&i1o<=5^P$(pkJ)2$ z9BSPwGud`4mDyHTWp>n?GS5~jr&KS_JYUMDuFmYCLYV_qqoSRVd)FSfKl4g;M&@#=<>`mPkXGb-kw=w+%mT#E%zq+%RGcSPg{_vmpEnN8E2N?=)F9T z_l2?A&Yb(>HAc>AqK{-XKU#k*O;#)8Qr{T81ZiseD&EohU{)twb2NOZUs+vP9(H7P zGd4zSnB0e#_BhTSgtK}^``fcro2)*nOV-uwS0rWir{7jtgOm^VEm~yVqE5(4@y;h( zU73}onrG!xUZ0g%XvBSc7VKR;zjj&otJYcU57py(luED8g3Xej*h+>wUbeWV394h( z3#xI}6z)eM^G<1JWzHp@e36dh&@I0G#mzs7vHs`RQHOj+ihVe!=)0gs^?4~A+;?a7QnLDJX6x(?2 zv0SgnZXx|j{cSdm#ku32gK|Hb4wU~}vQqyOe7oc^F>xcI^~_VawB-y5w<(C^D0pm!CRYuu8L`a<1c z+`q=Uec89_9ocE<4@JCVO?85l^rWZ)r^qxT?oCCNYy4yVCVz}$FhjF_Z{j!?u zdnoTuY_k%=--`Zoh1^dzZ93)!A&p8gUq7)ui$CW7A34tE{J{&*o6Iva{g3RixKG_v zPnGBL=7;?XPmXQPy-w4nq71&3{e=E7d!qg*dy-z2Jymbco)+_)D53l;@#fBz(;5!I$HZ;*keEXj062Iro+ZG*ce8%fo#^jSefOjR5(69By<{ycx{%2&MYV198jttS$BSTHQEv|5%8Oc!o z_1!<^8QhP5hph|M9w`WqJS0(<1%Wpqx#v-QQ=PAA0*{ z8$Zt%W39M%toVM|u3WaoiO2lGZ$7ca46{ z=h5ymfBR_dZ_{9Y@$%p1GtrmKWz{<{$K+7%)37_S0j$@p4Re zEUE7OLcD*s$n&dziYyfw_c~@Fo-HKrj9avh?e~1j)_bQ8{&&ECSqW?&zha|upw5FW{$Kt5wt5|Z7QGwK2DY1bD`R@x(_E6{_x5og`l$M+m*zAv?(yd| z{a?Hb)dAzK?s{=fZGYGcOSXDdPAmOgPIB~kmO0+nz&u;Hb;0%AAKSLgK9>rfuQjMWkSeQCZ4yDq)5Cutgc_m1xkZoeJ% zL4Mvjc=$PxfeB8&)dEC<kApXHzrf$NW( z`xfsXb0X@r?eVU^UDkKzyztw78=v~FoGE%x&g-(LAIO>E9j_wL- znd`~kR^O9Tn~wU4;z_mocXAf%0XfTR_n*sIrQgU|r@zRlK%Lkua~k!dIr}_!+3Kfr zj_4k%LFv1NHz%X^>eSiWGte^WoM_WiFtxtOPovFrBSre2t6mpLAh z{rcXq^4=k<_0RXVa$$Gi|4Z|Ju>K-{2HsQX3cuYwf3WY3Fi+%4X%FakLvAn6O@Ef# zSLPkEt$06o0NsDg9jsU9-l|vTrs=tIf59B*9aG=d8-wXB-a8VQ7xYaoj+H(#+M#!3 zj1_%8FempOFAV+Fv7eXf^4tgYY&;wP68C^VlR4~ot}pkdtIe}I^v|awU!L&7ir>m} zCVenER}0VU^j^$2t;F-*-O@*tK9ctzJ4#n$9_ORED4)3Ju>x}e{*yb^_(NQ(a;IVL z^>UVl`K9YHju{&}(n$J^+?jQCv3>N(*5$qz+m9ZzBKJegxnjQ5b^GOgGk?tUqnG2} z{OVlXYsy{j>Hf%8@BeMjfa->gX9HOECl~XT68l<+^KQ>C^0T&ld%D;*?!I*0zSd)T zZp>dy(%*Y+c&r~GeJ#BzuO-I1t@N6_wtBtHZ$9=<@5t++oypas& zlDsk8UJ)J7F2q>*je=?F$~$_#XKY0Q21MkJ^}0u+}70p%umnFdx!2nEC zy{X3B_%-#<{Xm*U#~@~Ypm)^o-+JSf-+v^oZz=5bVEtfTtbQ!a!??x#@T@eh9DCZM zw|b&?4+&}gYTkDJoq4}We4@S_@{Yj0eNW^T_t5<@g?U~b+c%P#5AO9p5ngoux=t9E z)jbOSMiln;wECg^CPq$7E6kf(gz`4l^XF~#+{8J!N5B6cUmm@q;q^rL0{r$*bBCC zm^adm^GBBF_wmAztvvtIcY}S<>-~OhJ~-wgXNh0=_6pkEQt!C>wt91{pM(2BSdS+*2LW~Dex&Ot z%o$kfjnNoxbRJFJ@bKR41iCLV_dq;%+&6ncFVCOi?VD}M^w@s>>mp;Ga&210+>C^0 z2easBabd0P;QsdbysUfF|9AWHeU)i#&t6%@swiDj>lAbSkLXOfO6ilppG{zqT@&12)U47ks%oV^``o@#mAL!=}Eto2P_-<87!OVF7cozO1-4=WE;o@C!%LhoYvvk5zg4e{dlvWp(eF;`HRf5rIlj=< zuN8))awDBr*bIHlmU@$ncM3a-9`9J4jr=)Ze^c1Q_%qbcm8`|{UDF3y5gV_1elY*G zH{K_W*@V5Zd%K>W`nFOQM33tj!tED@Lyf<}4DN5N|63Bq4~~m_1|9#+4Ea8n=jT{k zc?Z^W5b9Iq^ zN-S?Ak4!!pznD|JsmL$yNQ++HeU$Gjm@qImcWY5|e4DWq@+Fybvey)KstsSlmN{jZ zOI54;7T*{Z=4P|Ov(JgvKQHp-q_!V2JzbPuk_d@T| zUliS6dwiYpp_k$NovVu;$MucpwJ(i(kFL%)E-|lZmVC#~i@z79Y>^i1J5$EH;+~s) zXTr#d^EEj)eHG?mM1Kq$bL(VIS*>m%(xRehfwwPPbI&$j6O+xg!Oz5^ReDj;I{n6P zsLyfbLvJh@p*Lgh?zW=ZYlwyZ+a>S&CBOZMM4z$m=_KmSUabFIpR1?CI}HszS+@E< zd3VPAWd4>X*T0RwSA}Dj7q`Z@f;!ai_kW+(dxt6h40!jatG6%jHhJ>z*{e*{9P_6+fY$EuN^Ki9K_R zKd!Fqee1HW-rL9fkyC5GC$SOlWF>ryrg*0JK2z-d74Jw~Bj(@6=AB}H4aN!UisyMg za16fT;XT*!wD5=b(ed4@<=(aFwIw5w?i=KI&zCMQ-YPQUz1!2(-Rku?ZhgsKy}tBy z_&ZS7{kQJR_bvZdez@QF^RbSPpLcOAKlSPLGqG<6*XN$7OB{#E_clFuncrvR(kc3f zl4iAfjPX6$+v-gvEj`(6OVG!R{q%+s_BUf1kz(&NdFPV(*w817E57sDL%&qgTV&|i z7NK8yCEBp*MkmiQ>ahhB3V(v#v-nsP1l2^UB>a8X3=$A`o z8{g5ic%dY&e|Gfq9CK~8X|XaPz2~8SzqO=H!qdx3YH&^4y(^J=;mP%F_54!ygYd28 zKKiSYL!KY~L}`|hOFfmsd-8tS(R)f8M7K?hBj2lNu9ud!GW$Z4@3DOx=bh`p(k`A} zuPE)NpOtp7be3F$==EczSJ%2%m-fe;o;5J`R85^=J4t$@0<{Ae-_A`{4PFZM}CwVw&GB{r-DeUfB4y7dA#{gEuzq zn)*ti)s&j|grV<1pK2wt(?l3uMe7>sc~$NR%8kg5;)kn~*!E^>cvWU)3V*S<+6%}< zLbHmNb1mgpg)ZabrcrZ*w1sM-Fw~ElE<$TEW$MH8MUJk`bO!Z2i?e}pwNTN&l3Z3? z84#;dh(75rWC?kPFx*+_XI_{Ub3!gP?Sx5B3eCzmNf{!CCs5B-RKiTv=t@rvGjt36 zoGT24g=Y1rkXaL?yJ)QQg@OBp&N?Ai)iG;q`4r2ovD0?ZM!ic?^ixH^Z0zA|B$wSem)S!U(gB&LZym98v-VWzOl zJPnK&eG>CJ_&1S*mxDfK@{TDGVr~Y;QEz;vichHnxSTuEC2hVaaIY<3M|ppe=ZP>OB<3T z_-S%jHqb%`kH_%u*B{Df-}i@^kv>O|nKOeI~MEtX*DJ z=u*)*-wNFyDc>OU9T56Y76zm?MOWK&tLXY?YR(}CizaAM&Z7K@(8rLiQ6j5J)FcV5 zr9zu&<=ifEkfq&zTIA3+VUka1$yscRxg6+AJ!^+OKx8a~2RfBPv!bGp>6X-iKF_8r z)6@4U{V*;`yaxB*qA@EsaotZl^j{v;m*lninPSmZT#pldV7@T?5AtSVh-VMcHO%nM z%93GTn^~VX=`8WdRhy%$;F-0zd?ljy4;MPkg-JbyHtUPU9PwQx8sAQ#JA-5@S&c+i z)nIgW%aHiAR0e2=9v22#9^6Mn4xCA{M*A5%YmR8#P4vm!31(96CJcQpREcrp2>)4Z#MYiLSzCQtUy|n3d-IQbKHh8=+a-lxx*tPc%M;U^6}a`J%CJrT;>q zS$Q_8hsa3-h2eg}U|({OFu;<=71NYr9395pkt?(m{fEZTKkW(E`?k0)ua|j*i5$LC7@SPmTo3cttalb*`42I)5KEx@w)k{-cK3CW14}`({;65} z+YXHreTb#Vtn13vHlu6s1qO(#|5ebe`W;=f*uPEm!DOMIY2tfHWUGrXiC62BbJ5#+}4ty_~;4Y!V^b8LbIV`b=u8PYwwe9e1(Fd0aLl00M4@Or>4x7|Y z5`CDZ#d$^K;5uPw1i79XvH6x+Z#T%C4X|vORTKkD#E)4K)vPBQT*G6l$VY`iUb9)f zG;jknW?gtY$a)m~j(%2>Ey>}+Kqr!QB=|mLv&yrro`meJ7R|C6ETN_+nGD)V%!{DJ z(Na%|#uYy{SCuuZ0a}VV68^jB%?j-9Vv)@%$)O*3RGBcuy6LNc99_@OSu6S^)=HZt z(5y`DW4ieoi);8eVW5?eD}~u+byu@?ymbQfmbyY*gH7mDa?{6k$D-?WhiJF`5plJC z68ejTW(808PRdUR17B0VRu~FUZb)7u^f^L*9vEG}-k&KNt|@H$UlKX2=z5FDp`g&L zFdJ+{S<*bZKC(~BLv;0dv!b7GuV~Ebp8?jOz$np}l_EnQQhrewkkV+WTgi^ppGTRs zEKF-=R&Wk3hd#RQtyz~mv;`X5&-CP~vX+v1=dILyy|^aP{)aZxCv!G@ipYV1!q8tp zOEFCXd#KqY3{MuCm6U_cM7Ar1fp4idavzbCSe8P5ksYSL{f)>r`vYdZ^`udv37E3P z9Eq-$ev#;dCxVvh3;N8ucmY#KOnEq0{Dhfz{`W+-qz*<`lMm9uhi(vk$m9!4X+X-G zr7jh{SvS_lc&VR7<4&P|i7@mSXx1pS?b9GD#a!UZ@+#T~V;cIHhKkp~)svNK4ga?9 z9ney}$)2L|&k!aR2(9BmrC5Hwh5nbRzl-uTlGp6C z7uk0WT}@0)%4E>bYU)20+QyY7Gcc6;CiFwg6JT2T{w*43H#wHBrC@ZmL91LezRQJX z&0l}E$hJwnsUo{;g#pPa+s`vP7l>>>NPSbG?=_*zQe@Wr4L(EHhe1n89k+wb!C*gX zt`d!(wIlc?UB4xN6b44qHI149)a{Wp%=i0qRbxBZ=k;q$@hYUyqt$|<6Er7qh^mkP}l z`ZtQ~Gxq8r{TwF@v;{3C`C_TRiY9oP(D#7QpG(aPWHmK!2we%yQbR=cF|D|gwNj_k zkJJK7{h1sDD$_~^kEh%XwEgrMWN8obY6A_ZUnLAq5Qbj_qbt<=zY$G9N?>#?`;bpG z!RLe_hpvl+{&bUlL; zjZ;(9H1(94uI8y1)qMQTQVY~>^{(1$`Ba$|uo^;YWL>C^SQlBBSjpC<_&dY8%(}+v zWL;|wvd*&xTWN5~uyU+^R-QH58eomF##;H-IO}Pv#Cpbh$-3K`VokM1TCZAPS&vv> zTi;o4SSzgc);m_YwbS~@+HL!+uk3(*oVDIQ-fm-6+3oDpt%LR%b{FesyQ_V-?YHl> zAF}(~58D&$Kid=SXYCyOdHX#(&z^06WIt+ug1^7ppW0v8p&(zP-k|(7DiF>-2Ok zw|{i5aQfSo&OqlzdxtaFxy|0?q&b=PVJF*(*xD(;pXC%eC64V3bMC=8MmP^R4V;IZ zkxnCLl=G<5#0|U0JI&n2?g>syx3zn|bDH}fcb0R$udDAOrxGn^>TFucqPh#ALSq&%tUEukJ69}eI7od;-E|n1Bc_!QFr36y}C<1 z1lN)HYo|ux&sLA%FQ`VN^w=mp&wvwAhB}}OJrBO1-h$?Blp0(8SA7mHP*n)6Mr{Lk zs9n(P#$P+NN9~3D6aE~v4}a&V{g#ck&p~NBk)`cKl(y#T1gnL03fK}QFNl)Y6THN_ zM4cw(54_B}4D4g|Q3306tDiai|&4DJ*dRVzAlcPbDT5y~-9{i{Ev)=#!iB}q*HkGC6xP3&eWY@cMGq{C=@`&7FFc)EQ$ zG-udn!S!tWY!$N4vAaO-YF`5OvWF_mPO-DVh@AsXuAK`x&(4FKZ|CFK0=ocmp=%MI$r7_ho$Yxz0&WbI2zwqz zmQG8^sOPGy)6VG#O(&-le4go?3HdDNEV!QIbb;K}>8iRo7dRJysQ)-uPv;WV(CO{; zQ7xUzoy%1p*n(@I>F4xQ7dY2C*FnDCxgM_lo&JyqI0GQVQmD(Eo19yr8R`s$&)b~a zAg4O1a7}kI)tOF~lcicaIZh4;3xbegLDVTusZ$EgFy{_+i8I`}3z`wmeai3L@7%8( z*p&xV0G8z;6^3;gsk%9jIFBgH8SRWlEBUDNsA>jVlcajO4cy~Z8?iX*3|O2ts+HT; zZL2PI+qvyjZ#UUZhTPt554nTeL3MXecTZP6+>UNX$Y;1`KPv!V7@m+%Uv6rtGIC0qzROf2Umsr|<@zr7U9K!&UteFySNYN)XZSKyQ(vYpQ-yt5zAV)e))1xCm*+z% z_2v6YR0H4bzB|FYd`KDJ-M)L&@xFU~_dbO34_L?+sxPc$BU;HItz=MT;LoA0yok2)EZ9n12W@2o zbqD?|TFeHtm>0ofj)F$4WII|(i&oO2m25{V*$!56GOlO}{@SZo@OOfmiobU1Uuc0( zKnwgPI0Jt!tm#b1Z^M>aYBv7bsrS`f$e-fRMN2&ovb5C}?CdIB=W4YM@mx=9yIpOE zrQQK+dpd0GUNB~DEn3^Aw6>?v+MWVy3u^*9+Zi_KEUT;Ph!*`q$YOKDw7H#Wb34=K zcBaks!RB70l4)~$!sZTw?H&xvdm=4wOIqG`w7f1XZzk+`7VNJ@`|G3q4buKvw7-69 zlr>5nM=R{7752maJ_*fJ)>G;d^dg>yEVlSK+TzpDo0x(;7h8N9ZSkqF#jnEkHEX&$ z)q2x<6P9#_^%mrru+fcZov%aR<15wMT55fZW5q^aMjL%DZS+aB(bv;PpF|sd5^eNl zw9x_D=m2f><+RZ*ZL~`p?ZQT%tlH2nH?dpVE!7}c=Qgl+?d)W*z1<$xtpn_J6WZ(E zwAX<+dmVti?yds%MfOGT*#ova0NXtPed`yHZ1sYwBR-@cmd=>yGUJb z7sGZRN88<;w)=Y8?&h@J&1t)nXuFeOyZ@#xvB$!K_ofAJNDJN&7W@s^?&v1`giJN>1Tmr*&ETayR__0XxRg_?0(qv8g;I{$=-z6 zZMHWf9k$q8R6~2Ky%m~mu=q`A@f*_CUj-{4Ry~{~CrMpMTYoWZ{R!%LrztG{)v)-@ zA&af=4qM*>vRL_R(3@)uxt-GvM(KtA0$9hSW#T7u)9GtdT{jeg(R z2tnF_`~_{&t!O8jIe$e9(G~s5G=v~6 zL|^nSvmr;E2wbIw_=_Vg#QAI+20O!@;b>Rxbnb-aF0>ODICneusDWrLMyOWKeP}a+ zaczc=ZN?>RGp<9Qbd(yzmcwStaTZ&SbJ=qInJvdTY&kAv%W)=Kjv;J0Zf46dge}L- zY&rU~5ZaDh8ebbbBDFYQn) zf0;}B+~0F#>;4^zJ5OsfMl_}GoqtP*;C>*Cd;MJdsVgR5KliHMS8up}Zoda7 zPr5-}*IM*v=AN0`ar4;OIk}xWotfMFi0sRslk2QHt;K0ATD~JZt;N7sPpy)zHFfyR zE+gK)?vzoDs!kboag!!ZPv~>XsHVk(GENzF;-phXd4J6oW2EM{`;q>W4xQX_a9eSE z;z*0ZH*FB<=1DhCx^Ki^ZvAZZpHkxg=5+Z}$|XO`-_TVU=#O3)ez?BVinlr_niSvD_RE_oYk7s)=*I{&G`WXQZy)be;3CUcRXZLfwv-UZ~&N)Z4vC zf3oRHw}+m)X`p+tei_4_po-FNkrnn~_^ zdREQPzBBYQl0N#$nkrcPY+S(+$I(?a-5gg}*7U^L)+1$3(mRkECxiQN=G91(i{LZG z?ExaiaGss+C3f(uuelm$`?jV(&h~Z9Al1}y z)U2AP99O+v^Bl^+mo<}-CtugRiac3TGaY#{4<+G3^$}81&UT1rn~l_4g4FA!me$O7 z-%#z5!qe4_NXfTUUd>9R;aH^e+iD-u`CYZTrpA3wEw9;*-21r3MeePt*^7SXP=vQe zrGOjIqZBz8{7^ltRP{3T806)+`=V+Zs+sz5%@XwT#I>55?cf^AFM6vx<=&!4*-}-L zts3$GG-IuU$me<1VaV@UN5Chn|4~Ub6Kw@~l8sHxRNDoop+q*WnQ1o=|MqZ%FwS?T zDydlrTZ?m{2ab@@L%x__alDk27$h?Wofv~;#$dd}pk|g_d-V`rdvjjAuX%wnNM;N=AO?N(!RmV?^{dm=ZIC}yDSCfRTZsWQxoU7tM|C@n z4y%Xt{;Cx96#Os`ss|$;;)-}wO^|q0r%61jMp=#Z^VJVpC&TA^R(HL-`he9-zg0aN zaXL`d2laG9^;p(W*V?F$R9D%p5q=}|>n^Kl={%`_tbW3I7Dr8V8zIa|D8Fl~r=o5S zsGcU}w|XW@>)Pr*?jX{=YM_*ysxgw{RU;(DtAv!UX?2; zUR5k5r)s#AoT@QWaw-O)##UF4M~&T8Jqk5;XZ7P!mQbP)gR04r6BSt|9+j&k9+m4P z9*_}_$~h8`ilq{d%4Lj41BplFVu=S_B_0hV9+j&k9+itF9*`v-VqI1FB#8%zcvL7w342eg@5s62|D2WF&h)2cil0#(; zB_0(EBpwyFNIWW*;i0Ok*e9{6m?7)Se6fySe9>< z_?0h2{7$XNMG0(KQH&CJYQ=EG_1uaPh^r8Btr#hBtr#P5Eng#XEl-oUZp^@)U|5Q> z;S#^H5fVShh+o+diQmRTsbOWs63dO#B$j2l5=*!ume3%UWy2*e%W@@_kb8?>;@A<+ zwuL;(ZKwB?--Y{7BDbg939hr;Gsz3}_VOucz2OJ;a8LQ`QWwiIB*taKCB_?is?K$D zcjG9D{l>>7_K*?#jXNdwWlbgC8)i$qH{K=jh92>T2JzlFit%nO@rI1t-MCM3cSDB6 ze*I{PJ&4$E$d%mPxLI(j=R!ADG@Av<3_4ZL&U3dQW0nu7(EmcDp z<2a6E9mmPf$8oH6S?gHGajbP5V~jD5wKjG9Wvt^^v<3}9L_|cR1d(@%F-QOr0g)gg z0wMw;@(v>MR%47oqev9dC-?KYzRp@%>-p!iPR@Pj7Orc5_uglpeQu0_I}AD_v8z@s zyPbFS+kgJ&t@M*!@2H=A_~-Q=<6J#|o}(2x=g$W`#@q2v?0Ayv>#j19Ab;k%(^ZA< z&gTRFj#tIwc2_+lI)fn59xM`bT}@qcoz<@XE?4ysvTYVSBvBxs=Xc zvFUAJ=@zMfIos{1>q%FrTZijXXTL_fjxNVNP>j9YV_kh+E8RcSd(t)L6?6^KnAhAj z)MJpVrz_Iqdtxx$L;JzI!aTmO-)4%~-07O7Ht)2})NkC;wM=uoFFWcrI$r7W_V{Q0 zREsq_R&*`U?Cxe;np(!~F3r?kA9q#{{Cn5)u5|c zL`O3uI%*-&xe5|(`)L{NYayXmMWTHZBs#Z1qVpX{bhJUDLp`1AVOuyPI_^TEZ3!gW zQX$b90EvzVkmz^}iH@O==v)GcwnRvDJcmT5J0v>yL!xsqBszm3(K!JUol_xka|$Fn z7eb=F0ur6WAkjGw5}mUlag!Rk-8oVuKJ2K3MCU3<=r=VI9g`u^u@(|ak+^vj5*=QU zXs?1qdl@7;mO|oYASBvWLPD>KMB6$@sO5`9$7)El*F&Pc1rj$yAkp3diJS8waq}o7 zI<`Wh{Wc`pyCKo;4vCKWkhmEOiS`~ywD&=xBM=hp-$J6p4-y@tA<;1r60H*;(J==S zZH18N@PI_eOh|N0gG6gIBszxBCOSMJ(XkW~`b{TO9qr>G(Y_KAN|9)dghcyLNVJte zqOAZD?F%5$>I;dROCX_FMdIcvNVKnkMEf#Gv{gW&tr`-o{*Y*EghcBsNVG;mqJ4vA zghB28@Mvp=M_ZTXg8!#I6e>6Spwd1IDy?BqX}bfJw);?N_k~JZFI3unpwd1ZD(z#S za$_`9+NVS1W)4)^`ZbT7-5vy$_DN8=aR4gq4piC)L8W~GRPX_sDrmf0tkACXy#%8|TF*`o4-+R6YIXAl9s_jgTWrzNi7SVdz z8tc}jGXd_yRF$6Y6ZQT@?!VbtGCXi=cJG8(KOREgg$iO98ZWMzoZobu$fGH#R`4Whu07c0udL7-+RDf|gRW zZg@jW$D-AuYkb}Vt($?+x>*3No4L@s{x-C3jDgmTQfS=_f!56=(Yktb5wu#Y%5Jto zt7Q?iS{A*a)w%&%t!tolBNtjX?nA3J9a^okpmlvbw3_EYtN9MJT796US4FEi0$Qz% z&re$8q18GUTCMA#)vEcX>rCrnXtnx4>-rdIUB3%09gCKZMeBMPv~)(al%l0-6|I)p z(7HYzTCLg8y8ZxK*T+ChDO%U>LQBV@b$tx9u0McQ>oRDyhC!<}6k5&GpmqHLv|6H} z)w&W|t;}F9w~mL__3^Z=)^uoH9|NuHV_wj@F&kPprb4SF6k08X(7F)-tsBFj)$|xz zO~awplm{)(Q2ZnetKg$v~FyK){T+Sx-kP&DyAy0HpcO@pA-Tnw%2>!EeyRcPHOT1^jL&}#97R*MI; zuCIdD^>ApZ`b6t`2ecX)*LoUmL93AwzF+kyT6$Hq8fQSOWg@g%=0NNEZD_R&f!6hI zXkG7tR*M(38jGRTbO2g97A+lLasK^!J+yR2w3MRNG7efzUC?SQgI3E5Xf;JZtFahb zO3`XM04*JhR%0==nj)Zey$@O~)1cKd8Cng^&}xc+R`V=qUH=wZEv%$9wakZBqgHPq z0IkMiXf+nUpw-+3t>#8(HBW|Cv+irU`c-|R)tm*bhFECT=Rm7rHMCTZqNP_wt6>PV zn){)3eK@q5bD`DT3a#b>Xf>BWtN9MJ8X}<8xCUA}7A+mCZ8Z*pmd=QlQnZ?Tq1BiT zt%g`=U7rN4#&ytYh=7(-v>MkyOUI(s5CN^mbGCy)e!N5R#P^#nv$W_L#wG4 zT6KQVYM247h7M>o9fMX=A#JP4A6oSrprv{gt@?G)s$chlR%0-<8n;8Mp%z+=x@)d8 zWzcF|4z0S`(5hVttvYvTsUAg3uZmV}I5OP8MXNCyTJos{Sau?XF;oR3$z-;MeC~SPPFQD&k6$2s+$3=x*0EM zHLQSE!y;(aCqk>f1zM^;(Q43Ir0YqoC$wr_g;ou#di|Nv&)eeDHZ6maFELu7ity({5>5OP8MXO;wwCW~7tJV`*4M(9> z+X}7PA<$BaR&67+bSzr6L!edL3ay4Y&}#65R>M|k)r^H!Z7a0un3vWy%!gLPTG7g{ z*$Ay#PiUzgMXPoQv}%XEpjAH!TJ>X~Rksydb!pI2^@&zpKeVd5pmptAXjLacOZ6yP zdR4TlmqM$4F0|@bLaW|^R{eNr)enMJ{ZMGtPk~l-E3|5op`~Nd(y?gOEQXfOh?Y{c z>SsZ#<~g*gyP#F?1Ff2LXjQjDODS44$g8Sc>T=vC%1@%Mf}FZ{bpJ~Km%IMm8f!^)9Rm*O{Kf(DP`AayUAf(3fO4=n z4XO+9;O7bU_@0s#TjyGs?$~^-#tv8iKC{Z3t(_%+utuP>-uC}tus@b~;Y z1AX{;UNF0MA7)B1tIdL0?L?Sey92YDsW7XZ46_=o6S|&Oje=R#c9_+?4YS%1nCU%Y zb~PDh)q`MGyA@_t^S)zNn*_6}*)UV}iP^PSnANnvtacO3uJyyL_5jRkV_{Ym0JGXa znAL8AS?x5K)wICuTBB888q8{E!mM@<%xdSutfn4jwToa@yA)=%D`0kYG0bXL!>o2K z%xc%etTqB>wMSuAI~ry+Q(;zn0A{tZFPK&3!AvP;HU2QG?uA(ut4;S*iDFhwZT3|& zr+8X<8)jGM!>oET%xadwOz#o1$}KRfs)bn%v->AkJ-=gC;{&s+Lts|30A^L|U{;+3 zvzl2jt15+A%}SWntb^IrMKG&b46~Y9Fw<5aF{@63S=BMCL0_2F41!tBP?*&WhgtPe znALd0tY!?%YR1E?aum#JCc&&`3e0Mz!>ncv%xX5ktXgaA5P(_DN|@EGbEb(?eYNV* zajq}}>$%42Lud6CRw*mn#3{deyEye;V^!|ywKdxF#rZFk!&#rKyrtgjQT4ZOKX&b| z=Dt&1b+8CMuJKSMMpW-t@6~s0joW(H{i-ajIJ8{R?rQxW;p(MMS2F^(RVTO)bKS2_ zq))1B(~3jy)wk)BD!ttcU0u~Z?#-^QD`^AVUC*og26*V_^coPP^Qr2kPFH6P&}v9^ zj{2&GYF$aYdMXELZKAh2UuzSOs*ANY(OT80wTad$t{I!J&eIBK$Cde7;cTxe&AXmj$`Lt>R&N z$X#6yxym-kUCDyn6%WW=?xW4A#zn63F61g7K(12j$9yv6%CjL?wjOd<=RxlBw~)KK z5OSB_fn4Qt$X#`Z+||L5yE+?kWh)?ebr|HXj)dHmiIBT81af7bkh>fWxhw4PXsFx( zxw6TSyE+PTSI0u`>IBGLVXd|0>Z_2uIu&x2Lm?*uM()bDkSo0pxe6c1UD*%0%a0*< zB^+`U{*bHC`l_85xyzFvclkNwD!m|gB^q*95+GMO8gf?>Ay?rGx$+9gl@>rwH7;^j zG9Y&)2Xa^PA$R#P*8FE)xp*Ve|402bhAXn)Dxyxf9 zccmV3S6U!fejjq>)sQP~gk0%r$dxyW+}$eykSpzloEE4>?#gY*UFn8gc|YW?^g!-P zpU8cBB^YudW8^AIAa^Mna^*81SFsdwrTLJn&|N&WgL#lEpA9*k7r9GXebsR>A|^(FM7R^^hx{4Y{%~$XyD8oN8R;Dz-wd!Vhv4fsiZBhuq~kkh}C4auwB( zyA%q!iblwl^g^y81acJzAXgCqxrz$NUAhIiide{19EDt2Hss18A$KVra+k(Kt}LE5 zSKbG?OL>s1NP=8N8ssXtzA7&(gj_`~OZAtLvrA_H9`~h*UXXVeuueUT${2p8m5Wla>7Qyf0UHDb#8EEl~6~E4s z1o%~Of3Ue?u=ss`i4pc*$#&zHBYyoAtbLT0vLmXwf)Tc@f;&a~D@MUje16Iu)Qf)3 zKUvPzr^Xx0xnkL`^K10e*?y&xTCr>>yDgRviW9~1SvhM&&q{2ba&aasOEiZQryjA~ zRn{$*?d7z>Z**q1E1|4UET5KrE0#~nJ-Euc=qIAL%7++HPZ6ys_Y%>s%166(@v6A? zl@@SKrQ<-?^RiW<`>c3Bbae*0s|UF2c&mthUOrAl&y`OU%eL~#%-+hUiQMh-2$B2p zG9zr~MGukdxlGIJDd#@p=a(SYU9x~(Y{da0ceUg>S6}6GM6RMVSLE8u=ZoCk@cV_er10eW%-H%ivc!8-7J4@Kbe* z-{pSzT`Ys&#li3^nghR!`S2@q;CIOver4KMsS#zU_{ChF3BR&6@Dmf`SNI%$O7SZm z4Zosd`00%Jm9Bwb@lyDeu7h9Uc=(mJ!LN8V{0j5nSG*K{r9SX0-2lJRTkz8v@hkO( zU#UO*N`v56_#A$vq3|mWgI{SR{7U2DSNI%$C9~nDWAVGBt9kvLL*b`0;#b%Pzrx4x zD?J9k(q#CRro*o^8-ABZ!ms38_!Tn4>nTZupK4tEibLR6R0h972Y$tV@GC8ZU&##k zm6pPMB_*TBl2v(f^gK zr2i|?ofKEkg;Yk4k~NGRCF|(nlE*WU&o~$dcIOGk4dhEOMTiUL24Aa zSRj^nF9a}(lx(0EEb(C!De)D{^5S5zY$@><%Z`#Dv248bEfht=C|;Zg#e!*2EXsvq zaV-=taGxr_xEYE?B~UCXfTGTeVuAJv=$Q4TM;BK?v6y`Vb;Y#2#}`?9KX;>G1qJii5s7Y9S} z;xH&)90|pX3!zxB7K#@~LGj{PC>BkFVv!dV3r0inJgu<3XfzZ{3ZSSI#bRG57R-d= z#R*Wn_$m}HPKBasTofAWbOxB1_BoBtKt{IAI7e?>O`E4KMxkF!hY6V3gQQ9&E-NLYc7QzdaiG-tGY<{2puPn z>)*cfw^h;KoPU*y&Ks%KORL^udj>l ztGa`VMvGsS9=p@HQphf#Pm1oSx}W5YQgwe*)Wx`xm#7+VFB%8ABG!s?&u&nS-zX|j zjrSK#)L8N?Z$C9&SVfKJC8);loPUS0BridG)SaSgYP@JN6!Q{Q>352zsnTVut z{mc|EFvotNdej`TXufK^rD&0A{6x!ST+8zc(`i(VL^miWMfg5TIB75$>LX* z7p{u#JI7tu(t<`+bn}H$)+oKyF25-(q+J#c7uWv6mE!uiFqDy6@7M2|uPbHsOntOVz2^(plU(j(Jjx#} zy5F4l7hS(XZ_#~rVW#MI7mg9#(({h!KF!x&2v<+xc(u;{!VPMj-<^Rz}&ZvzR1gk}AzM&R*w{U@;gXqZ*6w%KM7h8*5 zCVrn6`ikGPT#YU;QM=U7rgo{H&6&Y?o!>&c{H%hJ=G$V8cp-uQQe=$ixe$ov zu=BC!EVIVWb83sPjfRMs`Dax=52`POoM3NVu+rthUnQwh~_PW=-F0?=4L=N*BzqS zk0E-t3!-Q5K=kZ=h@S0*XwGDa=48=E&%6WCytxp~eh$&R1rW_%2hp?r5Y2NSn#X?A zhP+u2J+mC5c|#$Z$F9@X+zAlP9SqSk!yuX+3DMkP5Y6v`sB)6)X8s)!B}Me?dWfER z6{30G5Y5xhD7C5a5Y3$m(Y#3z&6@(z&xS)(WM)$JxwjyCx*npRt$=8*4@9$DA)4z8 z(a*RteEONLtnIvrW_v<3y9=VZ&mo%Y57FEph@N$aXl^J(KU)paoD7Jbw)=0nT+^J+ z<(j4~Hy)z7#~_-`bxiLW+f9+}1<~9G5Ivm=(cH%nJv|blxycaCO^0Z1HbispLNwb0 zqPckx%`JpzPCZ0(av*v-AEKw{K{O{HqUX|?ADzpFtBy5G(y{2Ci-fLHbaTC-d%6X> zxuwv}t%PoFEp&4_pqtxl?KK;^=klN{dO!dF?*c#V&zN*N2)a7fm~{HCqmeXw7_+1- z#-V4YeHe#yUW|1{j8C)c`C0ZTc7f}7w(^0vKFeCoF7V6%c7ZGV^!~N%0zZArtzVxx zm0jSc^VkKhGyku$O@oM&Igef7r=PP6{B#(*z_SOl3;gsg_Ye34_XCy(EyFB7v<$b@ zyh-ojr(NrQ*i!Qf9qT7i##riS&RQ$pUD+GO`*z;j;(a^s9d_5}ZDdb*UI4r6b^H@M zo}}EO?{pwFhUIP77Ei1&C-_mLEh<`?IG!GhpC0K7ZhL()op2aBsHOhFtyG zVQRt8PTx@rewIB(<9>g38+YTgH>e#y$y}y(+>@c_3|xJg-Xh(featDeS4BFbL?iyw z>}-*Kl3nTM>3aX{IJN1+**hfzFw`d)Sdk7bXCaHtc`A6IdzX>o&3zj)QmEgPoNjlaRJHfENiSPq%}K8wefX!A#;Z8JUQvfSgRJ?oHdqf`7Fk; zy6jMoGkP^yq^mQBawk7?nPv#0H*gNW#lYY5ncUCMT&&r@dW>w{k9M3>$LKLmPZ00U z>^ZdQj8uAz?D_ND|moPL!aL+8c#R2!{1BZD3zyF!eA^I0zaM)o2x=6HnVNXwTj zy)0k0^tMzFlf6`odH*Yx>TR;C>20!Ch;iTPSLtoC{ON5@`Otr7uco)jUQ2J2y`J7C zdlTd`Qt53nCqwSkEqa^m0(zTMzVtTPCGN~mNme>N zNp=!FNp>1NNw$rA8L9H1-&gEci)$OR+ni>uw{mLfLvj|-hon!?T=w&vxoX+HnZxKq zbp8w1qnyQR*?Fw5DX znd|K{>os$|oz?HwAwm29PRIN^_~pVELQ)o`x@7c)05nPXQ>vM z-niH&1j#Vx-&q%$EgBQ{3gd&GuqA^{{c}tGkJjejdbn6<^(avJr&{M z;e0=X9jt$wRU*cZaze%U>zrF2gPrf^=*bLcSWcv#MjDu-ReERmXYT4T>OUK%S#L|a zo=R|D%F&%ky)#*izxr%2Z9OMlPsnO!CDN@qd1~=L&MEZBaekOnD$-x%=m`bKIoZq= zTTbo3-}AcK`LPH%bLe4mM#KKhDzSf*vk3NQ5@4T}D)wKWiRL;Y%dR7I{tMUB zGl^pV^_f($-+v}U?7ujZBlhiQ^2Pp6&P=g?m@!%G@0}?V`>r!@!~Wy}vA>t&DfZnt z6JdWM8}=D~V&8hESnOZUnI`rRPkV~J{$H`bl`}`|NwGhX2m7r3V*ey974}&?;qmlL z)d2O`Cv#=st26Z&NS}*=6L|ylf2G~VKw2~g&a}wD_s(c0?TkNjTL!vw=F7krS^H&R z;F))1;QME~#eP^;AM7)D^7=;BMzR0#nI5qpbS6OTyK^SP{!E{-AIG!ZCmzVa^)u`d z{b7!W417O_tL2ikrfu0sTfYAPzF1I|3)xvm&e1ifo##E~ZUUFVwFy zT9M7Dq8CalR4>$!QLkR;L29^qp=X(k7+KRQ>4i>Aq!&snR4?=}qeH#W&g=;J*_F|v zUWnrnmLn}+vh=ci+0xrm6`e^h#QR^d)K#}WO{=*Xx77YReqWRbb{g^*pyT1J(0yJeQM`LJ*5lksf@3W(yPRKclr`$i|Nam zEvDCtTtxaR`tOrd+!ni5oL=nq@0Lp}U$b0l`MTvY%Qq~STfS+z!tyOkjRR?ci~}iK zsjKw283)qeVH`-`$T*O`g>fJ?opIoV?Mh9#!#I#$%s7zZ$2gE)#yF6Cn{gn0JL5ol z0OLSI3t=2cIlwrO;>9?Sc7So<c%%px)`(iAi#D@5C5Y!j8;J znF&aZP$h6Y!g8eLOO{@iFI#$B{)MX{HCC0t`(Lrt`du>5Kcu7{mCxSfLGt-tX03dZ zODtcrTx$8c@*4Ska)MRidQQxhnTM$bGShWpmdxBvZIzijsk&NqoTS@w(s9C(lP^=~wXUR= z$i}nOerhANS2k{bx}pK)7$8Zx@&=Sk*KMvSCFjTmtmJ#x~W(WkMZFNqy`PfoUAK)u8a*P~SWlQQ)c zd#ta}Z=dPOjQ{u?x1-MYGrcr!e3`O9<3?rXXxew?IE@>RGqrw~S)vi6H*+Fa z2*+6qIFvS5%QIfDA`jp{y`~u8aD=Krr9iVnrh%g=1jHz7i&?enR8e# zO%2w#QJ6V@K$O_LIzW9?rk{tq>!Zv?9s^vFnM*x9T+x{;R1Z-nJFH*QN`d~}SVoM@ zwHh&oq>dEzFH#n3hb*K=S%f_8c8zkN%9lDaw5VSJ;Z^lh2xAKqM>UB{f;mEWK6=3 zQhpNe;^)+S{G3{ZA9Y#sb9^j*PA$dHDLsko>N`FdKWQ`Zb80nyQs&_2)LQ(UT2D`Z zY7>4wIf|cCTk-R0GJa0^;pbE!eopn`Cu2B%GREWQR0w`PIf|duWB56B06(85<0oko zev(JwC&TXWor=KEso3xQoH~jh^-=P3DhWTy`|-n^%JtEyH2j>(!p|w@tx;)n@N=pF zKcDpD=Tr%PlDFXJ({%its=$v@e$q4Wqm-YdB>bGJ#?Pl3Rh-eM8u4>t7=ALe?%+6y zlklT|U4Bk!7oGFNQ+HH3z3EyTgD`$h_2TD~qxi`fj-TXFFZ`s3;zubzpDe`B$t3)o zOv6w58vJ|`fS;3GF?XNL#m^`2;75It{G2Sn&!TrzH`@? zNrUloveo8YUHC~~hM#mF{G_&FAeAN}j{lRg(e=?moN z_DK)?q_4qG`ZD|+bMTYygP)}BFZ`t4#g9^cK2F6?+93R-4aJXUU-I*D2Y%9q<0s7< zKOdLjC%GR#X=Ct{xX}++L!3_oeQI#;XLjy4G6CoLO4N7vvd ztrkCtEie3>Sco5`{2U#EpVW2uN!@^-6JzmnbPj$}eejd&i=U$t@$>0Q{G|HhCvgpa zQiJf58j7FPF#IGG;wLo{KgY`OlNwL`q#nc1iIMm@F%>^2=HVwb89xby_&HvTAI-Sr z=U5qjJ`TgrCo}PL;#K^lX5%L{?>j%Kh4@JvgrC$>{CtvzpVa60Nv*_BYAt?JoALAM z8vLZT;pd}K_({EmpHCvFpJP?jPwHLjM=3uk)%a1$&&Q?sNqvByPrUGx`WQb+lksz6 zHhw-@h#&py@^iu+KPLv8pEUfO7>l11Bk_|^h@TTv@$<>d7k*Mw@uQTVg!TAIF2zrB zC4N!@@RM*9KgqTDNp8kZLI{42=iw*04L`>U@soTDKgoCTll%ZbN5TE` zPs&*Q99xH$I$~^ph+>4)&C*vn& zA%2wdlRO1KO8GfD0Y52A@bmFj{G=?$PvQamq$J?yNGg8xugg!$+xSU&2R|tr@skpO zpOh{5IWiVMDdG6}I2J!*zma>~%GIu~6LRF|i=;^TX-tZjpNGkwd@EC2u%mU2xGGty zO*$q^_ced}!qs~`NtPZS@s*_ylem}t)u-J5Do)ClrMpRay5j#@dCYY;Azx-X5+-0K zNh^P@uM+3UOt;R+Ok4n0{6|XV=TUsDo9>7oVf@KWDwUrvlPcv$pCCWq#QVvN6Ibv4 znd@2NJeesz&VKpFiNkdD|M+9BTZ)ou2ZX!6Id(wv;+~}D0f${rj#bOmjiffY`X+JY zfM~s%tNHSiq+9a#&8J!ka9#f>R^Dnq@{_mf1g>+=CEb;)?Vkv1Y^>e(8`ibeFevWogKgX-6pCfmupZE;wCt)S^leCEX zIbKiw9B=uqevWrgKOaq?evaR!eiACFpQO>$&+%^R=XekGbG(oGITk?u9RHU3iJeLP zBzaIj3E9-ok^9t7(h%xLIo6e(r0?mm{0CP~Y!_7&f14^w@}!Cqrc*^pUR2S?i>acd z)mVyMPZjBBP!%POql%IyQbkExm4q`@lr)Vhis_(=l9p0M2`in2R8ibysz|AwB1d}U z>dBFJSUroIC`_VQ)utePLb$4vez9q zpPeE{a

rB!>G;stY-+I&#}>j{aYNnY@1_Up4kDwng@OkBpMN?jsr4i<>EXkB(Hy z-lHS>{sz6jQ1T3)s+vn(fBjX%KBd^5fX=60I)0 z`^f^WE|5zsU$b0l`MTvY%Qq~STfS+z!tyQ4J^GoS(+fnGxgE3P<94jJ6TOi;GkR5b zW|X=!6Sqoh4^Lx8vw|1x&YhXKa8~f5Rr$P!btR>G=IAnNUdII_Z6Uf!{cvmCRQ0!C z$8F@!Ox!!F+n%W5veX={Cy>-Oxffm+r(KqMb-DV(rnmrBsH64w0l*oz6kUUW0|VuoQarV4v8tFRY24||9CUctV|!Ptul$DUr5y~Emp zpyNdB#pKK0?vKN;7xT94aXi9uq~%MNUY0LgdRxj~Ot9?n{#PtzFXkQW#caf0OgQ$A z)niX7drH|;%AQj8l(MIkJ*Dg^Wlt%4O4(D&UgT`-MGe88j%82BvKN_wJ)My~rR>Ed zU@vMl_9EwDFQy)QQCd&uJ=jyqUeplm=~(t6XJapl_1D&zE!d06z+Ox$_6{$@UX&;H zqH?hpvmJXe(b$Wzr>`UDVK1gi_QV8x$Lg^cIU9SCvtQVYs={6rJB^=3jly2k66{6m zJN)!hCtxpv`A5&8W7vyWh`p$K?CDk6JMTae| zcQci`o2k^@Or`E-DrGOKO!hd|-AtwIMWtddDg%2__1OEU4SP!2Q_7xF_LQ=xls%>F zDP>P7drH|;%AQj8A_A~?coFt=EPFbZy@(#{>5S|tWiP4&dxwu=FCrLw(O%d)ycBy8 z0oYT@-eDVWbu4=k0oXgd6njxTGx&8>5B8$Ev3DpEdxw`|?{Gi%qVlm9#ayH*+JoAQ z2*zHtr|gLa_C9LEUPJ))A_5rqT#v)IXm&!bc6Eo-e|9Bg$YEW=t8#ch^1i%1Oqi-s zt|(!KynT6udE>()eAB?a$osN%AYryh-%cnN>5_yp-A8y9o}rQL)`#4y(`RaAyZzxU zjcg53#~_qYrIGE<;r$xf?u74m)4U{NF5_GH7L9KY^qCqZ6Y4cPIhim|W8LR*JlU*I z(Cp-1oWA8rJ<`I>wm}?$5SaTQdDgEdp_5rs=uF$OVtH=_~D!UUlYE1i!LtPrvx|K6U ziL0`g6Sio+Qg%qQex09*w7gp@MFe6mOy3jXdUW^!_Tu+rFJAKx z&8xz(cVsg54trxSJ{o&^Rrd6M$zC{DZVwN2V=q1ddttgG=qe2h#$J3P_QEt@k!O2y z?}(lPa)!jGV(*Ba;dZq}@D$KjhX-LVJ_CE< z2lgVnu@^Z5d-2ra@c3fv#h1xmYkU><4o|_}A!aS&X7--MRmdJGdzyL5UfeY7#p$_R zwe@P*>j`@|Ep2s!H-sj?4`x&sUg?H1NxpYjVG&R??l{M>_uVkN!)tu#ch(kUhNT$bXCP|mA!`tJ!Oyg z$X=YE?DZU+%s$+3FWI{q7bts|qI5skReX@$WDikd);yR=4n@VREO-13Qv-~dvR&lJLoBU-fB~t7)#U4(c1W z9p}Ij+3Sqcy)0)$T(0bS#_@#Q7vT}ICl}Z|I2n5fJzv<19g01r?8R)tUd&eP#rR<_ zb~yG9&cmpx9Z^T|SPwC(LFdKU@f!K=)!CuS(?8SOxFYG?{K3IyqNH6Ri9EQD^ z2<&~Z343}~_6`ol-u_Y8+n>bp#g6mOCHTGikjJx9*!yT=Tm@e$aunV(0avb);?qDzGKK4G)H={e=G3&7x zX|?ylUUV?_^s4NAun2qm!?72mdtc6on11ZVI`o_ownHLzIQBl+guM@zzOWaqy+OJr zSd6`>EbK+)VlS!yd(pQ0>Vp969U6(f$PDa7>RBJPs>j%iD#2b9Pr#Q%RbwxDCH4+Z z!QQ?I>_x1=-Umyt7uATpeOcJktFrgOLhOaE!d_?}_M%#`7nzT}h)vjwSb@FJ+t`cL z+}!naUl#VFy091eE%rWS*UyP4?OJj5MDWD?lc@XHi|YN(UQ|E!_C;V%?~%P|2YdUn zuy-I9d(nfi7qJ?9`$Di6JrsK%*pnO4+VSc9Ale&y(POX|Jsx|}TG@AXN36x(LB7@D zUi1{~?Tf%(6nBlw4|A>1{lZ>k8TRz5?Cm>%y-=?7I-{p!FM1aCqUXxqov0*gFM1jF z_GMvjU&IT0>JMd4DSLi}c3c-d^nKRoUC8UEq!rl7PLvGqD#r273|T zVlO-kd*QLz+dC0^hc!2MJ>A=jy~y#{+pDKL)WeU*UgRX~g=b+eatih$r+;TJau)XX zHeyfjk-f;d*xTESz5T7&i(G)c@T1t<%hSW@k&Cgn-;3IdT!y{KmDr11gT2Ug*o)kN zJ@uKg_rV72MfzZGZzJ{&-^bn|t`+{*-r*tG)2p(#mv8QO_IhD2Qcoy4BO>+J0`&Pj z6?XM-EA}EIvA4GudwUyS*i(NfdrH}hn2x=OS=fu1i@k_u?1g$@?;v}WzC1Jydx!LF ztn1!>R>TfQEWlpGV(dk*OaFO(VJ~70_72U#URW>o!dkJnN8h=iYZT4RT~9-%V=rPI_Vxr~?*KcdPeg3M zURW>oB7CqH;rpGv2<;woJq+>2p57yS5kc4snU1}EW3U$yioLKd?1c=)UPKu7LRVlf zA`*KM`i=|7GvXNbB9gHek&Zp}nXp>@e$$7>_;mpRyM+9eW|(&LVcy>|4u@n&4&f*0YzZ_0G^9 zcGT?QIz2zMPxgBE-qMxc)4h7K)XwV~FF1@HHG5cJd9=?@*Lcehy(*KtL%-EE9>*gr zM_RsQ>1Fw{rMIPa;Oz5o8)e6@SZX)UzCg8thRt{o38Oz9Ga?YJW{)Hl-iA> z)NUN5cH=0u8%L?#I7;otQEE4iQoC`K+Kr>sZk)YS+_W1fcswm=pJq$0)BC)1E!Vbh zv|7sjedD-t+p~(@ID0y{?hcMr>*(6IiQPEClhj6@@7v05oZS&>BVGF@s&$;+H(9OY z$-Zf71r7Vw%X3d~5T5tV#Phy6?8e!{+GjKylV}pnf~&-(<37 zZ#5=^LS^#Nu2z}6r3#hF-QhA>v6pYS{wBzt=-Q+Cr_O6-TJJB#6&mLn}+vh=ci+0xrm-w&|2OD1{$E0+4MfL(WFGJY@5f%S!ql*u(8ZkI`N ziREjSOD$iwTxR)(<#Nk6Emv5+WvTBR2q}}vw$MCG?!7OQMWMAa`DgXU`p$vSWK8ak zmdPif_OxhFrA*%1)hm;qhd##So@7k!?U%`0p^i-Egbu=F$SO>RR#M5K)NOAtGoA-~ z!sPSokX88H)sN3mzHzvDcdC4L22r=|q0REy9I9G%eZIR}KJNvE;xn{VKKnz*$Y*JA zpnNum-j&a`(DCxQKXej4cZbWT=*g$}$mh>p&jRiFZ>4q{2HEr9yX^UIozZT?5bd{; zy`^&75VB5g9|Vq9l|Box=f8vO`R_n`{yWH?{|@ny+ut6{klS4$8|0Sb5tbt@U$XSF zeA&|5QomWqExG0WuUKlgp*~H!4MTk8_GyT}-2PYaY`Ga`i+gx><5}QP z>Q-lDJJ6m%542~{L+ly!5PJqa#GXM9v1ictj+DtSgY6mgKzjx~n5TbxLhKpzAbSRV zmpy|X=*Z;NAiGu!v1ibO?HTkCdj>tkoXVCZR&JkYZQ^e#`Oq|8^w7dJM&w#u1 zv_Tu?uyc0^4tMs@)9yY%PaCwIo;GL;^{Ml!&z;&Qr|hFXgNMuEe}8aW4tMWnpB%>{ zEJs?tWa(x3vZc4BdfMIWljHrbSgNND_NJ%Z%|5xlpe;E3pc{uuIaJD_QVx}JsFXvc z94h5dDThiqRLY@JJ?*Yi*=q@~`rMsFPrI9MVQ$-W-0E4UER>t}LtujK=D&T&xBrVl^-Ut2!^MJCsJ1X36UI{iCtEt3Xyc9$`7s@+C_z%a<*^EoF6AiLCPeS1e^UXf9TFRbVwRK~{gi zf1Iq6ODtcrTx$8cJpnm*-dT-j|CxB+*@)+W zVmt@r<5}nB`TdP}_Me02z?Jg+>(Hfm4xKO09FMRZY59_+m*vZr-j?#blhKv;zhWuR zfot4;!Ow>6op;>+$&TebAm8nmcC49G=n9!9HFHvG=A_ijNvWBWQZpx|W==}YoRpe5 zDK&FaYUZSr`G8@Vf8U+ncqilS>79H#aof&*%m+G{_t$EmtJl8(^Y3rLeBfftzdsoB zfy*%O=Y#pcL6{F5iupjk>$4$n0p{O-8}otQm=7F-`GBdI4;YF0_eWvg?-=F-M#=Eq zoh2B4e+GsF$748f5{3h(U^sxk1JM#V9m9dMWcbsa6&UuPDZ?Tu!}{%H_~#mPJh_5V z>I!Dh78&l^vt5Sk_5^SR6XGSeJ@3!u3MR0TE0|y(u3&7Un(vteRilQ|6&7n4qy- z!MwLne$w|;;pct+O39-=^<2U1Y2gZHPlx<;g!su%cSxZ8kYioVAtAc&Pfl@t8WO8L zG)*A~JpNI$w+N4Ec1Bk@AxF8&+0(;S&U?$b$_Yu5&+?#p`TXCjoZwKda`dXMa^73Q zRnGQE8SdQE$5qarZ@J3R@`l!j0__?##A~3ga^Bm*RnB|Mon_Q;a0Pv}zrK~zakg!w zhW%Js@AMx^4R7B{4g0gk*XzeO=05f7qlR@}HM}jA8s6?l4f{u`hTjjariT5!Rl^+X zewI@Avy{4@rPTc_rS4}bbw5j~8ulNf8s=E{vy`e~|9EOR*uHhqe>^p;W7V)bA4LN`68-a6QKRJKu+bHKx zw#~x1&gie)ZJWz}s%_(C{>i&G_wt>n3V-Ii$zhB)>+nDHdr=VHd!`XUbnoV>yq*At(G?}+br8H^)u^L^;ya;QZ3@9Z<^Xdk8hTT zr-W1YC5UVW1Z`M3I+`}H@~$tlk8-9z==CP#KV9#fs~ z?H=UukItTe8Tt$Aod2nkHOE%p0sfgCy-uU{EFp^9Yw$3XI7i4+SaNbf#W&)Nw%m*;P{_i&v#97n`ikimh&zD z)pCL5uPhf@E><-zQ4c~cv3$*PspadI%PilpTyFWMtavRwoU9w+P0NFN!$F`leBG!?u!rF7Ra8YZF;s5>ugv#tt&n|h zTZG1r-fgk$G}?Alt>ezN3R=WA{&H*ouIa3J`Mxu74s6vUh=9fg{hS&X^m97DVdM|+ zV&qq9~%+VC3HsOuNurT_ftB7BKQ}Z)N27XSec0jUyWQ-!0Hs;JKQc!cFh%a<&@EMKoilt@^JNUkwk{!`*D>X+M@Ae;-t1N$Gx!Ur#mTN43 zXQ^3+t{^na*xJV|WAiFz8Gd7#W&FvTett&+vy9E#nPuozRqUUJ?mWFv3WbQjID1o%h*xnp;?CC1ZEkVmoUrNQO^kS zr+7w?9WBfe#_IoptZvo6frZUS*whYY#Js9evCoHm_m^v4c^g+^D<9{pd$i!4%9P%=Rj+AYBdJ$8b@T4dBkS@r6cI6 zhtN3UGuc_AoW=;EWAz$7p6vMi;{<1~jwjM<{PAr@5T9}M7kX7Ah|XvP@tMpB;xmqK zQqb`n&b#qB2i!iChh=VhQdX&|Q0jA(hb^NlwKvqi(M_MG^LH%2w7hHim8E`Py?W15 z|Duk+wtQgOWBJJP8~b-3TRySu)A>PePepo+Tfd!oW~py**fEs9Ww>Lw+qZVcMXF#) zH%tA8j#a%%553>pU3*(}tm;+j?;ZMtxPRY{hgkl=Qhxz)#~Ak?+VPJpHEQVnKeims z&*?tGa-^kt0iE%(^tM!ApfjT^U$Oj4%Q2RJWjWUJuPw(}{*C2$%b!?Iu>4!g4VGIS z=YP7~_u27&%Rk#sb&KNxJ4t$_9lB@cOx&S+X3ndde6&;YuXjw*PRWrw7HFp=$3L;- zNzPyHn690Y35X&D}4z>KDV=e#Ma-8NzGaNDA>ZQLtN@~B9^6xEQwVZGHua*le ze`UGQ^4FG&EdR}NvE{#8F0p*ga;fF(mdh;Puv~8WrsWFDw=7p${)go%%imc3&hl-` z-;?U=f9H2ceLXqca)jkb%a<&@EMKoisdihpfR2NrR7}sXiTr&QmQeX{1?ml zmj7zG!17m?3oU=`%JW_5w#f3|EEl^r{4rajJh{a3HOr-zuUjs&e8Y0N<(rl(EZ?$R z>8kT<)d)ncviyzZYRlhRYIM}Azq9(F+r;+Z( zmKQBcEH7D>T3)s+vn;o)aNYFN{N1wB@~Z2u&qDVq%WIa^mNk~OmUWi(mJODTmQ9w; zme(zBFxzo&wY+KBX4!7pVcBV^=REYz&-v{J==a^~H9)_watD3~1nNA0rSZS2gb)i|7THrtk8D=q)S za+T$8EH%=v@6+|c_kE{3Yjpgr^E*5Lw&m|FHFE1c>y<6c0RHQNv)gh0kj6Y%6WT{_&4%b=^P?w;W+P(()xsFUyxLy)8#szG69= zf6?tPEj8xpSYw`2W1doDo>F6;@^36P=IK~to>Kj>QvI<~{jpN@s#LuyRj*3bt5Wr< zoNTG8)v>Bp`45)zuVdA&Qnjm8?J8BfO4Y7XwX5G+wX0**u2QwDRP8E%K?~70OU?D2 z<@Qf@JO@^8zqI3jcBXF{rezHYh9@(s)7mTy|FuzbsMrQ_`z>h>R&t1N$Gx!Ur#mTN43XQ|Oczuj8L zbA5-~JG2?*d4E{VJWqMZQtd>?hb^Nlqji0=fO($I-?9AC@~-7qmfe>3EY+g)&aW*W zSoT;xvi!#W^~aV^Ec+Z!-^I-H^i%a)KC^tT^FEsAt@d4}c^^E}QBvHXFh=6O2vL(3modRqS2ayUOH^E~B9%a<&@EWIuN!cy}* zz2_B6+WcyF+WcyF+WcyF+WcyF+WcyF+WcyF+WcyF+WcyF+WcyF+WcyF+Wczw4VLQr z-$`Shr!)I4|7<^%=6Qcut-es!)EAPP^ZjA9=6tia>U-RrnZD~Z=li*@zh-;Rf7S9` zV#-O*WZxjo^T?k%lYBR5R;Tlt)xGNL!>n%oZS|M>G*&Ct_YIsw6%YI+t&4fyA67em z)>Y{2x^1kKQmJcj@EFW3+>eG(7ePchvW6LL&eadxemA~2?saE-`%^URn zalhK^qwkOV)n;F{O7dG}samB{t@2l!{dM)hvAZ%zS05aEC_~jMIsTsIU?+NWm|7)g zhFJc& zE6cH#e{DI=@^381TmHmyg5}>@s@3T8)e|e#6D!pdE7cP#)e~<%=B}Pt$Lfid>VuW) zgO$@P)d%ZXeXvq}uu?5SsXkb#K3Ms4OZCAz{)Oc~tF;tsb1(OEl+L_oskw!YHMdauTdE~*c~$iX;r*7hw`1<{ zmPafTEI+b5YWcC{G0RUZ6D^NhCRu)JnQWP2nQD2$GR^X&WxC}l%M8m*%Ph;&mf4nP zEORV#Ezer!S)Q}ZcfPkd+5Nob1gOD!*3mRXitRyaT0oUPSe zveNRZK0i;ZyW};?YRekSTFW}iddmjOM$0D4X3OiAH$;9atGmjZ&W|=1y0_VJyJd%E zr)8JrpRF!#nWN8jW`cS|S@Rg8PpkF#f#pliJDZ!;JO1Zpt?@hmck?Y>5l`EESAFFC zEhBYB%<)g`_;2+IGg$joPICTZ^8@voO*SgNxb#ru${Oc|5toN>) z&&th~!Ma`^y=9nsR(+;=*57WizuNJiW$HoIUordKGGXBVC|?~&OPlKU16@0xU0tDT zXL5k0hvh)aL6+aM9Blc0%ORFOupDanL(5^7KeF_+{ITV5%Mq3%Enl+qvV7Un+wv95 z(UyN{ImYs@EXP{@wWUU~-)+?liPV^({H5hwUUggSIKN$~nGLzb@-@q)makhbvwXvH zx#gRdD=go#{J-{TBDZF{+XhQjxsEqlilvS>S*q@Jth!fjwp8`&Sk#s7 z)vw%csXjo*>I0NAqf{TD++i7DsZmyE0xi`y=yFQQa0qEs)UR4=0Z%u;=cj?Y@EFVV635@o*SdCLOJ3zmhJ zMV7^u7cEOHFIkpaUbZZ=EVooIqED`}yk@Ds;kU`UTSc~5s&CM-`Ud4qOZ5#pZnyli zrDjMvqn<$d+!?y@uKR30&HbM&=UA$(>dairf3}=w`7f69E&tVWf#t6(7h3+>a*^e~ zSuVExcT2T)eX?4+QmtL7)~-}*SE{uu)!LP6?Mk(FrCPgkrR9HEuCn}%k z47J2UIz6-!3+eRGN-U(icUX2>c3IxGfBg%79$mSse;BZvW8I67-1tEE;>juhA6fST z9aVMr|NqQnCYc08L~cbypp+saMMOnJL_kDP#^MT(Zk3*OLL zYb~WpsUo)o$c+$^kQ(%X^VRH6&f6qR9pV>2m zS79$yzsxwMaWt6ot!APcNuutr$D;IC)cy5Xls<~mM^XAHN*_h(qbPk8rH`WYQItN4 z(nnGHC`uni>7yup6s3=%^ih;Piqc0>`Y1{tMd_m`eH5jSqV!SJeKbExA4Tb-D1D@N z|CEi<-|{H^5}oZxM(@Wt_yEqu2k{}Cj}PMld;}kNY(wpI4XK*BN7oRWgb|ElGNz#3 z_11IruD49XM%WlHz$SPhHpPpu8D5Odu?4ooR@fRZ!M1oQw!_P?J$As3iGx#a_j^?{ z%XI(J>b)wd_o}4ctCD)JO6t8TsrRa+-m8*&uS)8@DtRNXa}(Z-1N{2Be|d7^WZl2C zx8PvB6^Gz$I24B^4$k~g_b=@oI6U#<6z^5CBXA^+!qJ$Hci|Yk8^_`}9G^HjbBFF< z+KD&`_3oZ(rr=b(2Y6wf130#6t;!=DHb?)k!%W(xhgDde5xC&R}v$zId#Fy}8T#v8d ztN0qefp6kl_%{9(-@(7(yZCo}5C4H1@t?Q}b=}jE>$)d(-IKcRNnQ7(u6y!7_#u9T zTk&JuhTCxm?!;Y~ftk1)_uyX4!fedJT-=WZc)Apvw26#zK{UjWbXqqH5;g14wS``$oQ>*j@Lt?M}={{4n($ z-OskWaS!grEX>9n%*DLKkK?ss!F=4GI6H2v?q}NrScpYfj0dpud_F^>5Y ztLvLa9$(+a&C;Fe`f>erXZq`LZFOh*>zR4F;++`3RaZRQIS3|AjdZ~t)LezV@VX#6 z#lP-yVrHS=^PO3yd)OLY+z3*$F%Jjakek#+~ zy1T1A4pVx1kAu`GIC-7-I80veJr0vMc#p%B-rnPoKHYm9#%b4E;?qgbdym7Um%PVe zOr>`>On%3^8z*n{K84Afy}NNrztDY&hH2eGv+;hMgAd?bd=Tg1LpUEF#s&BYF2qOi zG5j4a!pCtjK7mW{NnDCg;nTPrwKHN&^Uw;)&)`b@1Fpi=_$;nL?W7pBF!W-gZhR#4 z626S<@fCa(U&A+0J1-{m4ZTVEEnY{vFw%R4-lqIldLkg&CNMyKxWh#VpLm9L&Xix;eQuw4ZVT9zg9k z8MV&)O%%iO#Ib2TLlszwaje3_SdB-pCJ55Idw)x?4AwAsBe>Z#+_Q#uX01m`Kcnc23TX6{9hC^`}-i~+RaJ&;o;7A;WqcI)t!ZCO^ zj>T~}9w*>LoP?86`)(%m)iLe97_XIz*w&O3KT=4tIwKdrac z9`;Fm3iW9x)jW;MQQr-rnicpAuEakE!RYz=?lAi-uE9UyT6_+l$3Npbd;v8F(ZAIk zWYT-yLo~{trh9L4R`{6nIs6I7f6CACuA-5(-c>X<%e#su=O;bwN4>XbLb~@BO)mD{ zqRIa5xHIR!dtkD^JMODd3%$2U&(YqZ$^P!R)1%gTZ_!`md5Ak07ooJCPxQRCVQh~ZyqVW1?Cb`I2G@~-*7zlCBD2fPqPVoKhD7ia4tTG^Y9^@j}PMld;}Nbqxcy9 z4j19$xEPSeNC+@-w%*5Te2lrwYW@8TK;(jc^16Zn2a8{@c%M)LY${ zY_OUUDB7A4*#3Ak4#0sp2yel`cqLe3Y>1(dg%3)m+T-`!cJS%7^K^(hCHm*La6 z9Dk21@EKf*e+(KATcufmeHPc?pKvWchtK1maUH&ZFQV4-caGJ2Vpc18nGpm-hv+>q zt2MmbgIdF@oP}D$tE@G=)EZtMqg|iS-s8@e@TYhJKf{ywIiB)g*IH2{p5_#aYSQmr zud99SoqKe(w~Z5}6Xr%Pz$SPhwn%(7GE?tj1w)5xJ^B8~9IYp<<^p4~JQt9i6Ne`( z&|JXw;L)qF7hV_q{El{d|Ekr5MY`IboUl~yTm=*K-Is|o6IN)osZm|4O^vs@!mGb@ zh1U`43hy%6s4Ki{$VOe^T_ziKg?E{3)D_-kvQbxfm&rz5;aw&hb%l4CY}6IrWwKFM zc$djWUEy6O8+Cs{z(?>=_nNNiQtuyy5*vqqtE;+A!U$?UrDd`?3us1;OCVPz0lxf-y|R zG;D;8@d9juO%o5Lch$VWHp7dtIkv!-*a}-?8@vQ>^s#APU~k3&KIV|_c#JO8ZYz5W z4o2PmP>t?>$lFkNKU5x;_;_@g<`MP|9G=*c-b3>UI|4`IC>)LHco&YryKyXz!|{pw z(cX({C*mZWj8kwb>X)bVoZoQ#nn$Ex>v@FKJVI(*lo}VM#zm=lgw#AjY91jqkC2*2 zNX;Xp<`Gi!2&s95)I36J9w9Z4keWwG%_F4d5mNIAsdDYso6yO0L>=!S2UZj{qbfTfCF(5-hzYiRvd!2;ZPigx8of+ z9Ph*tI1)$UXiUeua17p!V{sgg#|bzQC*fq&_&vH-a|*ZO9-M~vqP|^H^}og$s85Wl zhI7Yriu7wer;wUcq!0I;LTXMSHK&l8Q%KDzq~;V-a|)?Bh18rvYEB{l7+jd1t~rH$ z7T4gPa4kNE&*PtQ9ln4s;!D9rqmPF*vly_|GYhGih1AUAmKmN|NX;yyW)@O23#plf z)XYNWpk@{-=LNy7W3}7H=3`u|{qcVHRUXD_Jc7PgHQt|ol(ne2iQ@AKaXaqZ)7-?K zz|Zg`evYTy3e8P4>Lx99K5K@em}`cjf2A2p&9EVwq1eWWqS0qGL$OWpLevapw030c zuLqu|$o7dpr%%;9<U9@G<;yAe;9p(E zEA$M4f!p*6Oxuh4>w;gVH}hO3oo|~;Z>Rk?dIi64oZd-u8C{7qm(iH-xlH=Cn#-sy zp3A7rxuCg>Yd9A)mvNbML30_GITtjSahY>La~YR87c`e~nR7vN8J9U1G?#Iib3t<% zmpK}8qNjXtPE>`f$%^q-Hcy zGa9KGjns@rYDOb9qmi1?NX=-ZW;F6q_qAp;_Hk!R=y#geY#;Wa<~246BN)YGOu-nY zVj4EW#&`iX!3(h|UWCo?Vr-5ruqC#_)_4iF#Y?drUWVZVxmovCZ&eY>q9kCAPxW*ak1b8~w`O=Ok~&0sf1cWgHpyl4cq94ji7yzHNqP z8FmDY#8EgJ)A24GgLmUt9EW<3W!UT5({1%0i=2ddk45DvsP|Y@z6XE9@!XgA>ed%D z%dq$39DD%h;)6I3AHwO?-=gt8Wt?_O|94K8C;IJNP$z z7j@55kG_Zhz>WA%+=Tx^-L24H)ZGfHyA@J*E2QpLNZqZFx?3T2w?gV}h1A^&sk;?Y zcPr!$+=;qdp_&ZT-3pa=qwZFyth*IbcPpgsR!H5gkh)tT^AhK7>ZMQ9ZW{KUW*n~3 zoeQZu7qT!2`YsI>Yn6B`bP!8}cEfgP_F>BtXKqi{?86=knhlzxIf%<~tir=sjYqH! z>#+fk`IY@?%D$_8cjKm0zPllHcSGKcy1SvW){s(nH>B=vNZs9#T2D%?C#BYtQtL^n z^`z8#QffUZwVsq(PfD#PrPh;D>q)8gq||y+YCS2no|IZoO06fQ){|1}NvZXu)Ou2C zJt?)Glv5I44OrlN94c!)DYc%I_oD7`sH}S&QujEd)|Qg9O!E=tlIA1!AxAa*gd(#d zyab=br*IiQjmz=(xB{QSmH5Zt^4lA0R$`yUHTWl7i_hWn_-9;)FW`&#QqXzW`}+Qj z)PAcpH~HBOVa-kKPRs~e^j)C2iQSERa4%+IHs)Y1<^`>8>aV$p&By(+yLJNG16YVf zSd0g;1WU0D%dr9vVLV8=y_se@_AplC5v;+ZSc}JqyzauNM~*wsHPf*t@H0G#pW`V< zMl&6a`E`Q2K*UvuNI zD$Qtm4AVD%1+#`7_a5WxkLw+oi~26ootieo^!;CcW!a~$QK z@7LUPN^=~SIlp|rMm3yYzF(s<=a=u-sLc80`!y$kIlp|rMrF>g=)KDJrJCbt6!Kjg)pKS= zXZtLO-j8!oJ49477azoja6Uea3-A$q)V-`Zj(yyD5?ZY}&iez$YK~)*FoID`#uSWU zDyCs0Y>XFR6TA?c;zigDFUIEB0$XA$Y>k&-Tf7w8;bo}1d^%3u<&zx~2d*6xbWVIa z@a^E2s9(NP`3mfYgYi}zg16yN9EP{!9XK5C#1S|WN8xBp$GdP0-i>2%9FE5cI1wk| zJva^T#TOEP9oS2AH2bm*B_%!^*e4XhD5hWxQ!x!2VPm`in_$z#yc#n>EM zU`uR;t+5SWf;T$0y7y#n#sQAG_7T<%?5}-wZl~0{ol@_1O1;}Dhb3Mg zI9Rhndk1PC;lTI3k5FnKq0~M?seOb}`v|4>5lZbNl-frqwU1D0AEDGfLODK>Kk#Cb`I2G@~-*Egh9dqrAsTug8=7#ouoP!VGTznAcp+1kSXU@lmaREMp3-M8W z41b4<@Nry>Pv8=K5|`pr_%trZ75EIU#6RFFT#e7-8hkNvc;Ft*4eiUg9$&#%@ilw{ z-^920w{PQL@g4jdzKegy_wXOM5&wysP`|>U_`J_+et?@%zl*Hn*@FMU5Ah@1iXY=P z+>SeNC+@-w%*5Te2lrwYW@8TKVqT)|hBcZG`WOoE02U@bx%#Z;eRTtOXx?WlF^*Mu z7_0FJ)?qz1Sk3bE%9`cb{&+JEz=1djZ^6NMD-OZia3~JL+wl$@j(6e+9EqcFG^XQS zI0o;=u{aLL;{=?DlW;QXF73ckn&r6__uw?V7pLQ|aR$yp&KJ${)bE<**(VaU1M_s( z$v%ls;WB(0m*eko1wMl-QTti0{>l4Ur1rDOHK_e8Dr-NB)P5GJ{VY=ZS)}%}NWGIQ zwVy@mep8PvzTYHwVutG9_5CKP`%O~!o22eHN!@Rfy5A&qze(zTlhpmDYqEU5N$P%+ z)S6&mp+2o|wI+~S6G*KIWDOog{pz1;juDwph}bE;=1)nBJpLp-j!$?*(rimvs@ay} zuX)z7fs-}Qvbu9HaJuGMR(B4h?i@&c*T+EpeypA|H}bPY!@vc)qqK71Qq8S|V{zSgWtN2^(t%jC0WRjwhQHLG%&eAcYWW%60GDwoM;&8l1` zpEav;nS9o)%4PCdvnrR#XU(cyCZ9E{a+!SAtjcBbS+gpary%*PS(R(ZXWco{S+7}@ z&Ror^Tu&zJ&XF=!caH2Fd;sU-gZL26$A@tNK7!iea`jK%;UXWW-;*^rxx4Gs;L1c^ z_tQal{1x`VtFR|tjo09{cpuKj`*99FfOGLdoQLyq0Y06`xMsTU`!safq&bCs8MAma z8*?xh^Kc*L<9;l_16YVfSd0g;1WU0D%dr9vVI{_~3J+s79zlI`w4!trYq1XNu>p_a zar_id;AeOeKgVyZ=1@m|`K{(qHi{`2!&FSeM%WlHz$Vx<(Yw!EnnT%UcriA|7T6M7 zVQXxImtb4>Md(s&hnHb{?0_Bd=hz8P!X!d7s!NGVd4#C@SC=O5jtk1ie>)8=F z5=Y@^Ovk%$4Bm}naU4$LcJ8rS@n_?a`FlqbdJ@ zt8g_wi)--3MD3MZHEXjk<9d7rU&Yt(4SW;d;@`fFf5mt3Z}=|$9pA%$;70r>Zo+@z z`@H4{xEcSATkt>lA%28g@nhVE+i?f(M13o%T9kpAxEuH2Ud+O5%)wmDOC0O6MYA@) zLIEDY!o-no(?dmx>OPx7rHOc-RiQG}4(98JXdY*^gIQ_^v(ye|sU6HxJD8<*FiY)M zmfEi@wO?7DNia8xF-`cst&K!|_fWfg^Dgj>dGn3&-Hy zI2Om@c$|O}aS~3(DT!mh{84jBx8fe0hWFxh{58(NnY8`4IE$M5DC@I;iV0`8=9oH< zG{?01ETHP=p*{OP|qA z^bV=j-fQXex{2N)wLY(#g#Vbx>9Zxg8lS~A_$OS8&*AgeCBSpI(sq^n%o< z7v%f+0dB^B;}-l7KfXt|+fBR1a3^LYGOt>$&n4O2xCi%Q7G`4(=HmXu-}@{N z7vKRb#3C%lgII#4Scc_Tfrl`j_@Vn*&C~5+ti~f)gGaFz8_0@d>e-my9d`B}_t+Bt z6i?u1coILyQ|`f}#h!0y7OzoDvv~d6q~H5d&E=1FKdHIAZJfyNvrTh(+XOGf7Ktw} zf6wm@|5CqB5(ItL>id>oxvr;v31NMoSM{ySF8_jZ=S0rc^^spDzUs0~GkTXVr`$F1 zKYd=%cQV_ai8I&r*1X#+~sfPJwa_V+wTD+68MwK7ojv@%cxv@&p+(Mc-<*DyM1 zW#BTSlU4>UGdgKy;4-6=Rt7FJI%#F#GNY4L1}-x?X=UItqmxz!E;Bl5W#BTSlU4>U zGdgKy;PMn?bkfSeHH=PL8R*KQm4U7WS{b;Wu_($|`?=M@=H=5}EW&5bJCHfdXPS*9&D&c77Hd-Zo(!D~f1p6!OfmdNq zyc(~;YwYLB7R9z&@;hEjVBrS=#~?J<5%)E+~rJ%&#!ah@Dn0`96!Yq_!*wW&r$aQ)kk08mv|b##xsepE?ckfUh{Ll z#qaPeevjwy2mBHL7k|Qp4F?!P{fBBa)=71~!+Q;zigDFUIEB0$XCM#1EZ1>KoYnuQa=nZSWGj6x-ot*d9Be?qlltKS$lo zRQVU!8GnhF^ZH%!3hahgVs}R}{42^muqR%Pz3>{m7O%tJ!Nl(Ggs-RE2lZWlYDHhX z5&JoRyxUZ2x2e1twcAu>?W&X7Z7Q|fRBE@W)DAnT9d`0I)DAn9wZl$ohn>_8JE!kM9N$stZ_oDWhs;qscQu|EhOj`Y0)NVV~+(&sfy>LHeMpv!Xbna`dW*?+}9?r*y zaREMp3-M9>9WFx6j}-I8_(bB{PF=KCvrpnvxD21h<@kGCfzRMd`~%0i3jdh+vcub2 zw^@y@QlqQ<6RySQ@Ok_*uEQ7bMSPj;T92>btN0qej&I-wd=s@7OF8)$)Ltx=wHHfj zFP79^EUCR%QhTwa_F_rx#gf{KCAAkzYA=@5UM%@Oet?_t-?#-o#E)<*evI2R_t>a) zo85^SiMsBawQjS!aS!grEX+oI#$3KE@qKsl0Odj~!eTs#C0L4OSdJBV2nT4^aJT4||W-o2|bV~bW=u3>D^O3P)&7Ok{gW^B<)%VowEt+ZTbY|%=~ zWyTio-c^~gMJp|r8C$f{a+$G3D=n89TeQ-0nXyGHEtj=>S2DtArR6dsjMiAX!unfO z+aJ?P%k_*WT50Kg*Gfz0xK>(r4nBZ$@j-kD=i|e;03Sik&^o-WHI~olTeY*Lw8IP9 z*BJy6pUdireP!0=g#zR^|>>t&z(tq?o2kti?A79jLoqHw!~K08ZW`Ncqz8S z%dkCmz>bN^)|uMb@>Po^+Sy{e;N^+4O;&1ui)*^!mHO)6EfiKB2d zrsG{W2JgnPI1b0-1e}PI@E)9o_u@>B^S3w)?;{$zjwy!s;~abd=i-An59i|oT*xat zijU#%a1lO^i}49uf=}Wyd^+)chvmU?%D=}I_zbSZKj13-Bd*40aSi?n*Wz>dXIzK> zgD)hGUzVqL1ntYXp88ktReTL!$2V{TzKL()U+``GE53t&!*}uT_#XZPH{w6>U-&+L zfSd8(_#gZbKfb@{xC3|MF3iA8+>Lv1FX~->^>Q}qU4E5wQSb7rybtqH*EH1> zpsr~u>zXEuP}ek-bxo7Hrb%7Xq^@aF*EFeXn$$H-R$?6WNfp%`MtxF6wN3j-l zCDfz363PZVhFW2$Mk@@d6^8r_PvYnJ1?~M3zrwHa41U9NzN35=zsGa<1OABri$7t) zh5`&>7`3Ws+R=AVq*fJDs|u-Ah19A-YE>Z{VPm`iwW?5k(?m(TwYr01o8iUS99v*Z zY=y0{4PJt6eIDr!ifxCNVSDU=9r5Sb34ei|@t3Gy!qO{Tj$QEz?1oojcl;Ihz^kw) zUX8u*8oU;-!|SmR-hh4aMvwNogJN&S0lo@ouic3@vwa^$>OP9peH5wtC{p)Pr0%0g z-A9qb5)Bux)?Pb%2M$kUv^%CdD|Q5q#8EgJ)A24GgLmUt9Eal*HJ6;!UOPJxC*fqA zf>ZGxoJPBTO@_?C-_W9&9*0AWz`8?owpl;jp|SVl9DD%h;)6I3AHw1vOO3`-qp{RzEHxTSjmA==v0REz;nS!wS@jx?rAA|^(O7CUmKu$v#$l;( zSZW-WFDAb0&|CLi?8~?wU%^-LHGBi#M2)NZw;EBUMpUU0Rcb_)8d0T2RH+eFYDAS9 zQKd#yxd}C{s{B5$`2lXmf8!SX4}ORr;a2<@x8Zi&fje;*W?&}n#yz+fvoITTFcguGO}EA_y*=7}lpNq}Dz1DO`q6<8u5xuE1w-C2mW6 z)8a9$2<=YP^|ZrUtq85Ir&8Bbsq3lC!fedJT-={H(`H7v01sdx7GW_S#1bsUGSp{r z^l$Z99C-+j(YjCEYVDoV=%KxHcCoGxlCqi4d*hMsWqI- zQ;^Km8qPIjrq*ye`?Pv<4cX|=C0sbspG%OMKTFM@rRL94^Jn=GYW}S9!>C!b%8%F} z92=z64X16!V>a}f4HwzSV>bG=3WH@$KhJtP`hr8NHl1=@OO{tWj;Ck%qnoutF!)$Cd-YG*tFUrlZg-tzQ zzKxo}HZ8{{KSIr4ZE6}WlR@wc8BX)R3IzjPj$Le1|BV;elzhq~Y^*iq#!?@uk)gmx z6$;gk+GHI%;RfW>fF5 zX`k9g{cNOMhJyQC4z;qe<&-b8kqG4yoBWw=bh8cf*Mo!97t2sE#pRSj%J;jRdZkTv zkA#8;@z<_t^rlU5?}vil;d8Eu(Dv{TF2`P^{%*Vl>5Ei9Kb-nMG6=49Ib3g}KDKCZ zwacLiHhH5A1^2j|a*=Jc*2aD-wN2RNl$A0ROu~Czlf24CkJ{KK>Sw#0La#;lP~%(; zf*WKwcK%4NqNb+|1*37CA5D1@&-YYImm7U7qk$tI4f@Mau)_6euiBW~s|^M&r?j(? z#WEC(bveng4M*Rw$iSgoOE~ttO(8z97gP>{4%G0sV(HZMmeHVx>l?WyoIIWKM4Nj4?5L(@0W~LV z%D-*Wh18H6k-xhfa*u>#v^RFhP* zbv}e+?p&j=l{BoISA<47=JhEZ`9CJDZ^C` zkyqj5!zzbU+Sue-l>cC3g_K9x)ald^f#g@HaR!Fd$ddrEAo*UGqYvTxxE*Ol z@Z0yQ~!t4-!LW0PDC^J>YL^JrTe zqb5cxVs}vUoQ-y;e1lCR$D${xr%xkCU5?DR$sVsl!OwWqy&U@Q8BOLyGF=~gjYq$* z$qyh$9x8J=?F2OkZ3?dwNw^$aW2613Uxp<%)jbjlh(q*4*CfAXQ_i2iKT`IY5(;jl ze499*@&*|Sdb*tA z2!x~iZQ36E-llG~Nt3AgM23P_sTp8n<7~-Ue!i0jQE{FQs@FAN--p1&W*r%?E z5sesULCX1_B3n}EqqNmL$GsUke?>^$M)_*jr|@X>J(ttIv$4ytEsv(l;J;(IdnyR7 zli{@2ZG>?k^d)7Vx#5tIW?@|vl?bUAoi20>$&lR8trz~yknh90$P zU1d^`D*HE-J3A7oMx zayb=aAW~z;GoYAw?Uu=^X^5_Jcbbi#T za5+Z&BYj*>+iSyL^PG;j$3`5Ht>!aje;Bz4qL|v2oAoY%+K3GHf zF`In5P1;UPo(u)kT~2Z2gMbJm|CaLk3@2y8DXIyljI^=OZDhR+oxi?EA5l4A&L0Yf zs~k>w*2XTh(FK&J$Pi~@C`hNiUh3oMGMq9RZ?>^|8;#rKaW?fRp0AlreU^>QqnvG{ zuTVp#rG4vi(s62hoZ)C4HS|K1Xs7meP4F+9Tw`O0Z7OYyV)EOr3Dc*sFI-Ogi;Y~x zGs)qU2A5MDfp9FxhM%Y0+(viW$j_)he>`uvoIFj2m`jA1bB0qMcYW+XHu$Se`IAk4 zM~2Ri_NjNeCPojYkuA{{u8E!><$Ce_Utl@U{J_>^G{320!QdSMccn;`wMhufjowO>?`# z$%AZ4%!Z${vFSGQA^HeI!3>vEf3RuGC|Al*u-xTF{)^${G@G;+dHvKET#o)lhJx2! zj`=x3aI?$Foop(RN%MIWj^)Z|aFGn1Kg&{SMKXQSh+c~%^C*8Y77baH(Vv8mtLl#@1%=O@#r(bcYr za;~M(3(?a&r@0ITFUe?-;+mi@_)`AWP1r?71OMGn@Oyj> zJ%)sWxiTC%XVWgDW`fJHebj$K`B|I%51VqMjGkYir@cwdAshPzorOV00C*0lT@cHsimyhZ9eVt77(yri{{Kb^}Tuz}CDSzSlvw1$RmfXOjv@V(RE_Q~R$u^peKOw!}D8!?j z@2L+^e$B=>h8Xc_v{*IK^DD$u;vZe+=OkZcqfcYHjd45?GA)H8kMS2Hg?=>k{9osI zB5Qg6Q0jSwR9Y8vZ-xS1IfY&h!aV0Aq))>iyBu9&)3R(M;+&F4`42Xg<4JAqa)hxZ z){c5wopyuEDP=MgR0V?q{RnT#mJ17-yw~`xH)dQETR6G#TaDiuo^St?)%dOZJ72uI zdF7?2I=y|>W0&jy+b_Rj%XN=kdaBjDTasILZZ)sb&La zDpHQe-tF8!)cS%Am-V?|!~gexHx_iLzFP# z(Aclu&@)t{e=|bA-*CL4x9)-;Z5X4^h@WnlANiS_qg_hJ8is0@((#6Ak%4;tMD0&H z-q264@p8kU=oPA&8C;}SYSPdnRG6q~m=~&1-L)a@wAT?%NmMoT

7owTpDLtrDL$ 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]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

g$GdDa8-}`C+EkL6p|%@zM#=}9`<;-EIq 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 literal 0 HcmV?d00001 diff --git a/assets/vpt_mainlogo.png b/assets/vpt_mainlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..e6b527dab9a4532a54be4baa606f314c769c4b6b GIT binary patch literal 23373 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 literal 0 HcmV?d00001 diff --git a/pubspec.lock b/pubspec.lock index f5ff1fb9..417eb6df 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,133 +5,152 @@ packages: dependency: "direct main" description: name: amplify_auth_cognito - url: "https://pub.dartlang.org" + sha256: "3427ef57d806d1a9f11f061dbc64e038a14263a0367fd8150b1d6b4f28ed5b3b" + url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" amplify_auth_cognito_android: dependency: transitive description: name: amplify_auth_cognito_android - url: "https://pub.dartlang.org" + sha256: "81d494f1b41f7bbd7abc923040870f30aeb645731edc122b6d5647e8d510e132" + url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" amplify_auth_cognito_ios: dependency: transitive description: name: amplify_auth_cognito_ios - url: "https://pub.dartlang.org" + sha256: "33f00126998f744e2d5981560821de363dce0c2671a2f15b6f1eb308f0776f38" + url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" amplify_core: dependency: transitive description: name: amplify_core - url: "https://pub.dartlang.org" + sha256: c6ba039ddfce18b0738379bff1cc5b488c7c27102a43046d5e533d88ce9dedfc + url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" amplify_datastore: dependency: "direct main" description: name: amplify_datastore - url: "https://pub.dartlang.org" + sha256: "13b12852830b96d7a318c28c055ff35fed050c9e23e54e217031a8928500cfdb" + url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" amplify_datastore_plugin_interface: dependency: transitive description: name: amplify_datastore_plugin_interface - url: "https://pub.dartlang.org" + sha256: "52c86859546f6da3ad547b23b4fe5cd38ea3584aefddeab301c5475eb6d1bcf2" + url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" amplify_flutter: dependency: "direct main" description: name: amplify_flutter - url: "https://pub.dartlang.org" + sha256: "832ead9bbc8ef392c0d27ccea81e2bd977871115d013b155015533bea5f99ca8" + url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" amplify_flutter_android: dependency: transitive description: name: amplify_flutter_android - url: "https://pub.dartlang.org" + sha256: b81daf0b08677b4d2cb47142bace5625a041b3de0b89c9eba246780d39d920b2 + url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" amplify_flutter_ios: dependency: transitive description: name: amplify_flutter_ios - url: "https://pub.dartlang.org" + sha256: "2433b4a2b0902be886f6d204316e7091df5187df33b4494868b565c291d52233" + url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.10.0" aws_common: dependency: transitive description: name: aws_common - url: "https://pub.dartlang.org" + sha256: b5f48304feacc833241d0d812ab68e5fec80d5440c991d5ca27eaaf23d5c8da5 + url: "https://pub.dev" source: hosted version: "0.1.1" bloc: dependency: "direct main" description: name: bloc - url: "https://pub.dartlang.org" + sha256: bd4f8027bfa60d96c8046dec5ce74c463b2c918dce1b0d36593575995344534a + url: "https://pub.dev" source: hosted version: "8.1.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" source: hosted version: "1.2.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" source: hosted version: "3.0.2" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" source: hosted version: "1.0.5" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" flutter: @@ -143,7 +162,8 @@ packages: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" source: hosted version: "2.0.1" flutter_test: @@ -155,70 +175,88 @@ packages: dependency: transitive description: name: http - url: "https://pub.dartlang.org" + sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + url: "https://pub.dev" source: hosted version: "0.13.5" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" source: hosted version: "4.0.2" intl: dependency: transitive description: name: intl - url: "https://pub.dartlang.org" + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" source: hosted version: "0.17.0" + js: + dependency: transitive + description: + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" json_annotation: dependency: transitive description: name: json_annotation - url: "https://pub.dartlang.org" + sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + url: "https://pub.dev" source: hosted version: "4.8.0" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" source: hosted version: "2.0.1" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.13" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" source: hosted version: "1.8.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" source: hosted version: "1.8.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + url: "https://pub.dev" source: hosted version: "2.1.3" sky_engine: @@ -230,72 +268,82 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" stream_transform: dependency: transitive description: name: stream_transform - url: "https://pub.dartlang.org" + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" source: hosted version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.4.16" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" source: hosted version: "1.3.1" uuid: dependency: transitive description: name: uuid - url: "https://pub.dartlang.org" + sha256: "2469694ad079893e3b434a627970c33f2fa5adc46dfe03c9617546969a9a8afc" + url: "https://pub.dev" source: hosted version: "3.0.6" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" sdks: - dart: ">=2.18.5 <3.0.0" + dart: ">=2.18.5 <4.0.0" flutter: ">=2.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index b125cab4..2473da5c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,10 +57,8 @@ flutter: # the material Icons class. # uses-material-design: true #Uncomment this line if we decide to use Material instead of Cupertino - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + assets: + - assets/vpt_mainlogo.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware @@ -68,17 +66,11 @@ flutter: # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic + fonts: + - family: Unbounded + fonts: + - asset: assets/unbounded.ttf + # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf From 795415c85c762bef2f387b1c9a912e193a3a9b8a Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:35:33 -0500 Subject: [PATCH 09/43] draft patient experience buildout --- lib/views/base_view.dart | 17 +- lib/views/components/patient_form.dart | 246 +++++++++++++++++++++++++ lib/views/styles.dart | 5 +- lib/views/view.dart | 22 --- lib/views/views.dart | 1 + 5 files changed, 264 insertions(+), 27 deletions(-) create mode 100644 lib/views/components/patient_form.dart delete mode 100644 lib/views/view.dart diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart index 2be42456..e87aa187 100644 --- a/lib/views/base_view.dart +++ b/lib/views/base_view.dart @@ -1,5 +1,7 @@ import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/components/patient_form.dart'; import 'package:visualpt/views/components/view_background.dart'; +import 'package:visualpt/views/styles.dart'; class BaseView extends StatelessWidget { const BaseView({super.key, required this.child}); @@ -10,16 +12,23 @@ class BaseView extends StatelessWidget { final screenSize = MediaQuery.of(context).size; return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - - ), child: Stack( children: [ const ViewBackground(), + SafeArea( + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * 0.7), + child: const Image( + image: AssetImage(Styles.mainLogoPath), + fit: BoxFit.fitWidth)), + ), SizedBox( height: screenSize.height, width: screenSize.width, - child: Container(child: child)) + child: Container(child: child)), + //TODO Fix this + SafeArea(child: PatientForm()) ], ), ); diff --git a/lib/views/components/patient_form.dart b/lib/views/components/patient_form.dart new file mode 100644 index 00000000..fb7a0b01 --- /dev/null +++ b/lib/views/components/patient_form.dart @@ -0,0 +1,246 @@ +import 'dart:developer'; +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/models/ModelProvider.dart'; +import 'package:visualpt/views/styles.dart'; + +class PatientForm extends StatefulWidget { + const PatientForm({Key? key}) : super(key: key); + + @override + State createState() => _PatientFormState(); +} + +class _PatientFormState extends State { + final _formKey = GlobalKey(); + DateTime date = DateTime.now(); + Patient patient = Patient( + first_name: "TODO", + last_name: "TODO", + dob: TemporalDate(DateTime.now()), + gender: Gender.MALE, + height_in_inches: 0); + @override + Widget build(BuildContext context) { + return CupertinoButton( + color: CupertinoColors.link, + onPressed: showFormDialog, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text("New Patient"), + ], + ), + ); + } + + Future showFormDialog() { + return showCupertinoModalPopup( + useRootNavigator: false, + context: context, + builder: (BuildContext context) => + StatefulBuilder(builder: (context, setState) { + return CupertinoAlertDialog( + title: const Text('Patient Information'), + content: Form( + key: _formKey, + child: SizedBox( + height: 310, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Who is being treated?"), + _formInput("First Name"), + _formInput("Last Name"), + Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + decoration: BoxDecoration( + color: CupertinoColors.systemBackground, + border: Border.all(), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Birth Date"), + CupertinoButton( + onPressed: () => showCupertinoModalPopup( + context: context, + builder: (BuildContext context) => Container( + height: 216, + padding: const EdgeInsets.only(top: 6.0), + margin: EdgeInsets.only( + bottom: MediaQuery.of(context) + .viewInsets + .bottom, + ), + color: CupertinoColors.systemBackground + .resolveFrom(context), + child: SafeArea( + top: false, + child: CupertinoDatePicker( + initialDateTime: date, + mode: CupertinoDatePickerMode.date, + onDateTimeChanged: + (DateTime newDate) { + setState(() => date = newDate); + }, + minimumYear: 1900, + maximumDate: DateTime.now()), + ), + )), + child: + Text('${date.month}/${date.day}/${date.year}'), + ), + ], + ), + ), + ), + GestureDetector( + onTap: () => setState(() => (patient.gender == Gender.MALE) + ? patient = patient.copyWith(gender: Gender.FEMALE) + : patient = patient.copyWith(gender: Gender.MALE)), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: CupertinoColors.systemBackground, + border: Border.all(), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Spacer(flex: 2), + const Text("Gender"), + const Spacer(), + Row( + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.all(1.0), + decoration: patient.gender == Gender.MALE + ? BoxDecoration( + border: Border.all(), + borderRadius: + BorderRadius.circular(8.0), + color: Styles + .backgroundComplementory + .withOpacity(0.5)) + : null, + child: const Text("Male"), + ), + const Text("/ "), + Container( + padding: const EdgeInsets.all(1.0), + decoration: patient.gender != Gender.MALE + ? BoxDecoration( + border: Border.all(), + borderRadius: + BorderRadius.circular(8.0), + color: Styles + .backgroundComplementory + .withOpacity(0.5)) + : null, + child: const Text("Female"), + ) + ], + ), + ], + ), + const Spacer(), + ], + ), + ), + ), + ) + ], + ), + ), + ), + actions: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: CupertinoButton( + color: CupertinoColors.link, + onPressed: () { + //storeDateTimeData(date); + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + Navigator.pop(context); + // TODO + } + }, + child: const Text("Begin Evaluation"), + ), + ), + ], + ); + }), + ); + } + + Widget _formInput(String field) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Container( + height: 63, + decoration: BoxDecoration( + color: CupertinoColors.systemBackground, + border: Border.all(), + borderRadius: BorderRadius.circular(8.0), + ), + child: CupertinoTextFormFieldRow( + prefix: Text(field), + textCapitalization: TextCapitalization.words, + placeholder: 'Enter a ${field.split(' ')[1].toLowerCase()}', + validator: (value) => value == null || value.isEmpty + ? 'Please enter a valid response' + : null, + onSaved: (String? value) { + storeValue(field, value); + }, + ), + ), + ); + } + + void storeValue(String field, String? value) { + try { + switch (field) { + case "First Name": + patient = patient.copyWith(first_name: value!); + break; + case "Last Name": + patient = patient.copyWith(last_name: value!); + break; + case "Height": + patient = patient.copyWith(height_in_inches: double.tryParse(value!)); + break; + default: + throw Exception("Invalid field or value used"); + } + } catch (e) { + log(e.toString()); + } + } +} + +// void storeDateTimeData(DateTime date) { +// patientData.bday = date.toString().split(" ")[0]; +// final now = DateTime.now(); +// final String age = (now.difference(date).inDays / 365).floor().toString(); +// patientData.age = age; +// final nowString = now.toString(); +// final String nowDate = nowString.split(" ")[0]; +// final String nowTime = nowString +// .split(" ")[1] +// .split(".")[0] +// .substring(0, nowString.split(" ")[1].split(".")[0].lastIndexOf(':')); +// patientData.date = nowDate; +// patientData.time = nowTime; +// } diff --git a/lib/views/styles.dart b/lib/views/styles.dart index 68f70231..a92e064c 100644 --- a/lib/views/styles.dart +++ b/lib/views/styles.dart @@ -7,7 +7,10 @@ class Styles { factory Styles() => _instance; Styles._internal(); - ///Background-related styles static const Color backgroundPrimary = Color(0xFF91DAFF); static const Color backgroundAccent = Color(0xFF4EC3FF); + static const Color backgroundComplementory = Color(0xFFf8c098); + + //Asset Paths + static const String mainLogoPath = "assets/vpt_mainlogo.png"; } diff --git a/lib/views/view.dart b/lib/views/view.dart deleted file mode 100644 index 5eca9acd..00000000 --- a/lib/views/view.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:visualpt/views/components/view_background.dart'; - -class View extends StatelessWidget { - const View({super.key, required this.child}); - final Widget child; - - @override - Widget build(BuildContext context) { - final screenSize = MediaQuery.of(context).size; - - return Stack( - children: [ - const ViewBackground(), - SizedBox( - height: screenSize.height, - width: screenSize.width, - child: Container(child: child)) - ], - ); - } -} diff --git a/lib/views/views.dart b/lib/views/views.dart index a8e37c02..0f185f7a 100644 --- a/lib/views/views.dart +++ b/lib/views/views.dart @@ -5,3 +5,4 @@ export 'home_view.dart'; export 'assessment_view.dart'; export 'launch_view.dart'; export 'analysis_view.dart'; +export 'base_view.dart'; From 27c92adad4b392a265b9f8b14c8adeb78a66d1a9 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Tue, 31 Jan 2023 20:41:44 -0500 Subject: [PATCH 10/43] assets --- assets/ctsib_mainlogo.png | Bin 0 -> 5554 bytes assets/gv_mainlogo.png | Bin 0 -> 21055 bytes pubspec.yaml | 2 ++ 3 files changed, 2 insertions(+) create mode 100644 assets/ctsib_mainlogo.png create mode 100644 assets/gv_mainlogo.png diff --git a/assets/ctsib_mainlogo.png b/assets/ctsib_mainlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..2f698d4cc13d1c728547d070b9f16a41b130d4e0 GIT binary patch literal 5554 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 P Date: Wed, 1 Feb 2023 00:42:49 -0500 Subject: [PATCH 11/43] steps closer to prod --- lib/views/auth_view.dart | 44 +++++++++++++++++-- lib/views/base_view.dart | 41 ++++++++++++------ lib/views/components/assessment_card.dart | 52 +++++++++++++++++++++++ lib/views/home_view.dart | 26 ++++++++++-- lib/views/styles.dart | 6 +++ lib/views/views.dart | 3 +- 6 files changed, 152 insertions(+), 20 deletions(-) create mode 100644 lib/views/components/assessment_card.dart diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart index bcffe3e4..7ffb1149 100644 --- a/lib/views/auth_view.dart +++ b/lib/views/auth_view.dart @@ -1,13 +1,51 @@ import 'package:flutter/cupertino.dart'; -import 'package:visualpt/views/view.dart'; +import 'package:visualpt/views/styles.dart'; +import 'base_view.dart'; +import 'components/components.dart'; class AuthView extends StatelessWidget { const AuthView({super.key}); @override Widget build(BuildContext context) { - return const View( - child: Text("TODO Implement"), + return CupertinoPageScaffold( + child: BaseView( + pageTitle: "Save clicks and minutes", + child: ConstrainedBox( + constraints: + BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.8), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Personalize your experience"), + ), + const AuthInput("Username"), + const AuthInput("Password", obscure: true), + Padding( + padding: const EdgeInsets.all(32.0), + child: GestureDetector( + child: Container( + alignment: Alignment.center, + height: MediaQuery.of(context).size.width / 3, + width: MediaQuery.of(context).size.width / 3, + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.actionPrimary), + child: const Text( + "Log In", + style: TextStyle( + color: CupertinoColors.white, + fontSize: 24, + fontWeight: FontWeight.bold), + ), + ), + //TODO Authentication! + onTap: () {}), + ) + ], + ), + ), + ), ); } } diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart index e87aa187..9634ee2f 100644 --- a/lib/views/base_view.dart +++ b/lib/views/base_view.dart @@ -1,11 +1,11 @@ import 'package:flutter/cupertino.dart'; -import 'package:visualpt/views/components/patient_form.dart'; -import 'package:visualpt/views/components/view_background.dart'; +import 'package:visualpt/views/components/components.dart'; import 'package:visualpt/views/styles.dart'; class BaseView extends StatelessWidget { - const BaseView({super.key, required this.child}); + const BaseView({super.key, required this.child, required this.pageTitle}); final Widget child; + final String pageTitle; @override Widget build(BuildContext context) { @@ -16,19 +16,36 @@ class BaseView extends StatelessWidget { children: [ const ViewBackground(), SafeArea( - child: ConstrainedBox( + //TODO If not signed in, this should be in the middle of the page and bigger + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ConstrainedBox( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * 0.7), child: const Image( image: AssetImage(Styles.mainLogoPath), - fit: BoxFit.fitWidth)), - ), - SizedBox( - height: screenSize.height, - width: screenSize.width, - child: Container(child: child)), - //TODO Fix this - SafeArea(child: PatientForm()) + fit: BoxFit.fitWidth), + ), + Padding( + padding: const EdgeInsets.only(left: 21.0), + child: Text( + pageTitle, + style: TextStyle(fontSize: 24), + overflow: TextOverflow.visible, + ), + ) + ], + )), + Positioned( + left: 16, + right: 16, + top: MediaQuery.of(context).size.height / 3, + child: SizedBox( + height: screenSize.height, + width: screenSize.width, + child: Container(child: child)), + ) ], ), ); diff --git a/lib/views/components/assessment_card.dart b/lib/views/components/assessment_card.dart new file mode 100644 index 00000000..2275a18e --- /dev/null +++ b/lib/views/components/assessment_card.dart @@ -0,0 +1,52 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/components/patient_form.dart'; + +class AssessmentCard extends StatelessWidget { + final String imagePath; + final String title; + final String subtitle; + + const AssessmentCard({ + Key? key, + required this.imagePath, + required this.title, + required this.subtitle, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: PatientForm( + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + SizedBox( + height: MediaQuery.of(context).size.height / 4, + width: MediaQuery.of(context).size.height / 4, + child: Container( + decoration: BoxDecoration( + color: CupertinoColors.white, + borderRadius: BorderRadius.circular(10.0), + ), + child: Padding( + padding: const EdgeInsets.all(8), + child: Image(image: AssetImage(imagePath), fit: BoxFit.contain), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, + style: const TextStyle( + fontWeight: FontWeight.bold, fontSize: 32)), + Text(subtitle, style: const TextStyle(fontSize: 24)), + ], + ), + ), + ]), + ), + ); + } +} diff --git a/lib/views/home_view.dart b/lib/views/home_view.dart index e87196f5..86b55480 100644 --- a/lib/views/home_view.dart +++ b/lib/views/home_view.dart @@ -1,11 +1,29 @@ -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/views.dart'; +import 'components/components.dart'; class HomeView extends StatelessWidget { const HomeView({super.key}); @override Widget build(BuildContext context) { - return Container(); + return CupertinoPageScaffold( + child: BaseView( + pageTitle: "", + child: ListView( + scrollDirection: Axis.horizontal, + children: const [ + AssessmentCard( + imagePath: Styles.ctsibLogoPath, + title: "CT-SIB", + subtitle: "3 minutes"), + AssessmentCard( + imagePath: Styles.gvLogoPath, + title: "Gait Velocity", + subtitle: "5 minutes"), + ], + ), + ), + ); } -} \ No newline at end of file +} diff --git a/lib/views/styles.dart b/lib/views/styles.dart index a92e064c..2bec14fc 100644 --- a/lib/views/styles.dart +++ b/lib/views/styles.dart @@ -10,7 +10,13 @@ class Styles { static const Color backgroundPrimary = Color(0xFF91DAFF); static const Color backgroundAccent = Color(0xFF4EC3FF); static const Color backgroundComplementory = Color(0xFFf8c098); + + static const Color actionPrimary = Color(0xFF1F7A97); + + //Asset Paths static const String mainLogoPath = "assets/vpt_mainlogo.png"; + static const String ctsibLogoPath = "assets/ctsib_mainlogo.png"; + static const String gvLogoPath = "assets/gv_mainlogo.png"; } diff --git a/lib/views/views.dart b/lib/views/views.dart index 0f185f7a..269fe01f 100644 --- a/lib/views/views.dart +++ b/lib/views/views.dart @@ -3,6 +3,7 @@ export 'auth_view.dart'; export 'home_view.dart'; export 'assessment_view.dart'; -export 'launch_view.dart'; +export 'landing_view.dart'; export 'analysis_view.dart'; export 'base_view.dart'; +export 'styles.dart'; From 178c7e0c7d0f3b8edf41c2c2593ce57ad438e222 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Thu, 2 Feb 2023 23:54:16 -0500 Subject: [PATCH 12/43] simplifying imports --- lib/views/components/components.dart | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 lib/views/components/components.dart diff --git a/lib/views/components/components.dart b/lib/views/components/components.dart new file mode 100644 index 00000000..e03cd374 --- /dev/null +++ b/lib/views/components/components.dart @@ -0,0 +1,4 @@ +export 'auth_input.dart'; +export 'assessment_card.dart'; +export 'patient_form.dart'; +export 'view_background.dart'; From c13b32c16cbc89266c2a81ef82339bdf7b2bb2bc Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 4 Feb 2023 23:57:57 -0500 Subject: [PATCH 13/43] camera code in interface branch --- ios/Podfile.lock | 169 ++++++- ios/Runner.xcodeproj/project.pbxproj | 4 +- pubspec.lock | 643 ++++++++++++++++++++++++++- pubspec.yaml | 5 +- 4 files changed, 815 insertions(+), 6 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3c6ca7db..5117b1c3 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1 +1,168 @@ -POD INSTALL \ No newline at end of file +PODS: + - Amplify (1.28.3): + - Amplify/Default (= 1.28.3) + - Amplify/Default (1.28.3) + - amplify_auth_cognito_ios (0.0.1): + - Amplify (= 1.28.3) + - amplify_core + - AmplifyPlugins/AWSCognitoAuthPlugin (= 1.28.3) + - Flutter + - ObjectMapper + - amplify_core (0.0.1): + - Flutter + - SwiftFormat/CLI + - SwiftLint (= 0.48.0) + - amplify_datastore (0.0.1): + - Amplify (= 1.28.3) + - amplify_core + - AmplifyPlugins/AWSDataStorePlugin (= 1.28.3) + - Flutter + - amplify_flutter_ios (0.0.1): + - Amplify (= 1.28.3) + - amplify_core + - AmplifyPlugins/AWSCognitoAuthPlugin (= 1.28.3) + - AWSPluginsCore (= 1.28.3) + - Flutter + - SwiftFormat/CLI + - SwiftLint (= 0.48.0) + - AmplifyPlugins/AWSCognitoAuthPlugin (1.28.3): + - AWSAuthCore (~> 2.28.0) + - AWSCognitoIdentityProvider (~> 2.28.0) + - AWSCognitoIdentityProviderASF (~> 2.28.0) + - AWSCore (~> 2.28.0) + - AWSMobileClient (~> 2.28.0) + - AWSPluginsCore (= 1.28.3) + - AmplifyPlugins/AWSDataStorePlugin (1.28.3): + - AWSCore (~> 2.28.0) + - AWSPluginsCore (= 1.28.3) + - SQLite.swift (= 0.13.2) + - AWSAuthCore (2.28.5): + - AWSCore (= 2.28.5) + - AWSCognitoIdentityProvider (2.28.5): + - AWSCognitoIdentityProviderASF (= 2.28.5) + - AWSCore (= 2.28.5) + - AWSCognitoIdentityProviderASF (2.28.5): + - AWSCore (= 2.28.5) + - AWSCore (2.28.5) + - AWSMobileClient (2.28.5): + - AWSAuthCore (= 2.28.5) + - AWSCognitoIdentityProvider (= 2.28.5) + - AWSCognitoIdentityProviderASF (= 2.28.5) + - AWSCore (= 2.28.5) + - AWSPluginsCore (1.28.3): + - Amplify (= 1.28.3) + - AWSCore (~> 2.28.0) + - camera_avfoundation (0.0.1): + - Flutter + - ffmpeg-kit-ios-min-gpl (5.1) + - ffmpeg_kit_flutter_min_gpl (5.1.0): + - ffmpeg_kit_flutter_min_gpl/min-gpl (= 5.1.0) + - Flutter + - ffmpeg_kit_flutter_min_gpl/min-gpl (5.1.0): + - ffmpeg-kit-ios-min-gpl (= 5.1) + - Flutter + - Flutter (1.0.0) + - libwebp (1.2.4): + - libwebp/demux (= 1.2.4) + - libwebp/mux (= 1.2.4) + - libwebp/webp (= 1.2.4) + - libwebp/demux (1.2.4): + - libwebp/webp + - libwebp/mux (1.2.4): + - libwebp/demux + - libwebp/webp (1.2.4) + - ObjectMapper (4.2.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - SQLite.swift (0.13.2): + - SQLite.swift/standard (= 0.13.2) + - SQLite.swift/standard (0.13.2) + - SwiftFormat/CLI (0.50.5) + - SwiftLint (0.48.0) + - video_player_avfoundation (0.0.1): + - Flutter + - video_thumbnail (0.0.1): + - Flutter + - libwebp + +DEPENDENCIES: + - amplify_auth_cognito_ios (from `.symlinks/plugins/amplify_auth_cognito_ios/ios`) + - amplify_core (from `.symlinks/plugins/amplify_core/ios`) + - amplify_datastore (from `.symlinks/plugins/amplify_datastore/ios`) + - amplify_flutter_ios (from `.symlinks/plugins/amplify_flutter_ios/ios`) + - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) + - ffmpeg_kit_flutter_min_gpl (from `.symlinks/plugins/ffmpeg_kit_flutter_min_gpl/ios`) + - Flutter (from `Flutter`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) + - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) + - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`) + +SPEC REPOS: + trunk: + - Amplify + - AmplifyPlugins + - AWSAuthCore + - AWSCognitoIdentityProvider + - AWSCognitoIdentityProviderASF + - AWSCore + - AWSMobileClient + - AWSPluginsCore + - ffmpeg-kit-ios-min-gpl + - libwebp + - ObjectMapper + - SQLite.swift + - SwiftFormat + - SwiftLint + +EXTERNAL SOURCES: + amplify_auth_cognito_ios: + :path: ".symlinks/plugins/amplify_auth_cognito_ios/ios" + amplify_core: + :path: ".symlinks/plugins/amplify_core/ios" + amplify_datastore: + :path: ".symlinks/plugins/amplify_datastore/ios" + amplify_flutter_ios: + :path: ".symlinks/plugins/amplify_flutter_ios/ios" + camera_avfoundation: + :path: ".symlinks/plugins/camera_avfoundation/ios" + ffmpeg_kit_flutter_min_gpl: + :path: ".symlinks/plugins/ffmpeg_kit_flutter_min_gpl/ios" + Flutter: + :path: Flutter + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/ios" + video_player_avfoundation: + :path: ".symlinks/plugins/video_player_avfoundation/ios" + video_thumbnail: + :path: ".symlinks/plugins/video_thumbnail/ios" + +SPEC CHECKSUMS: + Amplify: e89b493fc2fe840ac044bd4ddf71ad9828f8c364 + amplify_auth_cognito_ios: e532d70387aa0fe6f456855afe78ea275d58fe4b + amplify_core: 0876b2fbbcec7619f5b58b20c59ecc0118f65a47 + amplify_datastore: 4aa847727e99901625642db923deb86ee4c33817 + amplify_flutter_ios: 8943b5e5fb995a7aa3eb5c5238a6822756703584 + AmplifyPlugins: 98bcd23f2c13179f75c733b887e80dd06243be5e + AWSAuthCore: e728d73a2b4eed9e826506a1f0fcc58171537267 + AWSCognitoIdentityProvider: 261fac98d461e9e398bf3c1e18c3cd90578b33ca + AWSCognitoIdentityProviderASF: 8e35d6414d9ee2443e9167414ef5e839a58e1e72 + AWSCore: 4bad3f0eb17393b4926a9491e32b835991c56382 + AWSMobileClient: c898658a4d15e64e07d39c7dc6354fd3ea3055a9 + AWSPluginsCore: 93f813579479fa4c5bfff58817df89e0e10f9f3b + camera_avfoundation: 07c77549ea54ad95d8581be86617c094a46280d9 + ffmpeg-kit-ios-min-gpl: ac5eb47f98b27e26d14c009de3ce9181007ce688 + ffmpeg_kit_flutter_min_gpl: f657651b493b7608fad9dda3ad606eb0946c9faa + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef + ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81 + path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 + SQLite.swift: 4fc2be46c36392e3b87afe6fe7f1801c1daa07ef + SwiftFormat: fc64190719f3655079b7b4c246182383bd89026f + SwiftLint: 284cea64b6187c5d6bd83e9a548a64104d546447 + video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff + video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1 + +PODFILE CHECKSUM: c24c27344913105c54b95f33f73882923a0bd103 + +COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 8b760478..03a43bf8 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -255,6 +255,7 @@ }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -269,6 +270,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); diff --git a/pubspec.lock b/pubspec.lock index 866e1674..5d98d49c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1 +1,642 @@ -PUB GET \ No newline at end of file +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + amplify_auth_cognito: + dependency: "direct main" + description: + name: amplify_auth_cognito + sha256: "3427ef57d806d1a9f11f061dbc64e038a14263a0367fd8150b1d6b4f28ed5b3b" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + amplify_auth_cognito_android: + dependency: transitive + description: + name: amplify_auth_cognito_android + sha256: "81d494f1b41f7bbd7abc923040870f30aeb645731edc122b6d5647e8d510e132" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + amplify_auth_cognito_ios: + dependency: transitive + description: + name: amplify_auth_cognito_ios + sha256: "33f00126998f744e2d5981560821de363dce0c2671a2f15b6f1eb308f0776f38" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + amplify_core: + dependency: transitive + description: + name: amplify_core + sha256: c6ba039ddfce18b0738379bff1cc5b488c7c27102a43046d5e533d88ce9dedfc + url: "https://pub.dev" + source: hosted + version: "0.6.12" + amplify_datastore: + dependency: "direct main" + description: + name: amplify_datastore + sha256: "13b12852830b96d7a318c28c055ff35fed050c9e23e54e217031a8928500cfdb" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + amplify_datastore_plugin_interface: + dependency: transitive + description: + name: amplify_datastore_plugin_interface + sha256: "52c86859546f6da3ad547b23b4fe5cd38ea3584aefddeab301c5475eb6d1bcf2" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + amplify_flutter: + dependency: "direct main" + description: + name: amplify_flutter + sha256: "832ead9bbc8ef392c0d27ccea81e2bd977871115d013b155015533bea5f99ca8" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + amplify_flutter_android: + dependency: transitive + description: + name: amplify_flutter_android + sha256: b81daf0b08677b4d2cb47142bace5625a041b3de0b89c9eba246780d39d920b2 + url: "https://pub.dev" + source: hosted + version: "0.6.12" + amplify_flutter_ios: + dependency: transitive + description: + name: amplify_flutter_ios + sha256: "2433b4a2b0902be886f6d204316e7091df5187df33b4494868b565c291d52233" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + async: + dependency: transitive + description: + name: async + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" + source: hosted + version: "2.10.0" + aws_common: + dependency: transitive + description: + name: aws_common + sha256: b5f48304feacc833241d0d812ab68e5fec80d5440c991d5ca27eaaf23d5c8da5 + url: "https://pub.dev" + source: hosted + version: "0.1.1" + bloc: + dependency: transitive + description: + name: bloc + sha256: bd4f8027bfa60d96c8046dec5ce74c463b2c918dce1b0d36593575995344534a + url: "https://pub.dev" + source: hosted + version: "8.1.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + camera: + dependency: "direct main" + description: + name: camera + sha256: e7ac55af24a890d20276821eb3c95857074d03b7de6f9892b99a205ee30bd179 + url: "https://pub.dev" + source: hosted + version: "0.10.3" + camera_android: + dependency: transitive + description: + name: camera_android + sha256: e491c836147f60dd8a54cf3895fd2960e13b21b78a9d15b563a1b6c70894f142 + url: "https://pub.dev" + source: hosted + version: "0.10.4" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "6a68c20593d4cd58974d555f74a48b244f9db28cc9156de57781122d11b8754b" + url: "https://pub.dev" + source: hosted + version: "0.9.11" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: "0eedd642d905ca24f1c483fe9ea0d0e7287b86a402845c28d24df28cc7b0ee6e" + url: "https://pub.dev" + source: hosted + version: "2.3.4" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: "496de93c5d462738ce991dbfe91fb19026f115ed36406700a20a380fb0018299" + url: "https://pub.dev" + source: hosted + version: "0.3.1+1" + characters: + dependency: transitive + description: + name: characters + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" + source: hosted + version: "1.2.1" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" + source: hosted + version: "1.17.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" + url: "https://pub.dev" + source: hosted + version: "0.3.3+4" + crypto: + dependency: transitive + description: + name: crypto + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" + source: hosted + version: "3.0.2" + csslib: + dependency: transitive + description: + name: csslib + sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745 + url: "https://pub.dev" + source: hosted + version: "0.17.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" + source: hosted + version: "1.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + ffmpeg_kit_flutter_min_gpl: + dependency: transitive + description: + name: ffmpeg_kit_flutter_min_gpl + sha256: ca3dc2330c176c8ef96fb512e7a1a37ab369e15685d2e72a52ec1d50726360a0 + url: "https://pub.dev" + source: hosted + version: "5.1.0" + ffmpeg_kit_flutter_platform_interface: + dependency: transitive + description: + name: ffmpeg_kit_flutter_platform_interface + sha256: addf046ae44e190ad0101b2fde2ad909a3cd08a2a109f6106d2f7048b7abedee + url: "https://pub.dev" + source: hosted + version: "0.2.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: "890c51c8007f0182360e523518a0c732efb89876cb4669307af7efada5b55557" + url: "https://pub.dev" + source: hosted + version: "8.1.1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" + source: hosted + version: "2.0.1" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + url: "https://pub.dev" + source: hosted + version: "2.0.7" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + helpers: + dependency: "direct main" + description: + name: helpers + sha256: "7b2354fe7a3be05635766426a298ba9272dd7e1ceb8036d9c7ecb63ede300b9c" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + html: + dependency: transitive + description: + name: html + sha256: d9793e10dbe0e6c364f4c59bf3e01fb33a9b2a674bc7a1081693dba0614b6269 + url: "https://pub.dev" + source: hosted + version: "0.15.1" + http: + dependency: transitive + description: + name: http + sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + url: "https://pub.dev" + source: hosted + version: "0.13.5" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + intl: + dependency: transitive + description: + name: intl + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" + source: hosted + version: "0.17.0" + js: + dependency: transitive + description: + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + url: "https://pub.dev" + source: hosted + version: "4.8.0" + lints: + dependency: transitive + description: + name: lints + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" + source: hosted + version: "0.12.13" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + path: + dependency: transitive + description: + name: path + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" + source: hosted + version: "1.8.2" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + url: "https://pub.dev" + source: hosted + version: "2.0.12" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + url: "https://pub.dev" + source: hosted + version: "2.0.22" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + url: "https://pub.dev" + source: hosted + version: "2.1.7" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c + url: "https://pub.dev" + source: hosted + version: "2.1.3" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + url: "https://pub.dev" + source: hosted + version: "2.1.3" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" + quiver: + dependency: transitive + description: + name: quiver + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + url: "https://pub.dev" + source: hosted + version: "3.2.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" + source: hosted + version: "1.9.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" + source: hosted + version: "0.4.16" + transparent_image: + dependency: transitive + description: + name: transparent_image + sha256: e566a616922a781489f4d91cc939b1b3203b6e4a093317805f2f82f0bb0f8dec + url: "https://pub.dev" + source: hosted + version: "2.0.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + uuid: + dependency: transitive + description: + name: uuid + sha256: "2469694ad079893e3b434a627970c33f2fa5adc46dfe03c9617546969a9a8afc" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + video_editor: + dependency: "direct main" + description: + name: video_editor + sha256: "7a86c9d154f1355207c88ca19417b2adf6a9cdd142c576ffb4be6d191ec70653" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + video_player: + dependency: "direct main" + description: + name: video_player + sha256: "59f7f31c919c59cbedd37c617317045f5f650dc0eeb568b0b0de9a36472bdb28" + url: "https://pub.dev" + source: hosted + version: "2.5.1" + video_player_android: + dependency: transitive + description: + name: video_player_android + sha256: "984388511230bac63feb53b2911a70e829fe0976b6b2213f5c579c4e0a882db3" + url: "https://pub.dev" + source: hosted + version: "2.3.10" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + sha256: d9f7a46d6a77680adb03ec05a381025d6e890ebe636637c6c3014cc3926b97e9 + url: "https://pub.dev" + source: hosted + version: "2.3.8" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + sha256: "42bb75de5e9b79e1f20f1d95f688fac0f95beac4d89c6eb2cd421724d4432dae" + url: "https://pub.dev" + source: hosted + version: "6.0.1" + video_player_web: + dependency: transitive + description: + name: video_player_web + sha256: b649b07b8f8f553bee4a97a0a53d0fe78a70b115eafaf0105b612b32b05ddb99 + url: "https://pub.dev" + source: hosted + version: "2.0.13" + video_thumbnail: + dependency: transitive + description: + name: video_thumbnail + sha256: "3455c189d3f0bb4e3fc2236475aa84fe598b9b2d0e08f43b9761f5bc44210016" + url: "https://pub.dev" + source: hosted + version: "0.5.3" + win32: + dependency: transitive + description: + name: win32 + sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + url: "https://pub.dev" + source: hosted + version: "3.1.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + url: "https://pub.dev" + source: hosted + version: "0.2.0+3" +sdks: + dart: ">=2.18.5 <4.0.0" + flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 4638800a..70f2e3cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,14 +28,13 @@ dependencies: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.5 camera: ^0.10.1 path_provider: ^2.0.11 video_player: ^2.4.10 video_editor: ^2.2.0 helpers: ^1.2.0 - bloc: ^8.1.0 + flutter_bloc: ^8.1.0 amplify_flutter: ^0.6.0 amplify_datastore: ^0.6.0 amplify_auth_cognito: ^0.6.0 From 52015760a84a9ef9fa17c18c6041edd9352ea610 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sun, 5 Feb 2023 15:29:12 -0500 Subject: [PATCH 14/43] data model updated --- amplify/backend/api/visualpt/cli-inputs.json | 5 +- amplify/backend/api/visualpt/schema.graphql | 48 +- lib/models/Assessment.dart | 47 +- lib/models/ModelProvider.dart | 8 +- lib/models/Patient.dart | 55 +- lib/models/SkeletalSequence.dart | 543 +++++++++++++++++++ 6 files changed, 654 insertions(+), 52 deletions(-) create mode 100644 lib/models/SkeletalSequence.dart diff --git a/amplify/backend/api/visualpt/cli-inputs.json b/amplify/backend/api/visualpt/cli-inputs.json index 6d795836..cbbf8376 100644 --- a/amplify/backend/api/visualpt/cli-inputs.json +++ b/amplify/backend/api/visualpt/cli-inputs.json @@ -4,9 +4,8 @@ "apiName": "visualpt", "serviceName": "AppSync", "defaultAuthType": { - "mode": "API_KEY", - "keyDescription": "api key description", - "expirationTime": 30 + "mode": "AMAZON_COGNITO_USER_POOLS", + "cognitoUserPoolId": "authvisualpt" }, "additionalAuthTypes": [ { diff --git a/amplify/backend/api/visualpt/schema.graphql b/amplify/backend/api/visualpt/schema.graphql index 5a600bab..60435126 100644 --- a/amplify/backend/api/visualpt/schema.graphql +++ b/amplify/backend/api/visualpt/schema.graphql @@ -1,22 +1,46 @@ -type Assessment @model @auth(rules: [{allow: public}]) { +type SkeletalSequence @model @auth(rules: [{allow: public}]) { + id: ID! + frame_num: Int! + nose: String + left_eye: String + right_eye: String + left_ear: String + right_ear: String + left_shoulder: String + right_shoulder: String + left_elbow: String + right_elbow: String + left_wrist: String + right_wrist: String + left_hip: String + right_hip: String + left_knee: String + right_knee: String + left_ankle: String + right_ankle: String + assessmentID: ID! @index(name: "byAssessment") +} + +type Patient @model @auth(rules: [{allow: owner}]) { + id: ID! + name: String! + family_name: String! + dob: AWSDate! + gender: Gender! + height_in_inches: Float! + Assessments: [Assessment] @hasMany(indexName: "byPatient", fields: ["id"]) +} + +type Assessment @model @auth(rules: [{allow: owner}]) { id: ID! name: String! timestamp: AWSTimestamp! pdf_base64: String! patientID: ID! @index(name: "byPatient") + SkeletalSequences: [SkeletalSequence] @hasMany(indexName: "byAssessment", fields: ["id"]) } enum Gender { MALE FEMALE -} - -type Patient @model @auth(rules: [{allow: public}]) { - id: ID! - first_name: String! - last_name: String! - dob: AWSDate! - gender: Gender! - height_in_inches: Float! - Assessments: [Assessment] @hasMany(indexName: "byPatient", fields: ["id"]) -} +} \ No newline at end of file diff --git a/lib/models/Assessment.dart b/lib/models/Assessment.dart index f6a27919..15b32899 100644 --- a/lib/models/Assessment.dart +++ b/lib/models/Assessment.dart @@ -19,7 +19,9 @@ // ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously +import 'ModelProvider.dart'; import 'package:amplify_core/amplify_core.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; @@ -32,6 +34,7 @@ class Assessment extends Model { final TemporalTimestamp? _timestamp; final String? _pdf_base64; final String? _patientID; + final List? _SkeletalSequences; final TemporalDateTime? _createdAt; final TemporalDateTime? _updatedAt; @@ -100,6 +103,10 @@ class Assessment extends Model { } } + List? get SkeletalSequences { + return _SkeletalSequences; + } + TemporalDateTime? get createdAt { return _createdAt; } @@ -108,15 +115,16 @@ class Assessment extends Model { return _updatedAt; } - const Assessment._internal({required this.id, required name, required timestamp, required pdf_base64, required patientID, createdAt, updatedAt}): _name = name, _timestamp = timestamp, _pdf_base64 = pdf_base64, _patientID = patientID, _createdAt = createdAt, _updatedAt = updatedAt; + const Assessment._internal({required this.id, required name, required timestamp, required pdf_base64, required patientID, SkeletalSequences, createdAt, updatedAt}): _name = name, _timestamp = timestamp, _pdf_base64 = pdf_base64, _patientID = patientID, _SkeletalSequences = SkeletalSequences, _createdAt = createdAt, _updatedAt = updatedAt; - factory Assessment({String? id, required String name, required TemporalTimestamp timestamp, required String pdf_base64, required String patientID}) { + factory Assessment({String? id, required String name, required TemporalTimestamp timestamp, required String pdf_base64, required String patientID, List? SkeletalSequences}) { return Assessment._internal( id: id == null ? UUID.getUUID() : id, name: name, timestamp: timestamp, pdf_base64: pdf_base64, - patientID: patientID); + patientID: patientID, + SkeletalSequences: SkeletalSequences != null ? List.unmodifiable(SkeletalSequences) : SkeletalSequences); } bool equals(Object other) { @@ -131,7 +139,8 @@ class Assessment extends Model { _name == other._name && _timestamp == other._timestamp && _pdf_base64 == other._pdf_base64 && - _patientID == other._patientID; + _patientID == other._patientID && + DeepCollectionEquality().equals(_SkeletalSequences, other._SkeletalSequences); } @override @@ -154,13 +163,14 @@ class Assessment extends Model { return buffer.toString(); } - Assessment copyWith({String? name, TemporalTimestamp? timestamp, String? pdf_base64, String? patientID}) { + Assessment copyWith({String? name, TemporalTimestamp? timestamp, String? pdf_base64, String? patientID, List? SkeletalSequences}) { return Assessment._internal( id: id, name: name ?? this.name, timestamp: timestamp ?? this.timestamp, pdf_base64: pdf_base64 ?? this.pdf_base64, - patientID: patientID ?? this.patientID); + patientID: patientID ?? this.patientID, + SkeletalSequences: SkeletalSequences ?? this.SkeletalSequences); } Assessment.fromJson(Map json) @@ -169,15 +179,21 @@ class Assessment extends Model { _timestamp = json['timestamp'] != null ? TemporalTimestamp.fromSeconds(json['timestamp']) : null, _pdf_base64 = json['pdf_base64'], _patientID = json['patientID'], + _SkeletalSequences = json['SkeletalSequences'] is List + ? (json['SkeletalSequences'] as List) + .where((e) => e?['serializedData'] != null) + .map((e) => SkeletalSequence.fromJson(new Map.from(e['serializedData']))) + .toList() + : null, _createdAt = json['createdAt'] != null ? TemporalDateTime.fromString(json['createdAt']) : null, _updatedAt = json['updatedAt'] != null ? TemporalDateTime.fromString(json['updatedAt']) : null; Map toJson() => { - 'id': id, 'name': _name, 'timestamp': _timestamp?.toSeconds(), 'pdf_base64': _pdf_base64, 'patientID': _patientID, 'createdAt': _createdAt?.format(), 'updatedAt': _updatedAt?.format() + 'id': id, 'name': _name, 'timestamp': _timestamp?.toSeconds(), 'pdf_base64': _pdf_base64, 'patientID': _patientID, 'SkeletalSequences': _SkeletalSequences?.map((SkeletalSequence? e) => e?.toJson()).toList(), 'createdAt': _createdAt?.format(), 'updatedAt': _updatedAt?.format() }; Map toMap() => { - 'id': id, 'name': _name, 'timestamp': _timestamp, 'pdf_base64': _pdf_base64, 'patientID': _patientID, 'createdAt': _createdAt, 'updatedAt': _updatedAt + 'id': id, 'name': _name, 'timestamp': _timestamp, 'pdf_base64': _pdf_base64, 'patientID': _patientID, 'SkeletalSequences': _SkeletalSequences, 'createdAt': _createdAt, 'updatedAt': _updatedAt }; static final QueryModelIdentifier MODEL_IDENTIFIER = QueryModelIdentifier(); @@ -186,13 +202,19 @@ class Assessment extends Model { static final QueryField TIMESTAMP = QueryField(fieldName: "timestamp"); static final QueryField PDF_BASE64 = QueryField(fieldName: "pdf_base64"); static final QueryField PATIENTID = QueryField(fieldName: "patientID"); + static final QueryField SKELETALSEQUENCES = QueryField( + fieldName: "SkeletalSequences", + fieldType: ModelFieldType(ModelFieldTypeEnum.model, ofModelName: 'SkeletalSequence')); static var schema = Model.defineSchema(define: (ModelSchemaDefinition modelSchemaDefinition) { modelSchemaDefinition.name = "Assessment"; modelSchemaDefinition.pluralName = "Assessments"; modelSchemaDefinition.authRules = [ AuthRule( - authStrategy: AuthStrategy.PUBLIC, + authStrategy: AuthStrategy.OWNER, + ownerField: "owner", + identityClaim: "cognito:username", + provider: AuthRuleProvider.USERPOOLS, operations: [ ModelOperation.CREATE, ModelOperation.UPDATE, @@ -231,6 +253,13 @@ class Assessment extends Model { ofType: ModelFieldType(ModelFieldTypeEnum.string) )); + modelSchemaDefinition.addField(ModelFieldDefinition.hasMany( + key: Assessment.SKELETALSEQUENCES, + isRequired: false, + ofModelName: 'SkeletalSequence', + associatedKey: SkeletalSequence.ASSESSMENTID + )); + modelSchemaDefinition.addField(ModelFieldDefinition.nonQueryField( fieldName: 'createdAt', isRequired: false, diff --git a/lib/models/ModelProvider.dart b/lib/models/ModelProvider.dart index 78f7f65d..07677285 100644 --- a/lib/models/ModelProvider.dart +++ b/lib/models/ModelProvider.dart @@ -22,16 +22,18 @@ import 'package:amplify_core/amplify_core.dart'; import 'Assessment.dart'; import 'Patient.dart'; +import 'SkeletalSequence.dart'; export 'Assessment.dart'; export 'Gender.dart'; export 'Patient.dart'; +export 'SkeletalSequence.dart'; class ModelProvider implements ModelProviderInterface { @override - String version = "25e072c1c8a2f446d05486cb116b9a84"; + String version = "eb93377287d2ee82230970a11ad1ab6f"; @override - List modelSchemas = [Assessment.schema, Patient.schema]; + List modelSchemas = [Assessment.schema, Patient.schema, SkeletalSequence.schema]; static final ModelProvider _instance = ModelProvider(); @override List customTypeSchemas = []; @@ -44,6 +46,8 @@ class ModelProvider implements ModelProviderInterface { return Assessment.classType; case "Patient": return Patient.classType; + case "SkeletalSequence": + return SkeletalSequence.classType; default: throw Exception("Failed to find model in model provider for model name: " + modelName); } diff --git a/lib/models/Patient.dart b/lib/models/Patient.dart index 39dc1344..2b071018 100644 --- a/lib/models/Patient.dart +++ b/lib/models/Patient.dart @@ -30,8 +30,8 @@ import 'package:flutter/foundation.dart'; class Patient extends Model { static const classType = const _PatientModelType(); final String id; - final String? _first_name; - final String? _last_name; + final String? _name; + final String? _family_name; final TemporalDate? _dob; final Gender? _gender; final double? _height_in_inches; @@ -52,9 +52,9 @@ class Patient extends Model { ); } - String get first_name { + String get name { try { - return _first_name!; + return _name!; } catch(e) { throw new AmplifyCodeGenModelException( AmplifyExceptionMessages.codeGenRequiredFieldForceCastExceptionMessage, @@ -65,9 +65,9 @@ class Patient extends Model { } } - String get last_name { + String get family_name { try { - return _last_name!; + return _family_name!; } catch(e) { throw new AmplifyCodeGenModelException( AmplifyExceptionMessages.codeGenRequiredFieldForceCastExceptionMessage, @@ -129,13 +129,13 @@ class Patient extends Model { return _updatedAt; } - const Patient._internal({required this.id, required first_name, required last_name, required dob, required gender, required height_in_inches, Assessments, createdAt, updatedAt}): _first_name = first_name, _last_name = last_name, _dob = dob, _gender = gender, _height_in_inches = height_in_inches, _Assessments = Assessments, _createdAt = createdAt, _updatedAt = updatedAt; + const Patient._internal({required this.id, required name, required family_name, required dob, required gender, required height_in_inches, Assessments, createdAt, updatedAt}): _name = name, _family_name = family_name, _dob = dob, _gender = gender, _height_in_inches = height_in_inches, _Assessments = Assessments, _createdAt = createdAt, _updatedAt = updatedAt; - factory Patient({String? id, required String first_name, required String last_name, required TemporalDate dob, required Gender gender, required double height_in_inches, List? Assessments}) { + factory Patient({String? id, required String name, required String family_name, required TemporalDate dob, required Gender gender, required double height_in_inches, List? Assessments}) { return Patient._internal( id: id == null ? UUID.getUUID() : id, - first_name: first_name, - last_name: last_name, + name: name, + family_name: family_name, dob: dob, gender: gender, height_in_inches: height_in_inches, @@ -151,8 +151,8 @@ class Patient extends Model { if (identical(other, this)) return true; return other is Patient && id == other.id && - _first_name == other._first_name && - _last_name == other._last_name && + _name == other._name && + _family_name == other._family_name && _dob == other._dob && _gender == other._gender && _height_in_inches == other._height_in_inches && @@ -168,8 +168,8 @@ class Patient extends Model { buffer.write("Patient {"); buffer.write("id=" + "$id" + ", "); - buffer.write("first_name=" + "$_first_name" + ", "); - buffer.write("last_name=" + "$_last_name" + ", "); + buffer.write("name=" + "$_name" + ", "); + buffer.write("family_name=" + "$_family_name" + ", "); buffer.write("dob=" + (_dob != null ? _dob!.format() : "null") + ", "); buffer.write("gender=" + (_gender != null ? enumToString(_gender)! : "null") + ", "); buffer.write("height_in_inches=" + (_height_in_inches != null ? _height_in_inches!.toString() : "null") + ", "); @@ -180,11 +180,11 @@ class Patient extends Model { return buffer.toString(); } - Patient copyWith({String? first_name, String? last_name, TemporalDate? dob, Gender? gender, double? height_in_inches, List? Assessments}) { + Patient copyWith({String? name, String? family_name, TemporalDate? dob, Gender? gender, double? height_in_inches, List? Assessments}) { return Patient._internal( id: id, - first_name: first_name ?? this.first_name, - last_name: last_name ?? this.last_name, + name: name ?? this.name, + family_name: family_name ?? this.family_name, dob: dob ?? this.dob, gender: gender ?? this.gender, height_in_inches: height_in_inches ?? this.height_in_inches, @@ -193,8 +193,8 @@ class Patient extends Model { Patient.fromJson(Map json) : id = json['id'], - _first_name = json['first_name'], - _last_name = json['last_name'], + _name = json['name'], + _family_name = json['family_name'], _dob = json['dob'] != null ? TemporalDate.fromString(json['dob']) : null, _gender = enumFromString(json['gender'], Gender.values), _height_in_inches = (json['height_in_inches'] as num?)?.toDouble(), @@ -208,17 +208,17 @@ class Patient extends Model { _updatedAt = json['updatedAt'] != null ? TemporalDateTime.fromString(json['updatedAt']) : null; Map toJson() => { - 'id': id, 'first_name': _first_name, 'last_name': _last_name, 'dob': _dob?.format(), 'gender': enumToString(_gender), 'height_in_inches': _height_in_inches, 'Assessments': _Assessments?.map((Assessment? e) => e?.toJson()).toList(), 'createdAt': _createdAt?.format(), 'updatedAt': _updatedAt?.format() + 'id': id, 'name': _name, 'family_name': _family_name, 'dob': _dob?.format(), 'gender': enumToString(_gender), 'height_in_inches': _height_in_inches, 'Assessments': _Assessments?.map((Assessment? e) => e?.toJson()).toList(), 'createdAt': _createdAt?.format(), 'updatedAt': _updatedAt?.format() }; Map toMap() => { - 'id': id, 'first_name': _first_name, 'last_name': _last_name, 'dob': _dob, 'gender': _gender, 'height_in_inches': _height_in_inches, 'Assessments': _Assessments, 'createdAt': _createdAt, 'updatedAt': _updatedAt + 'id': id, 'name': _name, 'family_name': _family_name, 'dob': _dob, 'gender': _gender, 'height_in_inches': _height_in_inches, 'Assessments': _Assessments, 'createdAt': _createdAt, 'updatedAt': _updatedAt }; static final QueryModelIdentifier MODEL_IDENTIFIER = QueryModelIdentifier(); static final QueryField ID = QueryField(fieldName: "id"); - static final QueryField FIRST_NAME = QueryField(fieldName: "first_name"); - static final QueryField LAST_NAME = QueryField(fieldName: "last_name"); + static final QueryField NAME = QueryField(fieldName: "name"); + static final QueryField FAMILY_NAME = QueryField(fieldName: "family_name"); static final QueryField DOB = QueryField(fieldName: "dob"); static final QueryField GENDER = QueryField(fieldName: "gender"); static final QueryField HEIGHT_IN_INCHES = QueryField(fieldName: "height_in_inches"); @@ -231,7 +231,10 @@ class Patient extends Model { modelSchemaDefinition.authRules = [ AuthRule( - authStrategy: AuthStrategy.PUBLIC, + authStrategy: AuthStrategy.OWNER, + ownerField: "owner", + identityClaim: "cognito:username", + provider: AuthRuleProvider.USERPOOLS, operations: [ ModelOperation.CREATE, ModelOperation.UPDATE, @@ -243,13 +246,13 @@ class Patient extends Model { modelSchemaDefinition.addField(ModelFieldDefinition.id()); modelSchemaDefinition.addField(ModelFieldDefinition.field( - key: Patient.FIRST_NAME, + key: Patient.NAME, isRequired: true, ofType: ModelFieldType(ModelFieldTypeEnum.string) )); modelSchemaDefinition.addField(ModelFieldDefinition.field( - key: Patient.LAST_NAME, + key: Patient.FAMILY_NAME, isRequired: true, ofType: ModelFieldType(ModelFieldTypeEnum.string) )); diff --git a/lib/models/SkeletalSequence.dart b/lib/models/SkeletalSequence.dart new file mode 100644 index 00000000..a6236082 --- /dev/null +++ b/lib/models/SkeletalSequence.dart @@ -0,0 +1,543 @@ +/* +* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// NOTE: This file is generated and may not follow lint rules defined in your app +// Generated files can be excluded from analysis in analysis_options.yaml +// For more info, see: https://dart.dev/guides/language/analysis-options#excluding-code-from-analysis + +// ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously + +import 'package:amplify_core/amplify_core.dart'; +import 'package:flutter/foundation.dart'; + + +/** This is an auto generated class representing the SkeletalSequence type in your schema. */ +@immutable +class SkeletalSequence extends Model { + static const classType = const _SkeletalSequenceModelType(); + final String id; + final int? _frame_num; + final String? _nose; + final String? _left_eye; + final String? _right_eye; + final String? _left_ear; + final String? _right_ear; + final String? _left_shoulder; + final String? _right_shoulder; + final String? _left_elbow; + final String? _right_elbow; + final String? _left_wrist; + final String? _right_wrist; + final String? _left_hip; + final String? _right_hip; + final String? _left_knee; + final String? _right_knee; + final String? _left_ankle; + final String? _right_ankle; + final String? _assessmentID; + final TemporalDateTime? _createdAt; + final TemporalDateTime? _updatedAt; + + @override + getInstanceType() => classType; + + @Deprecated('[getId] is being deprecated in favor of custom primary key feature. Use getter [modelIdentifier] to get model identifier.') + @override + String getId() => id; + + SkeletalSequenceModelIdentifier get modelIdentifier { + return SkeletalSequenceModelIdentifier( + id: id + ); + } + + int get frame_num { + try { + return _frame_num!; + } catch(e) { + throw new AmplifyCodeGenModelException( + AmplifyExceptionMessages.codeGenRequiredFieldForceCastExceptionMessage, + recoverySuggestion: + AmplifyExceptionMessages.codeGenRequiredFieldForceCastRecoverySuggestion, + underlyingException: e.toString() + ); + } + } + + String? get nose { + return _nose; + } + + String? get left_eye { + return _left_eye; + } + + String? get right_eye { + return _right_eye; + } + + String? get left_ear { + return _left_ear; + } + + String? get right_ear { + return _right_ear; + } + + String? get left_shoulder { + return _left_shoulder; + } + + String? get right_shoulder { + return _right_shoulder; + } + + String? get left_elbow { + return _left_elbow; + } + + String? get right_elbow { + return _right_elbow; + } + + String? get left_wrist { + return _left_wrist; + } + + String? get right_wrist { + return _right_wrist; + } + + String? get left_hip { + return _left_hip; + } + + String? get right_hip { + return _right_hip; + } + + String? get left_knee { + return _left_knee; + } + + String? get right_knee { + return _right_knee; + } + + String? get left_ankle { + return _left_ankle; + } + + String? get right_ankle { + return _right_ankle; + } + + String get assessmentID { + try { + return _assessmentID!; + } catch(e) { + throw new AmplifyCodeGenModelException( + AmplifyExceptionMessages.codeGenRequiredFieldForceCastExceptionMessage, + recoverySuggestion: + AmplifyExceptionMessages.codeGenRequiredFieldForceCastRecoverySuggestion, + underlyingException: e.toString() + ); + } + } + + TemporalDateTime? get createdAt { + return _createdAt; + } + + TemporalDateTime? get updatedAt { + return _updatedAt; + } + + const SkeletalSequence._internal({required this.id, required frame_num, nose, left_eye, right_eye, left_ear, right_ear, left_shoulder, right_shoulder, left_elbow, right_elbow, left_wrist, right_wrist, left_hip, right_hip, left_knee, right_knee, left_ankle, right_ankle, required assessmentID, createdAt, updatedAt}): _frame_num = frame_num, _nose = nose, _left_eye = left_eye, _right_eye = right_eye, _left_ear = left_ear, _right_ear = right_ear, _left_shoulder = left_shoulder, _right_shoulder = right_shoulder, _left_elbow = left_elbow, _right_elbow = right_elbow, _left_wrist = left_wrist, _right_wrist = right_wrist, _left_hip = left_hip, _right_hip = right_hip, _left_knee = left_knee, _right_knee = right_knee, _left_ankle = left_ankle, _right_ankle = right_ankle, _assessmentID = assessmentID, _createdAt = createdAt, _updatedAt = updatedAt; + + factory SkeletalSequence({String? id, required int frame_num, String? nose, String? left_eye, String? right_eye, String? left_ear, String? right_ear, String? left_shoulder, String? right_shoulder, String? left_elbow, String? right_elbow, String? left_wrist, String? right_wrist, String? left_hip, String? right_hip, String? left_knee, String? right_knee, String? left_ankle, String? right_ankle, required String assessmentID}) { + return SkeletalSequence._internal( + id: id == null ? UUID.getUUID() : id, + frame_num: frame_num, + nose: nose, + left_eye: left_eye, + right_eye: right_eye, + left_ear: left_ear, + right_ear: right_ear, + left_shoulder: left_shoulder, + right_shoulder: right_shoulder, + left_elbow: left_elbow, + right_elbow: right_elbow, + left_wrist: left_wrist, + right_wrist: right_wrist, + left_hip: left_hip, + right_hip: right_hip, + left_knee: left_knee, + right_knee: right_knee, + left_ankle: left_ankle, + right_ankle: right_ankle, + assessmentID: assessmentID); + } + + bool equals(Object other) { + return this == other; + } + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is SkeletalSequence && + id == other.id && + _frame_num == other._frame_num && + _nose == other._nose && + _left_eye == other._left_eye && + _right_eye == other._right_eye && + _left_ear == other._left_ear && + _right_ear == other._right_ear && + _left_shoulder == other._left_shoulder && + _right_shoulder == other._right_shoulder && + _left_elbow == other._left_elbow && + _right_elbow == other._right_elbow && + _left_wrist == other._left_wrist && + _right_wrist == other._right_wrist && + _left_hip == other._left_hip && + _right_hip == other._right_hip && + _left_knee == other._left_knee && + _right_knee == other._right_knee && + _left_ankle == other._left_ankle && + _right_ankle == other._right_ankle && + _assessmentID == other._assessmentID; + } + + @override + int get hashCode => toString().hashCode; + + @override + String toString() { + var buffer = new StringBuffer(); + + buffer.write("SkeletalSequence {"); + buffer.write("id=" + "$id" + ", "); + buffer.write("frame_num=" + (_frame_num != null ? _frame_num!.toString() : "null") + ", "); + buffer.write("nose=" + "$_nose" + ", "); + buffer.write("left_eye=" + "$_left_eye" + ", "); + buffer.write("right_eye=" + "$_right_eye" + ", "); + buffer.write("left_ear=" + "$_left_ear" + ", "); + buffer.write("right_ear=" + "$_right_ear" + ", "); + buffer.write("left_shoulder=" + "$_left_shoulder" + ", "); + buffer.write("right_shoulder=" + "$_right_shoulder" + ", "); + buffer.write("left_elbow=" + "$_left_elbow" + ", "); + buffer.write("right_elbow=" + "$_right_elbow" + ", "); + buffer.write("left_wrist=" + "$_left_wrist" + ", "); + buffer.write("right_wrist=" + "$_right_wrist" + ", "); + buffer.write("left_hip=" + "$_left_hip" + ", "); + buffer.write("right_hip=" + "$_right_hip" + ", "); + buffer.write("left_knee=" + "$_left_knee" + ", "); + buffer.write("right_knee=" + "$_right_knee" + ", "); + buffer.write("left_ankle=" + "$_left_ankle" + ", "); + buffer.write("right_ankle=" + "$_right_ankle" + ", "); + buffer.write("assessmentID=" + "$_assessmentID" + ", "); + buffer.write("createdAt=" + (_createdAt != null ? _createdAt!.format() : "null") + ", "); + buffer.write("updatedAt=" + (_updatedAt != null ? _updatedAt!.format() : "null")); + buffer.write("}"); + + return buffer.toString(); + } + + SkeletalSequence copyWith({int? frame_num, String? nose, String? left_eye, String? right_eye, String? left_ear, String? right_ear, String? left_shoulder, String? right_shoulder, String? left_elbow, String? right_elbow, String? left_wrist, String? right_wrist, String? left_hip, String? right_hip, String? left_knee, String? right_knee, String? left_ankle, String? right_ankle, String? assessmentID}) { + return SkeletalSequence._internal( + id: id, + frame_num: frame_num ?? this.frame_num, + nose: nose ?? this.nose, + left_eye: left_eye ?? this.left_eye, + right_eye: right_eye ?? this.right_eye, + left_ear: left_ear ?? this.left_ear, + right_ear: right_ear ?? this.right_ear, + left_shoulder: left_shoulder ?? this.left_shoulder, + right_shoulder: right_shoulder ?? this.right_shoulder, + left_elbow: left_elbow ?? this.left_elbow, + right_elbow: right_elbow ?? this.right_elbow, + left_wrist: left_wrist ?? this.left_wrist, + right_wrist: right_wrist ?? this.right_wrist, + left_hip: left_hip ?? this.left_hip, + right_hip: right_hip ?? this.right_hip, + left_knee: left_knee ?? this.left_knee, + right_knee: right_knee ?? this.right_knee, + left_ankle: left_ankle ?? this.left_ankle, + right_ankle: right_ankle ?? this.right_ankle, + assessmentID: assessmentID ?? this.assessmentID); + } + + SkeletalSequence.fromJson(Map json) + : id = json['id'], + _frame_num = (json['frame_num'] as num?)?.toInt(), + _nose = json['nose'], + _left_eye = json['left_eye'], + _right_eye = json['right_eye'], + _left_ear = json['left_ear'], + _right_ear = json['right_ear'], + _left_shoulder = json['left_shoulder'], + _right_shoulder = json['right_shoulder'], + _left_elbow = json['left_elbow'], + _right_elbow = json['right_elbow'], + _left_wrist = json['left_wrist'], + _right_wrist = json['right_wrist'], + _left_hip = json['left_hip'], + _right_hip = json['right_hip'], + _left_knee = json['left_knee'], + _right_knee = json['right_knee'], + _left_ankle = json['left_ankle'], + _right_ankle = json['right_ankle'], + _assessmentID = json['assessmentID'], + _createdAt = json['createdAt'] != null ? TemporalDateTime.fromString(json['createdAt']) : null, + _updatedAt = json['updatedAt'] != null ? TemporalDateTime.fromString(json['updatedAt']) : null; + + Map toJson() => { + 'id': id, 'frame_num': _frame_num, 'nose': _nose, 'left_eye': _left_eye, 'right_eye': _right_eye, 'left_ear': _left_ear, 'right_ear': _right_ear, 'left_shoulder': _left_shoulder, 'right_shoulder': _right_shoulder, 'left_elbow': _left_elbow, 'right_elbow': _right_elbow, 'left_wrist': _left_wrist, 'right_wrist': _right_wrist, 'left_hip': _left_hip, 'right_hip': _right_hip, 'left_knee': _left_knee, 'right_knee': _right_knee, 'left_ankle': _left_ankle, 'right_ankle': _right_ankle, 'assessmentID': _assessmentID, 'createdAt': _createdAt?.format(), 'updatedAt': _updatedAt?.format() + }; + + Map toMap() => { + 'id': id, 'frame_num': _frame_num, 'nose': _nose, 'left_eye': _left_eye, 'right_eye': _right_eye, 'left_ear': _left_ear, 'right_ear': _right_ear, 'left_shoulder': _left_shoulder, 'right_shoulder': _right_shoulder, 'left_elbow': _left_elbow, 'right_elbow': _right_elbow, 'left_wrist': _left_wrist, 'right_wrist': _right_wrist, 'left_hip': _left_hip, 'right_hip': _right_hip, 'left_knee': _left_knee, 'right_knee': _right_knee, 'left_ankle': _left_ankle, 'right_ankle': _right_ankle, 'assessmentID': _assessmentID, 'createdAt': _createdAt, 'updatedAt': _updatedAt + }; + + static final QueryModelIdentifier MODEL_IDENTIFIER = QueryModelIdentifier(); + static final QueryField ID = QueryField(fieldName: "id"); + static final QueryField FRAME_NUM = QueryField(fieldName: "frame_num"); + static final QueryField NOSE = QueryField(fieldName: "nose"); + static final QueryField LEFT_EYE = QueryField(fieldName: "left_eye"); + static final QueryField RIGHT_EYE = QueryField(fieldName: "right_eye"); + static final QueryField LEFT_EAR = QueryField(fieldName: "left_ear"); + static final QueryField RIGHT_EAR = QueryField(fieldName: "right_ear"); + static final QueryField LEFT_SHOULDER = QueryField(fieldName: "left_shoulder"); + static final QueryField RIGHT_SHOULDER = QueryField(fieldName: "right_shoulder"); + static final QueryField LEFT_ELBOW = QueryField(fieldName: "left_elbow"); + static final QueryField RIGHT_ELBOW = QueryField(fieldName: "right_elbow"); + static final QueryField LEFT_WRIST = QueryField(fieldName: "left_wrist"); + static final QueryField RIGHT_WRIST = QueryField(fieldName: "right_wrist"); + static final QueryField LEFT_HIP = QueryField(fieldName: "left_hip"); + static final QueryField RIGHT_HIP = QueryField(fieldName: "right_hip"); + static final QueryField LEFT_KNEE = QueryField(fieldName: "left_knee"); + static final QueryField RIGHT_KNEE = QueryField(fieldName: "right_knee"); + static final QueryField LEFT_ANKLE = QueryField(fieldName: "left_ankle"); + static final QueryField RIGHT_ANKLE = QueryField(fieldName: "right_ankle"); + static final QueryField ASSESSMENTID = QueryField(fieldName: "assessmentID"); + static var schema = Model.defineSchema(define: (ModelSchemaDefinition modelSchemaDefinition) { + modelSchemaDefinition.name = "SkeletalSequence"; + modelSchemaDefinition.pluralName = "SkeletalSequences"; + + modelSchemaDefinition.authRules = [ + AuthRule( + authStrategy: AuthStrategy.PUBLIC, + operations: [ + ModelOperation.CREATE, + ModelOperation.UPDATE, + ModelOperation.DELETE, + ModelOperation.READ + ]) + ]; + + modelSchemaDefinition.indexes = [ + ModelIndex(fields: const ["assessmentID"], name: "byAssessment") + ]; + + modelSchemaDefinition.addField(ModelFieldDefinition.id()); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.FRAME_NUM, + isRequired: true, + ofType: ModelFieldType(ModelFieldTypeEnum.int) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.NOSE, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.LEFT_EYE, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.RIGHT_EYE, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.LEFT_EAR, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.RIGHT_EAR, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.LEFT_SHOULDER, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.RIGHT_SHOULDER, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.LEFT_ELBOW, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.RIGHT_ELBOW, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.LEFT_WRIST, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.RIGHT_WRIST, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.LEFT_HIP, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.RIGHT_HIP, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.LEFT_KNEE, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.RIGHT_KNEE, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.LEFT_ANKLE, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.RIGHT_ANKLE, + isRequired: false, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.field( + key: SkeletalSequence.ASSESSMENTID, + isRequired: true, + ofType: ModelFieldType(ModelFieldTypeEnum.string) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.nonQueryField( + fieldName: 'createdAt', + isRequired: false, + isReadOnly: true, + ofType: ModelFieldType(ModelFieldTypeEnum.dateTime) + )); + + modelSchemaDefinition.addField(ModelFieldDefinition.nonQueryField( + fieldName: 'updatedAt', + isRequired: false, + isReadOnly: true, + ofType: ModelFieldType(ModelFieldTypeEnum.dateTime) + )); + }); +} + +class _SkeletalSequenceModelType extends ModelType { + const _SkeletalSequenceModelType(); + + @override + SkeletalSequence fromJson(Map jsonData) { + return SkeletalSequence.fromJson(jsonData); + } + + @override + String modelName() { + return 'SkeletalSequence'; + } +} + +/** + * This is an auto generated class representing the model identifier + * of [SkeletalSequence] in your schema. + */ +@immutable +class SkeletalSequenceModelIdentifier implements ModelIdentifier { + final String id; + + /** Create an instance of SkeletalSequenceModelIdentifier using [id] the primary key. */ + const SkeletalSequenceModelIdentifier({ + required this.id}); + + @override + Map serializeAsMap() => ({ + 'id': id + }); + + @override + List> serializeAsList() => serializeAsMap() + .entries + .map((entry) => ({ entry.key: entry.value })) + .toList(); + + @override + String serializeAsString() => serializeAsMap().values.join('#'); + + @override + String toString() => 'SkeletalSequenceModelIdentifier(id: $id)'; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + + return other is SkeletalSequenceModelIdentifier && + id == other.id; + } + + @override + int get hashCode => + id.hashCode; +} \ No newline at end of file From d1d65b43250507d6e5ff13dac7df1de12394476b Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sun, 5 Feb 2023 15:54:17 -0500 Subject: [PATCH 15/43] inerface at 80% --- lib/main.dart | 22 +- lib/state/camera_cubit.dart | 211 ------------- lib/views/assessment_view.dart | 8 +- lib/views/auth_view.dart | 7 +- lib/views/base_view.dart | 7 +- lib/views/components/auth_input.dart | 51 ++++ lib/views/components/components.dart | 1 + lib/views/components/patient_form.dart | 284 +++++++++--------- .../components/video_recorder.dart} | 26 +- .../editor_view.dart} | 0 lib/views/landing_view.dart | 19 ++ lib/views/launch_view.dart | 14 - lib/views/styles.dart | 16 +- 13 files changed, 261 insertions(+), 405 deletions(-) delete mode 100644 lib/state/camera_cubit.dart create mode 100644 lib/views/components/auth_input.dart rename lib/{UI/video_page.dart => views/components/video_recorder.dart} (87%) rename lib/{UI/editor_page.dart => views/editor_view.dart} (100%) create mode 100644 lib/views/landing_view.dart delete mode 100644 lib/views/launch_view.dart diff --git a/lib/main.dart b/lib/main.dart index 70ccae22..a34036a0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,3 @@ - import 'package:flutter/cupertino.dart'; import 'package:visualpt/views/views.dart'; @@ -19,12 +18,19 @@ class VisualPT extends StatelessWidget { @override Widget build(BuildContext context) { - return CupertinoApp(routes: { - "/": (context) => const LaunchView(), - "/auth": (context) => const AuthView(), - "/home": (context) => const HomeView(), - "/assessment": (context) => const AssessmentView(), - "/analysis": (context) => const AnalysisView(), - }); + return CupertinoApp( + theme: const CupertinoThemeData( + textTheme: CupertinoTextThemeData( + textStyle: TextStyle( + fontFamily: "Unbounded", color: CupertinoColors.black))), + routes: { + "/": (context) => const AuthView(), + "/auth": (context) => const AuthView(), + "/home": (context) => const HomeView(), + "/assessment": (context) => const AssessmentView(), + "/analysis": (context) => const AnalysisView(), + }, + ); } } + \ No newline at end of file diff --git a/lib/state/camera_cubit.dart b/lib/state/camera_cubit.dart deleted file mode 100644 index 87e9e836..00000000 --- a/lib/state/camera_cubit.dart +++ /dev/null @@ -1,211 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter/services.dart'; -import 'package:camera/camera.dart'; -import 'package:visualpt/UI/editor_page.dart'; - -@immutable -abstract class CameraState {} - -class CameraLoading extends CameraState {} - -class CameraRecording extends CameraState { - final IconData icon = CupertinoIcons.square; - final CameraController controller; - final Duration duration; - CameraRecording({required this.controller, required this.duration}); -} - -class CameraStandby extends CameraState { - final IconData icon = CupertinoIcons.circle; - final CameraController controller; - CameraStandby({required this.controller}); -} - -class CameraError extends CameraState { - final IconData icon = CupertinoIcons.restart; - final Exception exception; - CameraError({required this.exception}); -} - -class CameraCubit extends Cubit { - Timer? timer; - late Duration duration = const Duration(seconds: 0); - - CameraCubit() : super(CameraLoading()); - void startTimer(CameraController controller) { - timer = Timer.periodic(const Duration(milliseconds: 10), (_) { - duration = Duration(milliseconds: duration.inMilliseconds + 10); - - emit(CameraRecording(controller: controller, duration: duration)); - }); - } - - void resetTimer() { - timer?.cancel(); - duration = const Duration(seconds: 0); - } - - void initCamera() async { - try { - final camera = await availableCameras().then((_cameras) => - _cameras.firstWhere( - (camera) => camera.lensDirection == CameraLensDirection.back)); - final cameraController = CameraController( - camera, - ResolutionPreset.medium, - enableAudio: false, - imageFormatGroup: ImageFormatGroup.bgra8888, - ); - await cameraController.initialize(); - emit(CameraStandby(controller: cameraController)); - } catch (e) { - emit(CameraError(exception: Exception(e.toString()))); - } - //TODO Handle exception when no camera available - } - - void triggerState(BuildContext context, CameraState state) async { - try { - if (state is CameraRecording) { - resetTimer(); - XFile file = await state.controller.stopVideoRecording(); - CupertinoPageRoute route = CupertinoPageRoute( - fullscreenDialog: true, - builder: (_) => EditorPage(file: File(file.path))); - Navigator.push(context, route); - initCamera(); - } else if (state is CameraStandby) { - startTimer(state.controller); - state.controller.startVideoRecording(); - } - } on Exception catch (e) { - resetTimer(); - emit(CameraError(exception: e)); - } - } - - void resetCamera() async { - emit(CameraLoading()); - initCamera(); - } - //TODO build robust videocamera widget using methods below -} - - // @override - // void initState() { - // super.initState(); - // WidgetsBinding.instance.addObserver(this); - - // } - - // @override - // void dispose() { - // WidgetsBinding.instance.removeObserver(this); - // super.dispose(); - // } - - // // #docregion AppLifecycle - // @override - // void didChangeAppLifecycleState(AppLifecycleState state) { - // final CameraController? cameraController = widget.controller; - - // // App state changed before we got the chance to initialize. - // if (cameraController == null || !cameraController.value.isInitialized) { - // return; - // } - - // if (state == AppLifecycleState.inactive) { - // cameraController.dispose(); - // } else if (state == AppLifecycleState.resumed) { - // onNewCameraSelected(cameraController.description); - // } - // } - // void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { - // if (widget.controller == null) { - // return; - // } - - // final CameraController cameraController = widget.controller!; - - // final Offset offset = Offset( - // details.localPosition.dx / constraints.maxWidth, - // details.localPosition.dy / constraints.maxHeight, - // ); - // cameraController.setExposurePoint(offset); - // cameraController.setFocusPoint(offset); - // } - // Future onNewCameraSelected(CameraDescription cameraDescription) async { - // final CameraController? oldController = widget.controller; - // if (oldController != null) { - // // `controller` needs to be set to null before getting disposed, - // // to avoid a race condition when we use the controller that is being - // // disposed. This happens when camera permission dialog shows up, - // // which triggers `didChangeAppLifecycleState`, which disposes and - // // re-creates the controller. - // widget.controller = null; - // await oldController.dispose(); - // } - - // final CameraController cameraController = CameraController( - // cameraDescription, - // kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, - // enableAudio: enableAudio, - // imageFormatGroup: ImageFormatGroup.jpeg, - // ); - - // widget.controller = cameraController; - - // // If the controller is updated then update the UI. - // cameraController.addListener(() { - // if (mounted) { - // setState(() {}); - // } - // if (cameraController.value.hasError) { - // showInSnackBar( - // 'Camera error ${cameraController.value.errorDescription}'); - // } - // }); - - // try { - // await cameraController.initialize(); - // } on CameraException catch (e) { - // switch (e.code) { - // case 'CameraAccessDenied': - // showInSnackBar('You have denied camera access.'); - // break; - // case 'CameraAccessDeniedWithoutPrompt': - // // iOS only - // showInSnackBar('Please go to Settings app to enable camera access.'); - // break; - // case 'CameraAccessRestricted': - // // iOS only - // showInSnackBar('Camera access is restricted.'); - // break; - // case 'AudioAccessDenied': - // showInSnackBar('You have denied audio access.'); - // break; - // case 'AudioAccessDeniedWithoutPrompt': - // // iOS only - // showInSnackBar('Please go to Settings app to enable audio access.'); - // break; - // case 'AudioAccessRestricted': - // // iOS only - // showInSnackBar('Audio access is restricted.'); - // break; - // default: - // _showCameraException(e); - // break; - // } - // } - - // if (mounted) { - // setState(() {}); - // } - // } - // void showInSnackBar(String message) { - // ScaffoldMessenger.of(context) - // .showSnackBar(SnackBar(content: Text(message))); - // } diff --git a/lib/views/assessment_view.dart b/lib/views/assessment_view.dart index 3bdec12b..8c9cb62c 100644 --- a/lib/views/assessment_view.dart +++ b/lib/views/assessment_view.dart @@ -1,11 +1,13 @@ - import 'package:flutter/material.dart'; +import 'package:visualpt/views/components/components.dart'; +import 'package:visualpt/views/views.dart'; class AssessmentView extends StatelessWidget { const AssessmentView({super.key}); @override Widget build(BuildContext context) { - return Container(); + return const BaseView( + pageTitle: "Performing ", child: VideoRecorder()); } -} \ No newline at end of file +} diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart index 7ffb1149..44d182ae 100644 --- a/lib/views/auth_view.dart +++ b/lib/views/auth_view.dart @@ -13,7 +13,8 @@ class AuthView extends StatelessWidget { pageTitle: "Save clicks and minutes", child: ConstrainedBox( constraints: - BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.8), + BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.1), + //TODO AuthBloc Goes Here child: Column( children: [ const Padding( @@ -39,8 +40,8 @@ class AuthView extends StatelessWidget { fontWeight: FontWeight.bold), ), ), - //TODO Authentication! - onTap: () {}), + //TODO Authentication State Management! + onTap: () => Navigator.pushNamed(context, "/home")), ) ], ), diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart index 9634ee2f..66cd022b 100644 --- a/lib/views/base_view.dart +++ b/lib/views/base_view.dart @@ -31,18 +31,17 @@ class BaseView extends StatelessWidget { padding: const EdgeInsets.only(left: 21.0), child: Text( pageTitle, - style: TextStyle(fontSize: 24), + style: Styles.subtitleStyle, overflow: TextOverflow.visible, ), ) ], )), + //TODO make this better and animatable Positioned( - left: 16, - right: 16, top: MediaQuery.of(context).size.height / 3, child: SizedBox( - height: screenSize.height, + height: screenSize.height * 15 / 24, width: screenSize.width, child: Container(child: child)), ) diff --git a/lib/views/components/auth_input.dart b/lib/views/components/auth_input.dart new file mode 100644 index 00000000..df3de8e4 --- /dev/null +++ b/lib/views/components/auth_input.dart @@ -0,0 +1,51 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/styles.dart'; + +class AuthInput extends StatefulWidget { + final String placeholder; + final bool obscure; + const AuthInput(this.placeholder, {this.obscure = false, super.key}); + + @override + State createState() => _AuthInputState(); +} + +class _AuthInputState extends State { + bool obsController = false; + + @override + void initState() { + super.initState(); + obsController = widget.obscure; + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: CupertinoTextField( + placeholder: widget.placeholder, + placeholderStyle: Styles.placeholderStyle, + style: Styles.inputStyle, + textAlign: TextAlign.center, + obscureText: obsController, + ), + ), + if (widget.obscure) + Positioned( + right: 24, + child: GestureDetector( + child: Icon( + obsController + ? CupertinoIcons.eye_fill + : CupertinoIcons.eye_slash_fill, + color: CupertinoColors.inactiveGray), + onTap: () => setState(() => obsController = !obsController)), + ), + ], + ); + } +} diff --git a/lib/views/components/components.dart b/lib/views/components/components.dart index e03cd374..ce339e0d 100644 --- a/lib/views/components/components.dart +++ b/lib/views/components/components.dart @@ -2,3 +2,4 @@ export 'auth_input.dart'; export 'assessment_card.dart'; export 'patient_form.dart'; export 'view_background.dart'; +export 'video_recorder.dart'; diff --git a/lib/views/components/patient_form.dart b/lib/views/components/patient_form.dart index fb7a0b01..1895c744 100644 --- a/lib/views/components/patient_form.dart +++ b/lib/views/components/patient_form.dart @@ -5,33 +5,26 @@ import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/styles.dart'; class PatientForm extends StatefulWidget { - const PatientForm({Key? key}) : super(key: key); + final Widget child; + const PatientForm({Key? key, required this.child}) : super(key: key); @override State createState() => _PatientFormState(); } +//TODO FIX WITH BLOC (post nap) class _PatientFormState extends State { final _formKey = GlobalKey(); DateTime date = DateTime.now(); Patient patient = Patient( - first_name: "TODO", - last_name: "TODO", + name: "TODO", + family_name: "TODO", dob: TemporalDate(DateTime.now()), gender: Gender.MALE, height_in_inches: 0); @override Widget build(BuildContext context) { - return CupertinoButton( - color: CupertinoColors.link, - onPressed: showFormDialog, - child: Row( - mainAxisSize: MainAxisSize.min, - children: const [ - Text("New Patient"), - ], - ), - ); + return GestureDetector(onTap: showFormDialog, child: widget.child); } Future showFormDialog() { @@ -41,123 +34,24 @@ class _PatientFormState extends State { builder: (BuildContext context) => StatefulBuilder(builder: (context, setState) { return CupertinoAlertDialog( - title: const Text('Patient Information'), + title: const Text('Patient Information', + style: Styles.defaultTitleOverride), content: Form( key: _formKey, - child: SizedBox( - height: 310, + child: ConstrainedBox( + constraints: const BoxConstraints( + minHeight: 100, + maxHeight: 400, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - const Text("Who is being treated?"), _formInput("First Name"), _formInput("Last Name"), - Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - decoration: BoxDecoration( - color: CupertinoColors.systemBackground, - border: Border.all(), - borderRadius: BorderRadius.circular(8.0), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text("Birth Date"), - CupertinoButton( - onPressed: () => showCupertinoModalPopup( - context: context, - builder: (BuildContext context) => Container( - height: 216, - padding: const EdgeInsets.only(top: 6.0), - margin: EdgeInsets.only( - bottom: MediaQuery.of(context) - .viewInsets - .bottom, - ), - color: CupertinoColors.systemBackground - .resolveFrom(context), - child: SafeArea( - top: false, - child: CupertinoDatePicker( - initialDateTime: date, - mode: CupertinoDatePickerMode.date, - onDateTimeChanged: - (DateTime newDate) { - setState(() => date = newDate); - }, - minimumYear: 1900, - maximumDate: DateTime.now()), - ), - )), - child: - Text('${date.month}/${date.day}/${date.year}'), - ), - ], - ), - ), - ), - GestureDetector( - onTap: () => setState(() => (patient.gender == Gender.MALE) - ? patient = patient.copyWith(gender: Gender.FEMALE) - : patient = patient.copyWith(gender: Gender.MALE)), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), - child: Container( - padding: const EdgeInsets.all(10.0), - decoration: BoxDecoration( - color: CupertinoColors.systemBackground, - border: Border.all(), - borderRadius: BorderRadius.circular(8.0), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Spacer(flex: 2), - const Text("Gender"), - const Spacer(), - Row( - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.all(1.0), - decoration: patient.gender == Gender.MALE - ? BoxDecoration( - border: Border.all(), - borderRadius: - BorderRadius.circular(8.0), - color: Styles - .backgroundComplementory - .withOpacity(0.5)) - : null, - child: const Text("Male"), - ), - const Text("/ "), - Container( - padding: const EdgeInsets.all(1.0), - decoration: patient.gender != Gender.MALE - ? BoxDecoration( - border: Border.all(), - borderRadius: - BorderRadius.circular(8.0), - color: Styles - .backgroundComplementory - .withOpacity(0.5)) - : null, - child: const Text("Female"), - ) - ], - ), - ], - ), - const Spacer(), - ], - ), - ), - ), - ) + _formInput("Birth Date", inputPopup: datePopup), + _formInput("Gender", inputPopup: genderPopup), + _formInput("Height"), ], ), ), @@ -172,10 +66,11 @@ class _PatientFormState extends State { if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); Navigator.pop(context); - // TODO + //TODO Specify assessment type argument + Navigator.pushNamed(context, "/assessment"); } }, - child: const Text("Begin Evaluation"), + child: const Text("Next"), ), ), ], @@ -184,39 +79,142 @@ class _PatientFormState extends State { ); } - Widget _formInput(String field) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), - child: Container( - height: 63, + Widget _formInput(String field, {Future Function()? inputPopup}) { + TextEditingController controller = TextEditingController(); + return CupertinoTextFormFieldRow( + controller: controller, + textAlign: TextAlign.center, + style: Styles.inputStyle, + autocorrect: false, decoration: BoxDecoration( color: CupertinoColors.systemBackground, border: Border.all(), borderRadius: BorderRadius.circular(8.0), ), - child: CupertinoTextFormFieldRow( - prefix: Text(field), - textCapitalization: TextCapitalization.words, - placeholder: 'Enter a ${field.split(' ')[1].toLowerCase()}', - validator: (value) => value == null || value.isEmpty - ? 'Please enter a valid response' - : null, - onSaved: (String? value) { - storeValue(field, value); - }, - ), + textCapitalization: TextCapitalization.words, + placeholder: field, + validator: (value) => value == null || value.isEmpty + ? 'Please enter a valid response' + : null, + onSaved: (String? value) { + storeValue(field, value); + }, + //TODO Manage the controller.text (maybe do this after the rest of the UI build?) + onTap: () => inputPopup != null + ? inputPopup() + .then((value) => value != null ? controller.text = value : null) + : null); + } + + Future datePopup() { + String? value; + return showCupertinoModalPopup( + context: context, + builder: (BuildContext context) => Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + CupertinoButton( + child: const Text("Ok"), + onPressed: () => Navigator.pop(context, value), + ), + Container( + height: 216, + padding: const EdgeInsets.only(top: 6.0), + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + color: CupertinoColors.systemBackground.resolveFrom(context), + child: SafeArea( + top: false, + child: CupertinoDatePicker( + initialDateTime: date, + mode: CupertinoDatePickerMode.date, + onDateTimeChanged: (DateTime newDate) => + value = newDate.toString().split(" ")[0], + minimumYear: 1900, + maximumDate: DateTime.now(), + ), + ), + ), + ], ), ); } + Future genderPopup() { + String? value; + return showCupertinoModalPopup( + context: context, + builder: (BuildContext context) => Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + //Make this a reusable widget + Container( + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.all(8.0), + color: CupertinoColors.systemBackground.resolveFrom(context), + alignment: Alignment.centerRight, + child: Row( + children: [ + const Text("Gender", style: Styles.inputStyle), + const Spacer(), + CupertinoButton( + color: CupertinoColors.link, + onPressed: () => Navigator.pop(context, value), + child: const Text("Save")), + ], + )), + Container( + height: 216, + padding: const EdgeInsets.only(top: 6.0), + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + color: CupertinoColors.systemBackground.resolveFrom(context), + child: SafeArea( + top: false, + child: CupertinoPicker( + itemExtent: Styles.kItemExtent, + onSelectedItemChanged: (int selectedItem) { + setState( + () { + value = selectedItem == 0 + ? "" + : Gender.values[selectedItem - 1].name.toString(); + }, + ); + }, + children: List.generate( + 3, + (index) => index == 0 + ? const Text("") + : Text( + Gender.values[index - 1].name.toString(), + ), + ), + ), + ), + ), + ]), + ); + } + void storeValue(String field, String? value) { try { switch (field) { case "First Name": - patient = patient.copyWith(first_name: value!); + patient = patient.copyWith(name: value!); break; case "Last Name": - patient = patient.copyWith(last_name: value!); + patient = patient.copyWith(family_name: value!); + break; + case "Birth Date": + patient = patient.copyWith(dob: TemporalDate.fromString(value!)); + break; + case "Gender": + //TODO FIX WITH BLOC PATTERN + patient = patient.copyWith(gender: Gender.MALE); break; case "Height": patient = patient.copyWith(height_in_inches: double.tryParse(value!)); diff --git a/lib/UI/video_page.dart b/lib/views/components/video_recorder.dart similarity index 87% rename from lib/UI/video_page.dart rename to lib/views/components/video_recorder.dart index 7e11396f..3dee842a 100644 --- a/lib/UI/video_page.dart +++ b/lib/views/components/video_recorder.dart @@ -1,28 +1,22 @@ import 'package:camera/camera.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:visualpt/state/camera_cubit.dart'; +import 'package:visualpt/bloc/camera_cubit.dart'; -class VideoPage extends StatefulWidget { - const VideoPage({Key? key}) : super(key: key); - - @override - _VideoPageState createState() => _VideoPageState(); -} - -class _VideoPageState extends State { - @override - void initState() { - super.initState(); - } +class VideoRecorder extends StatelessWidget { + const VideoRecorder({Key? key}) : super(key: key); @override Widget build(BuildContext context) { + //TODO Put all this blocProvider jumble in the bloc folder return BlocProvider( create: (context) => CameraCubit()..initCamera(), child: BlocBuilder( builder: (context, state) { - return CupertinoPageScaffold( + return Container( + height: MediaQuery.of(context).size.height * 0.8, + width: MediaQuery.of(context).size.width * 0.8, + decoration: BoxDecoration(border: Border.all()), child: state is CameraError ? Center( child: Column( @@ -69,8 +63,8 @@ class _VideoPageState extends State { Widget cameraWidget(BuildContext context, CameraState state) { return SizedBox( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height * 0.8, + width: MediaQuery.of(context).size.width * 0.8, child: Stack( alignment: Alignment.center, children: (() { diff --git a/lib/UI/editor_page.dart b/lib/views/editor_view.dart similarity index 100% rename from lib/UI/editor_page.dart rename to lib/views/editor_view.dart diff --git a/lib/views/landing_view.dart b/lib/views/landing_view.dart new file mode 100644 index 00000000..de274d83 --- /dev/null +++ b/lib/views/landing_view.dart @@ -0,0 +1,19 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/styles.dart'; +import 'base_view.dart'; +import 'components/components.dart'; + +//TODO The first view anyone on the app sees. Synonymous with the loading view +class LandingView extends StatelessWidget { + const LandingView({super.key}); + + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + child: Stack( + children: const [ + BaseView(pageTitle: "TODO", child: Text("TODO")), + ], + )); + } +} diff --git a/lib/views/launch_view.dart b/lib/views/launch_view.dart deleted file mode 100644 index e3bbdc2c..00000000 --- a/lib/views/launch_view.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:visualpt/views/components/view_background.dart'; - -class LaunchView extends StatelessWidget { - const LaunchView({super.key}); - - @override - Widget build(BuildContext context) { - return CupertinoPageScaffold( - child: Stack( - children: const [ViewBackground(seed: 0)], - )); - } -} diff --git a/lib/views/styles.dart b/lib/views/styles.dart index 2bec14fc..fe5686c9 100644 --- a/lib/views/styles.dart +++ b/lib/views/styles.dart @@ -7,16 +7,26 @@ class Styles { factory Styles() => _instance; Styles._internal(); + static const double kItemExtent = 32.0; + + //Default Colors static const Color backgroundPrimary = Color(0xFF91DAFF); static const Color backgroundAccent = Color(0xFF4EC3FF); static const Color backgroundComplementory = Color(0xFFf8c098); - - static const Color actionPrimary = Color(0xFF1F7A97); - + static const Color actionPrimary = Color(0xFF1F7A97); //Asset Paths static const String mainLogoPath = "assets/vpt_mainlogo.png"; static const String ctsibLogoPath = "assets/ctsib_mainlogo.png"; static const String gvLogoPath = "assets/gv_mainlogo.png"; + + //Text Styles + static const TextStyle subtitleStyle = TextStyle(fontSize: 24); + static const TextStyle defaultTitleOverride = TextStyle( + fontFamily: "Unbounded", fontSize: 32, fontWeight: FontWeight.bold); + static const TextStyle placeholderStyle = + TextStyle(color: CupertinoColors.inactiveGray, fontSize: 24); + static const TextStyle inputStyle = + TextStyle(color: CupertinoColors.black, fontSize: 24); } From e8d93af33b448762ced1ee20b6c7bc3063935a37 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Mon, 6 Feb 2023 23:56:56 -0500 Subject: [PATCH 16/43] updated models for assessment classification --- amplify/backend/api/visualpt/schema.graphql | 7 ++++- lib/models/Assessment.dart | 32 ++++++++++----------- lib/models/AssessmentType.dart | 25 ++++++++++++++++ lib/models/ModelProvider.dart | 3 +- 4 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 lib/models/AssessmentType.dart diff --git a/amplify/backend/api/visualpt/schema.graphql b/amplify/backend/api/visualpt/schema.graphql index 60435126..caa6837c 100644 --- a/amplify/backend/api/visualpt/schema.graphql +++ b/amplify/backend/api/visualpt/schema.graphql @@ -33,7 +33,7 @@ type Patient @model @auth(rules: [{allow: owner}]) { type Assessment @model @auth(rules: [{allow: owner}]) { id: ID! - name: String! + type: AssessmentType! timestamp: AWSTimestamp! pdf_base64: String! patientID: ID! @index(name: "byPatient") @@ -43,4 +43,9 @@ type Assessment @model @auth(rules: [{allow: owner}]) { enum Gender { MALE FEMALE +} + +enum AssessmentType { + CTSIB + GAIT } \ No newline at end of file diff --git a/lib/models/Assessment.dart b/lib/models/Assessment.dart index 15b32899..b423c2ab 100644 --- a/lib/models/Assessment.dart +++ b/lib/models/Assessment.dart @@ -30,7 +30,7 @@ import 'package:flutter/foundation.dart'; class Assessment extends Model { static const classType = const _AssessmentModelType(); final String id; - final String? _name; + final AssessmentType? _type; final TemporalTimestamp? _timestamp; final String? _pdf_base64; final String? _patientID; @@ -51,9 +51,9 @@ class Assessment extends Model { ); } - String get name { + AssessmentType get type { try { - return _name!; + return _type!; } catch(e) { throw new AmplifyCodeGenModelException( AmplifyExceptionMessages.codeGenRequiredFieldForceCastExceptionMessage, @@ -115,12 +115,12 @@ class Assessment extends Model { return _updatedAt; } - const Assessment._internal({required this.id, required name, required timestamp, required pdf_base64, required patientID, SkeletalSequences, createdAt, updatedAt}): _name = name, _timestamp = timestamp, _pdf_base64 = pdf_base64, _patientID = patientID, _SkeletalSequences = SkeletalSequences, _createdAt = createdAt, _updatedAt = updatedAt; + const Assessment._internal({required this.id, required type, required timestamp, required pdf_base64, required patientID, SkeletalSequences, createdAt, updatedAt}): _type = type, _timestamp = timestamp, _pdf_base64 = pdf_base64, _patientID = patientID, _SkeletalSequences = SkeletalSequences, _createdAt = createdAt, _updatedAt = updatedAt; - factory Assessment({String? id, required String name, required TemporalTimestamp timestamp, required String pdf_base64, required String patientID, List? SkeletalSequences}) { + factory Assessment({String? id, required AssessmentType type, required TemporalTimestamp timestamp, required String pdf_base64, required String patientID, List? SkeletalSequences}) { return Assessment._internal( id: id == null ? UUID.getUUID() : id, - name: name, + type: type, timestamp: timestamp, pdf_base64: pdf_base64, patientID: patientID, @@ -136,7 +136,7 @@ class Assessment extends Model { if (identical(other, this)) return true; return other is Assessment && id == other.id && - _name == other._name && + _type == other._type && _timestamp == other._timestamp && _pdf_base64 == other._pdf_base64 && _patientID == other._patientID && @@ -152,7 +152,7 @@ class Assessment extends Model { buffer.write("Assessment {"); buffer.write("id=" + "$id" + ", "); - buffer.write("name=" + "$_name" + ", "); + buffer.write("type=" + (_type != null ? enumToString(_type)! : "null") + ", "); buffer.write("timestamp=" + (_timestamp != null ? _timestamp!.toString() : "null") + ", "); buffer.write("pdf_base64=" + "$_pdf_base64" + ", "); buffer.write("patientID=" + "$_patientID" + ", "); @@ -163,10 +163,10 @@ class Assessment extends Model { return buffer.toString(); } - Assessment copyWith({String? name, TemporalTimestamp? timestamp, String? pdf_base64, String? patientID, List? SkeletalSequences}) { + Assessment copyWith({AssessmentType? type, TemporalTimestamp? timestamp, String? pdf_base64, String? patientID, List? SkeletalSequences}) { return Assessment._internal( id: id, - name: name ?? this.name, + type: type ?? this.type, timestamp: timestamp ?? this.timestamp, pdf_base64: pdf_base64 ?? this.pdf_base64, patientID: patientID ?? this.patientID, @@ -175,7 +175,7 @@ class Assessment extends Model { Assessment.fromJson(Map json) : id = json['id'], - _name = json['name'], + _type = enumFromString(json['type'], AssessmentType.values), _timestamp = json['timestamp'] != null ? TemporalTimestamp.fromSeconds(json['timestamp']) : null, _pdf_base64 = json['pdf_base64'], _patientID = json['patientID'], @@ -189,16 +189,16 @@ class Assessment extends Model { _updatedAt = json['updatedAt'] != null ? TemporalDateTime.fromString(json['updatedAt']) : null; Map toJson() => { - 'id': id, 'name': _name, 'timestamp': _timestamp?.toSeconds(), 'pdf_base64': _pdf_base64, 'patientID': _patientID, 'SkeletalSequences': _SkeletalSequences?.map((SkeletalSequence? e) => e?.toJson()).toList(), 'createdAt': _createdAt?.format(), 'updatedAt': _updatedAt?.format() + 'id': id, 'type': enumToString(_type), 'timestamp': _timestamp?.toSeconds(), 'pdf_base64': _pdf_base64, 'patientID': _patientID, 'SkeletalSequences': _SkeletalSequences?.map((SkeletalSequence? e) => e?.toJson()).toList(), 'createdAt': _createdAt?.format(), 'updatedAt': _updatedAt?.format() }; Map toMap() => { - 'id': id, 'name': _name, 'timestamp': _timestamp, 'pdf_base64': _pdf_base64, 'patientID': _patientID, 'SkeletalSequences': _SkeletalSequences, 'createdAt': _createdAt, 'updatedAt': _updatedAt + 'id': id, 'type': _type, 'timestamp': _timestamp, 'pdf_base64': _pdf_base64, 'patientID': _patientID, 'SkeletalSequences': _SkeletalSequences, 'createdAt': _createdAt, 'updatedAt': _updatedAt }; static final QueryModelIdentifier MODEL_IDENTIFIER = QueryModelIdentifier(); static final QueryField ID = QueryField(fieldName: "id"); - static final QueryField NAME = QueryField(fieldName: "name"); + static final QueryField TYPE = QueryField(fieldName: "type"); static final QueryField TIMESTAMP = QueryField(fieldName: "timestamp"); static final QueryField PDF_BASE64 = QueryField(fieldName: "pdf_base64"); static final QueryField PATIENTID = QueryField(fieldName: "patientID"); @@ -230,9 +230,9 @@ class Assessment extends Model { modelSchemaDefinition.addField(ModelFieldDefinition.id()); modelSchemaDefinition.addField(ModelFieldDefinition.field( - key: Assessment.NAME, + key: Assessment.TYPE, isRequired: true, - ofType: ModelFieldType(ModelFieldTypeEnum.string) + ofType: ModelFieldType(ModelFieldTypeEnum.enumeration) )); modelSchemaDefinition.addField(ModelFieldDefinition.field( diff --git a/lib/models/AssessmentType.dart b/lib/models/AssessmentType.dart new file mode 100644 index 00000000..82f3ed1e --- /dev/null +++ b/lib/models/AssessmentType.dart @@ -0,0 +1,25 @@ +/* +* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// NOTE: This file is generated and may not follow lint rules defined in your app +// Generated files can be excluded from analysis in analysis_options.yaml +// For more info, see: https://dart.dev/guides/language/analysis-options#excluding-code-from-analysis + +// ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously + +enum AssessmentType { + CTSIB, + GAIT +} \ No newline at end of file diff --git a/lib/models/ModelProvider.dart b/lib/models/ModelProvider.dart index 07677285..5ebe7478 100644 --- a/lib/models/ModelProvider.dart +++ b/lib/models/ModelProvider.dart @@ -25,13 +25,14 @@ import 'Patient.dart'; import 'SkeletalSequence.dart'; export 'Assessment.dart'; +export 'AssessmentType.dart'; export 'Gender.dart'; export 'Patient.dart'; export 'SkeletalSequence.dart'; class ModelProvider implements ModelProviderInterface { @override - String version = "eb93377287d2ee82230970a11ad1ab6f"; + String version = "6816d463b78b5418ba6d1ba2381da496"; @override List modelSchemas = [Assessment.schema, Patient.schema, SkeletalSequence.schema]; static final ModelProvider _instance = ModelProvider(); From b537a76e85c806a24aa85b31560bdc9260b6bccf Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Tue, 7 Feb 2023 00:00:22 -0500 Subject: [PATCH 17/43] auth UI presentable, init bloc connection --- lib/bloc/auth/auth_bloc.dart | 57 ++++++++++ lib/bloc/auth/auth_event.dart | 11 ++ lib/bloc/auth/auth_state.dart | 15 +++ lib/bloc/bloc.dart | 2 + lib/views/auth_view.dart | 153 +++++++++++++++++++++------ lib/views/components/auth_input.dart | 24 +++-- 6 files changed, 220 insertions(+), 42 deletions(-) create mode 100644 lib/bloc/auth/auth_bloc.dart create mode 100644 lib/bloc/auth/auth_event.dart create mode 100644 lib/bloc/auth/auth_state.dart create mode 100644 lib/bloc/bloc.dart diff --git a/lib/bloc/auth/auth_bloc.dart b/lib/bloc/auth/auth_bloc.dart new file mode 100644 index 00000000..d1c9e953 --- /dev/null +++ b/lib/bloc/auth/auth_bloc.dart @@ -0,0 +1,57 @@ +import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; + +part 'auth_event.dart'; +part 'auth_state.dart'; + +enum AuthDataType { email, password, confirmPassword } + +//TODO possibly a security risk, address this before production +class AuthData { + late String email = ""; + late String password = ""; + late String confirmPassword = ""; + + void setEmail(String value) => email = value; + void setPass(String value) => password = value; + void setConfirmPass(String value) => confirmPassword = value; + bool isLoginReady() => email.isNotEmpty && password.isNotEmpty; +//TODO REGEX FOR EMAIL VALIDATION AND PASSWORD STRENGTH + bool isSignupReady() => + email.isNotEmpty && + password.isNotEmpty && + confirmPassword.isNotEmpty && + password == confirmPassword; +} + +class AuthBloc extends Bloc { + AuthData authData = AuthData(); + AuthBloc() : super(AuthLogin()) { + on(_authToggled); + on(_authAttempted); + } + void _authToggled(AuthToggle event, Emitter emit) { + state is AuthSignUp ? emit(AuthLogin()) : emit(AuthSignUp()); + } + + void _authAttempted(AuthAttempt event, Emitter emit) { + if (state is AuthLogin && event.authData.isLoginReady()) { + //TODO set up the user account in the cloud + emit(AuthSuccess()); + } else if (state is AuthSignUp && event.authData.isSignupReady()) { + //TODO set up the user account in the cloud + emit(AuthSuccess()); + } + emit(AuthError("Something went wrong, please try again")); + } + + void storeAuthData(AuthDataType type, String data) { + if (type == AuthDataType.email) { + authData.setEmail(data); + } else if (type == AuthDataType.password) { + authData.setPass(data); + } else if (type == AuthDataType.confirmPassword) { + authData.setConfirmPass(data); + } + } +} diff --git a/lib/bloc/auth/auth_event.dart b/lib/bloc/auth/auth_event.dart new file mode 100644 index 00000000..700db9af --- /dev/null +++ b/lib/bloc/auth/auth_event.dart @@ -0,0 +1,11 @@ +part of 'auth_bloc.dart'; + +@immutable +abstract class AuthEvent {} + +class AuthToggle extends AuthEvent {} + +class AuthAttempt extends AuthEvent { + final AuthData authData; + AuthAttempt({required this.authData}); +} diff --git a/lib/bloc/auth/auth_state.dart b/lib/bloc/auth/auth_state.dart new file mode 100644 index 00000000..01df1881 --- /dev/null +++ b/lib/bloc/auth/auth_state.dart @@ -0,0 +1,15 @@ +part of 'auth_bloc.dart'; + +@immutable +abstract class AuthState {} + +class AuthSignUp extends AuthState {} + +class AuthLogin extends AuthState {} + +class AuthSuccess extends AuthState {} + +class AuthError extends AuthState { + final String message; + AuthError(this.message); +} diff --git a/lib/bloc/bloc.dart b/lib/bloc/bloc.dart new file mode 100644 index 00000000..ed80dc4e --- /dev/null +++ b/lib/bloc/bloc.dart @@ -0,0 +1,2 @@ +export 'assessment/assessment_bloc.dart'; +export 'auth/auth_bloc.dart'; diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart index 44d182ae..7bb30660 100644 --- a/lib/views/auth_view.dart +++ b/lib/views/auth_view.dart @@ -1,5 +1,7 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:visualpt/views/styles.dart'; +import '../bloc/bloc.dart'; import 'base_view.dart'; import 'components/components.dart'; @@ -11,42 +13,127 @@ class AuthView extends StatelessWidget { return CupertinoPageScaffold( child: BaseView( pageTitle: "Save clicks and minutes", - child: ConstrainedBox( - constraints: - BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.1), - //TODO AuthBloc Goes Here - child: Column( - children: [ - const Padding( - padding: EdgeInsets.all(8.0), - child: Text("Personalize your experience"), - ), - const AuthInput("Username"), - const AuthInput("Password", obscure: true), - Padding( - padding: const EdgeInsets.all(32.0), - child: GestureDetector( - child: Container( - alignment: Alignment.center, - height: MediaQuery.of(context).size.width / 3, - width: MediaQuery.of(context).size.width / 3, - decoration: const BoxDecoration( - shape: BoxShape.circle, color: Styles.actionPrimary), - child: const Text( - "Log In", - style: TextStyle( - color: CupertinoColors.white, - fontSize: 24, - fontWeight: FontWeight.bold), - ), - ), - //TODO Authentication State Management! - onTap: () => Navigator.pushNamed(context, "/home")), - ) - ], + child: BlocProvider( + create: (context) => AuthBloc(), + child: BlocBuilder( + builder: (context, state) { + if (state is AuthSuccess) { + Navigator.pushNamed(context, "/home"); + } else if (state is AuthError) { + // showCupertinoDialog( + // context: context, + // builder: (context) => CupertinoAlertDialog( + // title: const Text( + // "Uh oh...", + // style: Styles.defaultTitleOverride, + // ), + // content: Column( + // children: [ + const Text( + "An error has occured", + ); + // const Text( + // "We got the following error message:", + // ), + // Text(state.message) + // ], + // ), + // ), + // ); + } + + if (state is AuthSignUp) { + return signupForm(context); + } else { + return loginForm(context); + } + }, ), ), ), ); } } + +Widget loginForm(BuildContext context) { + return Column( + children: [ + //TODO stylize for ciewability + CupertinoButton( + child: Text("Sign up"), + onPressed: () => addAuthEvent(context, event: AuthToggle()), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Access your workflows"), + ), + const AuthInput(AuthDataType.email), + const AuthInput(AuthDataType.password, obscure: true), + Padding( + padding: const EdgeInsets.all(32.0), + child: GestureDetector( + child: Container( + alignment: Alignment.center, + height: MediaQuery.of(context).size.width / 3, + width: MediaQuery.of(context).size.width / 3, + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.actionPrimary), + child: const Text( + "Log In", + style: TextStyle( + color: CupertinoColors.white, + fontSize: 24, + fontWeight: FontWeight.bold), + ), + ), + onTap: () => addAuthEvent(context)), + ) + ], + ); +} + +Widget signupForm(BuildContext context) { + return Column( + children: [ + CupertinoButton( + child: Text("Log in"), + onPressed: () => addAuthEvent(context, event: AuthToggle()), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Rethink your workflows"), + ), + const AuthInput(AuthDataType.email), + const AuthInput(AuthDataType.password, obscure: true), + const AuthInput(AuthDataType.confirmPassword, obscure: true), + Padding( + padding: const EdgeInsets.all(32.0), + child: GestureDetector( + child: Container( + alignment: Alignment.center, + height: MediaQuery.of(context).size.width / 3, + width: MediaQuery.of(context).size.width / 3, + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.actionPrimary), + child: const Text( + "Sign Up", + style: TextStyle( + color: CupertinoColors.white, + fontSize: 24, + fontWeight: FontWeight.bold), + ), + ), + onTap: () => addAuthEvent(context), + ), + ) + ], + ); +} + +///If event is null, [AuthAttempt] is assumed and [AuthData] is passed from the bloc +void addAuthEvent(BuildContext context, {AuthEvent? event}) { + final authBloc = BlocProvider.of(context); + event == null + ? authBloc.add(AuthAttempt(authData: authBloc.authData)) + : authBloc.add(event); +} diff --git a/lib/views/components/auth_input.dart b/lib/views/components/auth_input.dart index df3de8e4..85d921f2 100644 --- a/lib/views/components/auth_input.dart +++ b/lib/views/components/auth_input.dart @@ -1,10 +1,13 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:visualpt/views/styles.dart'; +import '../../bloc/bloc.dart'; + class AuthInput extends StatefulWidget { - final String placeholder; + final AuthDataType type; final bool obscure; - const AuthInput(this.placeholder, {this.obscure = false, super.key}); + const AuthInput(this.type, {this.obscure = false, super.key}); @override State createState() => _AuthInputState(); @@ -27,23 +30,26 @@ class _AuthInputState extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: CupertinoTextField( - placeholder: widget.placeholder, + placeholder: widget.type.name.toString(), placeholderStyle: Styles.placeholderStyle, style: Styles.inputStyle, textAlign: TextAlign.center, obscureText: obsController, + onSubmitted: (value) => BlocProvider.of(context) + .storeAuthData(widget.type, value), ), ), if (widget.obscure) Positioned( right: 24, child: GestureDetector( - child: Icon( - obsController - ? CupertinoIcons.eye_fill - : CupertinoIcons.eye_slash_fill, - color: CupertinoColors.inactiveGray), - onTap: () => setState(() => obsController = !obsController)), + child: Icon( + obsController + ? CupertinoIcons.eye_fill + : CupertinoIcons.eye_slash_fill, + color: CupertinoColors.inactiveGray), + onTap: () => setState(() => obsController = !obsController), + ), ), ], ); From af342aad780e688add3e7129ccb2f8bbb10c0966 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Tue, 7 Feb 2023 23:51:50 -0500 Subject: [PATCH 18/43] Debug auth --- lib/views/auth_view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart index 7bb30660..af289c5c 100644 --- a/lib/views/auth_view.dart +++ b/lib/views/auth_view.dart @@ -13,6 +13,7 @@ class AuthView extends StatelessWidget { return CupertinoPageScaffold( child: BaseView( pageTitle: "Save clicks and minutes", + //TODO print debug to figure out why this shit broke child: BlocProvider( create: (context) => AuthBloc(), child: BlocBuilder( From af9e0dd5b90d1df1f2178c6c2d7dd9d908dceb14 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 8 Feb 2023 11:45:03 -0500 Subject: [PATCH 19/43] Auth is Done --- lib/bloc/auth/auth_bloc.dart | 21 ++- lib/views/auth_view.dart | 202 +++++++++++++-------------- lib/views/components/auth_input.dart | 8 +- lib/views/components/components.dart | 1 + lib/views/components/shaker.dart | 46 ++++++ pubspec.lock | 2 +- pubspec.yaml | 1 + 7 files changed, 172 insertions(+), 109 deletions(-) create mode 100644 lib/views/components/shaker.dart diff --git a/lib/bloc/auth/auth_bloc.dart b/lib/bloc/auth/auth_bloc.dart index d1c9e953..aa065af4 100644 --- a/lib/bloc/auth/auth_bloc.dart +++ b/lib/bloc/auth/auth_bloc.dart @@ -1,4 +1,5 @@ -import 'package:bloc/bloc.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/cupertino.dart'; import 'package:meta/meta.dart'; part 'auth_event.dart'; @@ -22,27 +23,34 @@ class AuthData { password.isNotEmpty && confirmPassword.isNotEmpty && password == confirmPassword; + void debugPrint() => + print("email:$email\npass:$password\nconfirm:$confirmPassword\n"); } class AuthBloc extends Bloc { AuthData authData = AuthData(); + bool method = true; //True = Login/False = Signup AuthBloc() : super(AuthLogin()) { on(_authToggled); on(_authAttempted); } void _authToggled(AuthToggle event, Emitter emit) { state is AuthSignUp ? emit(AuthLogin()) : emit(AuthSignUp()); + method = !method; } - void _authAttempted(AuthAttempt event, Emitter emit) { + void _authAttempted(AuthAttempt event, Emitter emit) async { if (state is AuthLogin && event.authData.isLoginReady()) { //TODO set up the user account in the cloud emit(AuthSuccess()); } else if (state is AuthSignUp && event.authData.isSignupReady()) { //TODO set up the user account in the cloud emit(AuthSuccess()); + } else { + final tempState = state; + emit(AuthError("Something went wrong, please try again")); + await Future.delayed(const Duration(seconds: 1), () => emit(tempState)); } - emit(AuthError("Something went wrong, please try again")); } void storeAuthData(AuthDataType type, String data) { @@ -54,4 +62,11 @@ class AuthBloc extends Bloc { authData.setConfirmPass(data); } } + + void clearInput(TextEditingController controller) { + print("Attempting to Clear"); + if (state is AuthError) { + controller.clear(); + } + } } diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart index af289c5c..ed827ced 100644 --- a/lib/views/auth_view.dart +++ b/lib/views/auth_view.dart @@ -5,9 +5,16 @@ import '../bloc/bloc.dart'; import 'base_view.dart'; import 'components/components.dart'; -class AuthView extends StatelessWidget { +class AuthView extends StatefulWidget { const AuthView({super.key}); + @override + State createState() => _AuthViewState(); +} + +class _AuthViewState extends State { + final GlobalKey _shakeKey = GlobalKey(); + @override Widget build(BuildContext context) { return CupertinoPageScaffold( @@ -18,31 +25,14 @@ class AuthView extends StatelessWidget { create: (context) => AuthBloc(), child: BlocBuilder( builder: (context, state) { - if (state is AuthSuccess) { - Navigator.pushNamed(context, "/home"); - } else if (state is AuthError) { - // showCupertinoDialog( - // context: context, - // builder: (context) => CupertinoAlertDialog( - // title: const Text( - // "Uh oh...", - // style: Styles.defaultTitleOverride, - // ), - // content: Column( - // children: [ - const Text( - "An error has occured", - ); - // const Text( - // "We got the following error message:", - // ), - // Text(state.message) - // ], - // ), - // ), - // ); - } - + WidgetsBinding.instance.addPostFrameCallback((_) { + if (state is AuthSuccess) { + context.read().close(); //TODO save auth data before closing + Navigator.popAndPushNamed(context, "/home"); + } else if (state is AuthError) { + _shakeKey.currentState?.shake(); + } + }); if (state is AuthSignUp) { return signupForm(context); } else { @@ -54,87 +44,93 @@ class AuthView extends StatelessWidget { ), ); } -} -Widget loginForm(BuildContext context) { - return Column( - children: [ - //TODO stylize for ciewability - CupertinoButton( - child: Text("Sign up"), - onPressed: () => addAuthEvent(context, event: AuthToggle()), - ), - const Padding( - padding: EdgeInsets.all(8.0), - child: Text("Access your workflows"), + Widget loginForm(BuildContext context) { + return Shaker( + key: _shakeKey, + child: Column( + children: [ + //TODO stylize for ciewability + CupertinoButton( + child: const Text("Sign up"), + onPressed: () => addAuthEvent(context, event: AuthToggle()), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Access your workflows"), + ), + const AuthInput(AuthDataType.email), + const AuthInput(AuthDataType.password, obscure: true), + Padding( + padding: const EdgeInsets.all(32.0), + child: GestureDetector( + child: Container( + alignment: Alignment.center, + height: MediaQuery.of(context).size.width / 3, + width: MediaQuery.of(context).size.width / 3, + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.actionPrimary), + child: const Text( + "Log In", + style: TextStyle( + color: CupertinoColors.white, + fontSize: 24, + fontWeight: FontWeight.bold), + ), + ), + onTap: () => addAuthEvent(context)), + ) + ], ), - const AuthInput(AuthDataType.email), - const AuthInput(AuthDataType.password, obscure: true), - Padding( - padding: const EdgeInsets.all(32.0), - child: GestureDetector( - child: Container( - alignment: Alignment.center, - height: MediaQuery.of(context).size.width / 3, - width: MediaQuery.of(context).size.width / 3, - decoration: const BoxDecoration( - shape: BoxShape.circle, color: Styles.actionPrimary), - child: const Text( - "Log In", - style: TextStyle( - color: CupertinoColors.white, - fontSize: 24, - fontWeight: FontWeight.bold), + ); + } + + Widget signupForm(BuildContext context) { + return Shaker( + key: _shakeKey, + child: Column( + children: [ + CupertinoButton( + child: const Text("Log in"), + onPressed: () => addAuthEvent(context, event: AuthToggle()), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Rethink your workflows"), + ), + const AuthInput(AuthDataType.email), + const AuthInput(AuthDataType.password, obscure: true), + const AuthInput(AuthDataType.confirmPassword, obscure: true), + Padding( + padding: const EdgeInsets.all(32.0), + child: GestureDetector( + child: Container( + alignment: Alignment.center, + height: MediaQuery.of(context).size.width / 3, + width: MediaQuery.of(context).size.width / 3, + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.actionPrimary), + child: const Text( + "Sign Up", + style: TextStyle( + color: CupertinoColors.white, + fontSize: 24, + fontWeight: FontWeight.bold), + ), ), + onTap: () => addAuthEvent(context), ), - onTap: () => addAuthEvent(context)), - ) - ], - ); -} - -Widget signupForm(BuildContext context) { - return Column( - children: [ - CupertinoButton( - child: Text("Log in"), - onPressed: () => addAuthEvent(context, event: AuthToggle()), - ), - const Padding( - padding: EdgeInsets.all(8.0), - child: Text("Rethink your workflows"), + ) + ], ), - const AuthInput(AuthDataType.email), - const AuthInput(AuthDataType.password, obscure: true), - const AuthInput(AuthDataType.confirmPassword, obscure: true), - Padding( - padding: const EdgeInsets.all(32.0), - child: GestureDetector( - child: Container( - alignment: Alignment.center, - height: MediaQuery.of(context).size.width / 3, - width: MediaQuery.of(context).size.width / 3, - decoration: const BoxDecoration( - shape: BoxShape.circle, color: Styles.actionPrimary), - child: const Text( - "Sign Up", - style: TextStyle( - color: CupertinoColors.white, - fontSize: 24, - fontWeight: FontWeight.bold), - ), - ), - onTap: () => addAuthEvent(context), - ), - ) - ], - ); -} + ); + } -///If event is null, [AuthAttempt] is assumed and [AuthData] is passed from the bloc -void addAuthEvent(BuildContext context, {AuthEvent? event}) { - final authBloc = BlocProvider.of(context); - event == null - ? authBloc.add(AuthAttempt(authData: authBloc.authData)) - : authBloc.add(event); + ///If event is null, [AuthAttempt] is assumed and [AuthData] is passed from the bloc + void addAuthEvent(BuildContext context, {AuthEvent? event}) { + final authBloc = BlocProvider.of(context); + event == null + ? authBloc.add(AuthAttempt(authData: authBloc.authData)) + : authBloc.add(event); + } } diff --git a/lib/views/components/auth_input.dart b/lib/views/components/auth_input.dart index 85d921f2..d76069ce 100644 --- a/lib/views/components/auth_input.dart +++ b/lib/views/components/auth_input.dart @@ -15,6 +15,7 @@ class AuthInput extends StatefulWidget { class _AuthInputState extends State { bool obsController = false; + late TextEditingController controller = TextEditingController(); @override void initState() { @@ -24,19 +25,22 @@ class _AuthInputState extends State { @override Widget build(BuildContext context) { + final authBloc = BlocProvider.of(context); return Stack( alignment: Alignment.center, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: CupertinoTextField( + controller: controller, placeholder: widget.type.name.toString(), placeholderStyle: Styles.placeholderStyle, style: Styles.inputStyle, textAlign: TextAlign.center, obscureText: obsController, - onSubmitted: (value) => BlocProvider.of(context) - .storeAuthData(widget.type, value), + onChanged: (value) { + authBloc.storeAuthData(widget.type, value); + }, ), ), if (widget.obscure) diff --git a/lib/views/components/components.dart b/lib/views/components/components.dart index ce339e0d..4263d7ab 100644 --- a/lib/views/components/components.dart +++ b/lib/views/components/components.dart @@ -3,3 +3,4 @@ export 'assessment_card.dart'; export 'patient_form.dart'; export 'view_background.dart'; export 'video_recorder.dart'; +export 'shaker.dart'; diff --git a/lib/views/components/shaker.dart b/lib/views/components/shaker.dart new file mode 100644 index 00000000..09acfbe3 --- /dev/null +++ b/lib/views/components/shaker.dart @@ -0,0 +1,46 @@ +//Sourced from https://stackoverflow.com/questions/59123469/how-to-shake-a-widget-in-flutter-on-invalid-input +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:vector_math/vector_math_64.dart' as math; + +class Shaker extends StatefulWidget { + const Shaker({Key? key, required this.child}) : super(key: key); + final Widget child; + + @override + State createState() => ShakerState(); +} + +class ShakerState extends State with SingleTickerProviderStateMixin { + late AnimationController animationController; + + @override + void initState() { + super.initState(); + animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 200), // how long the shake happens + )..addListener(() => setState(() {})); + } + + math.Vector3 _shake() { + double progress = animationController.value; + double offset = + sin(progress * pi * 5.0); // increase to make it vibrate faster + return math.Vector3( + offset * 20, 0.0, 0.0); // increase to make it vibrate wider + } + + void shake() { + animationController.forward(from: 0); + } + + @override + Widget build(BuildContext context) { + return Transform( + transform: Matrix4.translation(_shake()), + child: widget.child, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 5d98d49c..68575867 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -558,7 +558,7 @@ packages: source: hosted version: "3.0.6" vector_math: - dependency: transitive + dependency: "direct main" description: name: vector_math sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" diff --git a/pubspec.yaml b/pubspec.yaml index 70f2e3cf..56cb4d90 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: amplify_flutter: ^0.6.0 amplify_datastore: ^0.6.0 amplify_auth_cognito: ^0.6.0 + vector_math: ^2.1.4 dev_dependencies: flutter_test: From cf9819759e20447afe573af831d633d9dc55430b Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 8 Feb 2023 14:06:41 -0500 Subject: [PATCH 20/43] renaming barrelfiles --- lib/bloc/_bloc.dart | 4 ++++ lib/views/_views.dart | 9 +++++++++ lib/views/components/_components.dart | 6 ++++++ 3 files changed, 19 insertions(+) create mode 100644 lib/bloc/_bloc.dart create mode 100644 lib/views/_views.dart create mode 100644 lib/views/components/_components.dart diff --git a/lib/bloc/_bloc.dart b/lib/bloc/_bloc.dart new file mode 100644 index 00000000..98c64ef5 --- /dev/null +++ b/lib/bloc/_bloc.dart @@ -0,0 +1,4 @@ +export 'assessment/assessment_bloc.dart'; +export 'auth/auth_bloc.dart'; +export 'form/form_bloc.dart'; +export 'camera/camera_bloc.dart'; diff --git a/lib/views/_views.dart b/lib/views/_views.dart new file mode 100644 index 00000000..269fe01f --- /dev/null +++ b/lib/views/_views.dart @@ -0,0 +1,9 @@ +///Barrel File for ALL views. Use this when importing more than 1 view into a file. + +export 'auth_view.dart'; +export 'home_view.dart'; +export 'assessment_view.dart'; +export 'landing_view.dart'; +export 'analysis_view.dart'; +export 'base_view.dart'; +export 'styles.dart'; diff --git a/lib/views/components/_components.dart b/lib/views/components/_components.dart new file mode 100644 index 00000000..4263d7ab --- /dev/null +++ b/lib/views/components/_components.dart @@ -0,0 +1,6 @@ +export 'auth_input.dart'; +export 'assessment_card.dart'; +export 'patient_form.dart'; +export 'view_background.dart'; +export 'video_recorder.dart'; +export 'shaker.dart'; From 4c335d3943695830ba21b9af6612da188769ba79 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Fri, 10 Feb 2023 21:29:10 -0500 Subject: [PATCH 21/43] minor additions and organization --- lib/bloc/bloc.dart | 2 -- lib/main.dart | 4 +-- lib/views/assessment_view.dart | 32 ++++++++++++++++++--- lib/views/auth_view.dart | 43 ++++++++++++++-------------- lib/views/base_view.dart | 6 ++-- lib/views/components/auth_input.dart | 4 +-- lib/views/components/components.dart | 6 ---- lib/views/home_view.dart | 4 +-- lib/views/landing_view.dart | 2 +- lib/views/styles.dart | 5 ++++ lib/views/views.dart | 9 ------ pubspec.lock | 16 +++++------ 12 files changed, 73 insertions(+), 60 deletions(-) delete mode 100644 lib/bloc/bloc.dart delete mode 100644 lib/views/components/components.dart delete mode 100644 lib/views/views.dart diff --git a/lib/bloc/bloc.dart b/lib/bloc/bloc.dart deleted file mode 100644 index ed80dc4e..00000000 --- a/lib/bloc/bloc.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'assessment/assessment_bloc.dart'; -export 'auth/auth_bloc.dart'; diff --git a/lib/main.dart b/lib/main.dart index a34036a0..b6cb6db2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,5 @@ import 'package:flutter/cupertino.dart'; -import 'package:visualpt/views/views.dart'; +import 'package:visualpt/views/_views.dart'; void main() { runApp(const VisualPT()); @@ -24,7 +24,7 @@ class VisualPT extends StatelessWidget { textStyle: TextStyle( fontFamily: "Unbounded", color: CupertinoColors.black))), routes: { - "/": (context) => const AuthView(), + "/": (context) => const AuthView(), //TODO make automatic reference based on auth "/auth": (context) => const AuthView(), "/home": (context) => const HomeView(), "/assessment": (context) => const AssessmentView(), diff --git a/lib/views/assessment_view.dart b/lib/views/assessment_view.dart index 8c9cb62c..7586df41 100644 --- a/lib/views/assessment_view.dart +++ b/lib/views/assessment_view.dart @@ -1,13 +1,37 @@ import 'package:flutter/material.dart'; -import 'package:visualpt/views/components/components.dart'; -import 'package:visualpt/views/views.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/views/components/_components.dart'; +import 'package:visualpt/views/_views.dart'; + +import '../bloc/_bloc.dart'; class AssessmentView extends StatelessWidget { const AssessmentView({super.key}); @override Widget build(BuildContext context) { - return const BaseView( - pageTitle: "Performing ", child: VideoRecorder()); + return BaseView( + pageTitle: "Performing ", + child: BlocProvider( + create: (context) => AssessmentBloc(), + child: BlocBuilder( + builder: (context, state) { + return Text("ToDo"); + if (state is AssessmentTutorial) { + } else if (state is AssessmentActive) { + return VideoRecorder(); + } else if (state is AssessmentAborted) { + //Reset the video recorder / + } else if (state is AssessmentPaused) { + //Pause the video recorder and timer / show a modal over the recorder + } else if (state is AssessmentSuccess) { + //Go to PDF + } else { + assert(state is AssessmentError); + } + }, + ), + ), + ); } } diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart index ed827ced..1fbbac3a 100644 --- a/lib/views/auth_view.dart +++ b/lib/views/auth_view.dart @@ -1,9 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:visualpt/views/styles.dart'; -import '../bloc/bloc.dart'; -import 'base_view.dart'; -import 'components/components.dart'; +import 'package:visualpt/bloc/_bloc.dart'; +import 'package:visualpt/views/_views.dart'; +import 'components/_components.dart'; class AuthView extends StatefulWidget { const AuthView({super.key}); @@ -27,7 +26,10 @@ class _AuthViewState extends State { builder: (context, state) { WidgetsBinding.instance.addPostFrameCallback((_) { if (state is AuthSuccess) { - context.read().close(); //TODO save auth data before closing + //Sync with Amplify services + context + .read() + .close(); //TODO save auth data before closing Navigator.popAndPushNamed(context, "/home"); } else if (state is AuthError) { _shakeKey.currentState?.shake(); @@ -52,12 +54,16 @@ class _AuthViewState extends State { children: [ //TODO stylize for ciewability CupertinoButton( - child: const Text("Sign up"), + color: Styles.actionSecondary, + child: const Text( + "Sign up", + style: Styles.subactionText, + ), onPressed: () => addAuthEvent(context, event: AuthToggle()), ), const Padding( padding: EdgeInsets.all(8.0), - child: Text("Access your workflows"), + child: Text("or Access your workflows"), ), const AuthInput(AuthDataType.email), const AuthInput(AuthDataType.password, obscure: true), @@ -66,16 +72,13 @@ class _AuthViewState extends State { child: GestureDetector( child: Container( alignment: Alignment.center, - height: MediaQuery.of(context).size.width / 3, - width: MediaQuery.of(context).size.width / 3, + height: MediaQuery.of(context).size.width / 2, + width: MediaQuery.of(context).size.width / 2, decoration: const BoxDecoration( shape: BoxShape.circle, color: Styles.actionPrimary), child: const Text( "Log In", - style: TextStyle( - color: CupertinoColors.white, - fontSize: 24, - fontWeight: FontWeight.bold), + style: Styles.actionText, ), ), onTap: () => addAuthEvent(context)), @@ -91,12 +94,13 @@ class _AuthViewState extends State { child: Column( children: [ CupertinoButton( - child: const Text("Log in"), + color: Styles.actionSecondary, + child: const Text("Log in", style: Styles.subactionText), onPressed: () => addAuthEvent(context, event: AuthToggle()), ), const Padding( padding: EdgeInsets.all(8.0), - child: Text("Rethink your workflows"), + child: Text("or Rethink your workflows"), ), const AuthInput(AuthDataType.email), const AuthInput(AuthDataType.password, obscure: true), @@ -106,16 +110,13 @@ class _AuthViewState extends State { child: GestureDetector( child: Container( alignment: Alignment.center, - height: MediaQuery.of(context).size.width / 3, - width: MediaQuery.of(context).size.width / 3, + height: MediaQuery.of(context).size.width / 2, + width: MediaQuery.of(context).size.width / 2, decoration: const BoxDecoration( shape: BoxShape.circle, color: Styles.actionPrimary), child: const Text( "Sign Up", - style: TextStyle( - color: CupertinoColors.white, - fontSize: 24, - fontWeight: FontWeight.bold), + style: Styles.actionText, ), ), onTap: () => addAuthEvent(context), diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart index 66cd022b..f4e9bbe4 100644 --- a/lib/views/base_view.dart +++ b/lib/views/base_view.dart @@ -1,5 +1,5 @@ import 'package:flutter/cupertino.dart'; -import 'package:visualpt/views/components/components.dart'; +import 'package:visualpt/views/components/_components.dart'; import 'package:visualpt/views/styles.dart'; class BaseView extends StatelessWidget { @@ -39,9 +39,9 @@ class BaseView extends StatelessWidget { )), //TODO make this better and animatable Positioned( - top: MediaQuery.of(context).size.height / 3, + top: screenSize.height / 3, child: SizedBox( - height: screenSize.height * 15 / 24, + height: screenSize.height * 16 / 24, width: screenSize.width, child: Container(child: child)), ) diff --git a/lib/views/components/auth_input.dart b/lib/views/components/auth_input.dart index d76069ce..be1629d9 100644 --- a/lib/views/components/auth_input.dart +++ b/lib/views/components/auth_input.dart @@ -2,8 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:visualpt/views/styles.dart'; -import '../../bloc/bloc.dart'; - +import '../../bloc/_bloc.dart'; +//TODO this needs to become a formfield class AuthInput extends StatefulWidget { final AuthDataType type; final bool obscure; diff --git a/lib/views/components/components.dart b/lib/views/components/components.dart deleted file mode 100644 index 4263d7ab..00000000 --- a/lib/views/components/components.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'auth_input.dart'; -export 'assessment_card.dart'; -export 'patient_form.dart'; -export 'view_background.dart'; -export 'video_recorder.dart'; -export 'shaker.dart'; diff --git a/lib/views/home_view.dart b/lib/views/home_view.dart index 86b55480..ff92c095 100644 --- a/lib/views/home_view.dart +++ b/lib/views/home_view.dart @@ -1,6 +1,6 @@ import 'package:flutter/cupertino.dart'; -import 'package:visualpt/views/views.dart'; -import 'components/components.dart'; +import 'package:visualpt/views/_views.dart'; +import 'components/_components.dart'; class HomeView extends StatelessWidget { const HomeView({super.key}); diff --git a/lib/views/landing_view.dart b/lib/views/landing_view.dart index de274d83..1a9b72f9 100644 --- a/lib/views/landing_view.dart +++ b/lib/views/landing_view.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:visualpt/views/styles.dart'; import 'base_view.dart'; -import 'components/components.dart'; +import 'components/_components.dart'; //TODO The first view anyone on the app sees. Synonymous with the loading view class LandingView extends StatelessWidget { diff --git a/lib/views/styles.dart b/lib/views/styles.dart index fe5686c9..610a8398 100644 --- a/lib/views/styles.dart +++ b/lib/views/styles.dart @@ -15,6 +15,7 @@ class Styles { static const Color backgroundComplementory = Color(0xFFf8c098); static const Color actionPrimary = Color(0xFF1F7A97); + static const Color actionSecondary = Color(0x551F7A97); //Asset Paths static const String mainLogoPath = "assets/vpt_mainlogo.png"; @@ -29,4 +30,8 @@ class Styles { TextStyle(color: CupertinoColors.inactiveGray, fontSize: 24); static const TextStyle inputStyle = TextStyle(color: CupertinoColors.black, fontSize: 24); + static const TextStyle actionText = TextStyle( + color: CupertinoColors.white, fontSize: 32, fontWeight: FontWeight.bold); + static const TextStyle subactionText = TextStyle( + color: CupertinoColors.white, fontSize: 18, fontWeight: FontWeight.bold); } diff --git a/lib/views/views.dart b/lib/views/views.dart deleted file mode 100644 index 269fe01f..00000000 --- a/lib/views/views.dart +++ /dev/null @@ -1,9 +0,0 @@ -///Barrel File for ALL views. Use this when importing more than 1 view into a file. - -export 'auth_view.dart'; -export 'home_view.dart'; -export 'assessment_view.dart'; -export 'landing_view.dart'; -export 'analysis_view.dart'; -export 'base_view.dart'; -export 'styles.dart'; diff --git a/pubspec.lock b/pubspec.lock index 68575867..f32e9573 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: bloc - sha256: bd4f8027bfa60d96c8046dec5ce74c463b2c918dce1b0d36593575995344534a + sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80" url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.1.1" boolean_selector: dependency: transitive description: @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: camera_platform_interface - sha256: "0eedd642d905ca24f1c483fe9ea0d0e7287b86a402845c28d24df28cc7b0ee6e" + sha256: b632be28e61d00a233f67d98ea90fd7041956f27a1c65500188ee459be60e15f url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.4.0" camera_web: dependency: transitive description: @@ -250,10 +250,10 @@ packages: dependency: "direct main" description: name: flutter_bloc - sha256: "890c51c8007f0182360e523518a0c732efb89876cb4669307af7efada5b55557" + sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775" url: "https://pub.dev" source: hosted - version: "8.1.1" + version: "8.1.2" flutter_lints: dependency: "direct dev" description: @@ -569,10 +569,10 @@ packages: dependency: "direct main" description: name: video_editor - sha256: "7a86c9d154f1355207c88ca19417b2adf6a9cdd142c576ffb4be6d191ec70653" + sha256: fc76db21433345aebc567152cf65468650673124b637dff842ee174216660976 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.0" video_player: dependency: "direct main" description: From 6fba34c55ea893203abdd04a128476bc387d512a Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 11 Feb 2023 22:39:32 -0500 Subject: [PATCH 22/43] polishing thoughts on assessment bloc --- lib/bloc/auth/auth_bloc.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bloc/auth/auth_bloc.dart b/lib/bloc/auth/auth_bloc.dart index aa065af4..1c32f611 100644 --- a/lib/bloc/auth/auth_bloc.dart +++ b/lib/bloc/auth/auth_bloc.dart @@ -1,6 +1,5 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/cupertino.dart'; -import 'package:meta/meta.dart'; part 'auth_event.dart'; part 'auth_state.dart'; @@ -49,7 +48,8 @@ class AuthBloc extends Bloc { } else { final tempState = state; emit(AuthError("Something went wrong, please try again")); - await Future.delayed(const Duration(seconds: 1), () => emit(tempState)); + await Future.delayed( + const Duration(milliseconds: 100), () => emit(tempState)); } } From e1b524189407b79d54069a74ce68409861bfe5cf Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 11 Feb 2023 22:40:13 -0500 Subject: [PATCH 23/43] clean slate --- lib/bloc/assessment/assessment_bloc.dart | 16 ++++++++++++++++ lib/bloc/assessment/assessment_event.dart | 12 ++++++++++++ lib/bloc/assessment/assessment_state.dart | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 lib/bloc/assessment/assessment_bloc.dart create mode 100644 lib/bloc/assessment/assessment_event.dart create mode 100644 lib/bloc/assessment/assessment_state.dart diff --git a/lib/bloc/assessment/assessment_bloc.dart b/lib/bloc/assessment/assessment_bloc.dart new file mode 100644 index 00000000..f1294101 --- /dev/null +++ b/lib/bloc/assessment/assessment_bloc.dart @@ -0,0 +1,16 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +part 'assessment_event.dart'; +part 'assessment_state.dart'; + +class AssessmentBloc extends Bloc { + late List assessmentAssets; + AssessmentBloc() : super(AssessmentTutorial()) { + on((event, emit) { + // TODO: implement event handler + }); + } +} diff --git a/lib/bloc/assessment/assessment_event.dart b/lib/bloc/assessment/assessment_event.dart new file mode 100644 index 00000000..a5843ad2 --- /dev/null +++ b/lib/bloc/assessment/assessment_event.dart @@ -0,0 +1,12 @@ +part of 'assessment_bloc.dart'; + +@immutable +abstract class AssessmentEvent {} + +class AssessmentIntroduce extends AssessmentEvent {} + +class AssessmentBegin extends AssessmentEvent {} + +class AssessmentIncrement extends AssessmentEvent {} + +class AssessmentFinish extends AssessmentEvent {} \ No newline at end of file diff --git a/lib/bloc/assessment/assessment_state.dart b/lib/bloc/assessment/assessment_state.dart new file mode 100644 index 00000000..72bb14a4 --- /dev/null +++ b/lib/bloc/assessment/assessment_state.dart @@ -0,0 +1,18 @@ +part of 'assessment_bloc.dart'; + +@immutable +abstract class AssessmentState {} + +class AssessmentTutorial extends AssessmentState {} + +class AssessmentActive extends AssessmentState {} + +class AssessmentPaused extends AssessmentState {} + +class AssessmentAborted extends AssessmentState {} + +class AssessmentSuccess extends AssessmentState {} + +class AssessmentError extends AssessmentState { + final String = "hello"; +} From 9ea81c593726b8ba3a1d4160432cf28433187748 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Mon, 13 Feb 2023 00:53:12 -0500 Subject: [PATCH 24/43] firm assessment/camera state managment groundwork --- lib/bloc/assessment/assessment_bloc.dart | 28 +++- lib/bloc/assessment/assessment_event.dart | 10 +- lib/bloc/assessment/assessment_state.dart | 35 ++++- lib/bloc/camera/camera_bloc.dart | 113 ++++++++++++++++ lib/bloc/camera/camera_event.dart | 12 ++ lib/bloc/camera/camera_state.dart | 25 ++++ lib/main.dart | 19 ++- lib/views/assessment_view.dart | 59 ++++++--- lib/views/assessments/_assessments.dart | 25 ++++ lib/views/assessments/ctsib.dart | 23 ++++ lib/views/assessments/default.dart | 13 ++ lib/views/assessments/gait.dart | 17 +++ lib/views/components/assessment_card.dart | 8 +- lib/views/components/patient_form.dart | 8 +- lib/views/components/video_recorder.dart | 153 +++++++++++++++++----- lib/views/home_view.dart | 5 +- 16 files changed, 479 insertions(+), 74 deletions(-) create mode 100644 lib/bloc/camera/camera_bloc.dart create mode 100644 lib/bloc/camera/camera_event.dart create mode 100644 lib/bloc/camera/camera_state.dart create mode 100644 lib/views/assessments/_assessments.dart create mode 100644 lib/views/assessments/ctsib.dart create mode 100644 lib/views/assessments/default.dart create mode 100644 lib/views/assessments/gait.dart diff --git a/lib/bloc/assessment/assessment_bloc.dart b/lib/bloc/assessment/assessment_bloc.dart index f1294101..ed1997df 100644 --- a/lib/bloc/assessment/assessment_bloc.dart +++ b/lib/bloc/assessment/assessment_bloc.dart @@ -1,16 +1,38 @@ import 'dart:io'; - import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:helpers/helpers.dart'; +import 'package:visualpt/bloc/camera/camera_bloc.dart'; +import 'package:visualpt/models/ModelProvider.dart'; part 'assessment_event.dart'; part 'assessment_state.dart'; class AssessmentBloc extends Bloc { + final AssessmentType assessmentType; + final List assessmentViews; late List assessmentAssets; - AssessmentBloc() : super(AssessmentTutorial()) { - on((event, emit) { + late List skeletalSequence; + final DateTime assessmentStart = DateTime.now(); + late DateTime assessmentEnd; + late int index; + + AssessmentBloc(this.assessmentType, this.assessmentViews) + : super(AssessmentIntro(assessmentViews[0], 0)) { + on((event, emit) {}); + on((event, emit) { + // TODO: implement event handler + }); + on((event, emit) { // TODO: implement event handler }); + on((event, emit) { + // TODO: implement event handler + }); + } + + //Store persistent bloc data + void load() { + index = 0; } } diff --git a/lib/bloc/assessment/assessment_event.dart b/lib/bloc/assessment/assessment_event.dart index a5843ad2..239f9e02 100644 --- a/lib/bloc/assessment/assessment_event.dart +++ b/lib/bloc/assessment/assessment_event.dart @@ -3,10 +3,14 @@ part of 'assessment_bloc.dart'; @immutable abstract class AssessmentEvent {} -class AssessmentIntroduce extends AssessmentEvent {} +class AssessmentInit extends AssessmentEvent {} class AssessmentBegin extends AssessmentEvent {} -class AssessmentIncrement extends AssessmentEvent {} +class AssessmentIncrement extends AssessmentEvent { + final File assessmentData; -class AssessmentFinish extends AssessmentEvent {} \ No newline at end of file + AssessmentIncrement(this.assessmentData); +} + +class AssessmentFinish extends AssessmentEvent {} diff --git a/lib/bloc/assessment/assessment_state.dart b/lib/bloc/assessment/assessment_state.dart index 72bb14a4..6d04cb71 100644 --- a/lib/bloc/assessment/assessment_state.dart +++ b/lib/bloc/assessment/assessment_state.dart @@ -3,16 +3,39 @@ part of 'assessment_bloc.dart'; @immutable abstract class AssessmentState {} -class AssessmentTutorial extends AssessmentState {} +class AssessmentForm extends AssessmentState { + final Widget assessmentView; + final int index; + final AssessmentType assessmentType; -class AssessmentActive extends AssessmentState {} + AssessmentForm(this.assessmentView, this.index, this.assessmentType); +} + +class AssessmentIntro extends AssessmentState { + final Widget assessmentView; + final int index; + + AssessmentIntro(this.assessmentView, this.index); +} -class AssessmentPaused extends AssessmentState {} +class AssessmentActive extends AssessmentState { + final Widget assessmentView; + final int index; -class AssessmentAborted extends AssessmentState {} + AssessmentActive(this.assessmentView, this.index); +} + +class AssessmentSuccess extends AssessmentState { + final Widget assessmentView; + final List assessmentAssets; + final int index; -class AssessmentSuccess extends AssessmentState {} + AssessmentSuccess(this.assessmentView, this.assessmentAssets, this.index); +} class AssessmentError extends AssessmentState { - final String = "hello"; + //TODO Establish VisualPT Error Codes + final Exception e; + + AssessmentError(this.e); } diff --git a/lib/bloc/camera/camera_bloc.dart b/lib/bloc/camera/camera_bloc.dart new file mode 100644 index 00000000..864b57ea --- /dev/null +++ b/lib/bloc/camera/camera_bloc.dart @@ -0,0 +1,113 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:camera/camera.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +part 'camera_event.dart'; +part 'camera_state.dart'; + +class CameraBloc extends Bloc { + CameraController? cameraController; + Timer? timer; + Duration duration = const Duration(seconds: 0); + CameraBloc() : super(CameraLoading()) { + on(_cameraInit); + on(_cameraPlay); + on(_cameraPause); + on(_cameraSave); + } + + void resetTimer() { + timer?.cancel(); + duration = const Duration(seconds: 0); + } + + // void triggerState(BuildContext context, CameraState state) async { + // try { + // if (state is CameraRecording) { + // resetTimer(); + // XFile file = await state.controller.stopVideoRecording(); + // CupertinoPageRoute route = CupertinoPageRoute( + // fullscreenDialog: true, + // builder: (_) => EditorPage(file: File(file.path))); + // Navigator.push(context, route); + // initCamera(); + // } else if (state is CameraStandby) { + // startTimer(state.controller); + // state.controller.startVideoRecording(); + // } + // } on Exception catch (e) { + // resetTimer(); + // emit(CameraError(exception: e)); + // } + // } + + // void resetCamera() async { + // emit(CameraLoading()); + // initCamera(); + // } + + FutureOr _cameraInit( + CameraInit event, Emitter emit) async { + try { + getCamera(); + emit(CameraStandby(controller: cameraController!)); + } on CameraException catch (e) { + switch (e.code) { + case 'CameraAccessDenied': + print('You have denied camera access.'); + break; + case 'CameraAccessDeniedWithoutPrompt': + print('Please go to Settings app to enable camera access.'); + break; + case 'CameraAccessRestricted': + print('Camera access is restricted.'); + break; + case 'AudioAccessDenied': + print('You have denied audio access.'); + break; + case 'AudioAccessDeniedWithoutPrompt': + print('Please go to Settings app to enable audio access.'); + break; + case 'AudioAccessRestricted': + print('Audio access is restricted.'); + break; + default: + emit(CameraError(exception: e)); + break; + } + } + } + + FutureOr _cameraPlay(CameraPlay event, Emitter emit) { + timer = Timer.periodic(const Duration(milliseconds: 10), (_) { + duration = Duration(milliseconds: duration.inMilliseconds + 10); + + emit(CameraRecording(controller: cameraController!, duration: duration)); + }); + } + + FutureOr _cameraPause(CameraPause event, Emitter emit) {} + + FutureOr _cameraSave( + CameraSave event, Emitter emit) async { + final XFile data = await cameraController!.stopVideoRecording(); + cameraController!.dispose(); + getCamera(); + emit(CameraStandby(controller: cameraController!)); + } + + Future getCamera() async { + final camera = await availableCameras().then((cameras) => + cameras.firstWhere( + (camera) => camera.lensDirection == CameraLensDirection.back)); + cameraController = CameraController( + camera, + ResolutionPreset.max, + enableAudio: false, + imageFormatGroup: ImageFormatGroup.bgra8888, + ); + await cameraController!.initialize(); + } +} diff --git a/lib/bloc/camera/camera_event.dart b/lib/bloc/camera/camera_event.dart new file mode 100644 index 00000000..3941f351 --- /dev/null +++ b/lib/bloc/camera/camera_event.dart @@ -0,0 +1,12 @@ +part of 'camera_bloc.dart'; + +@immutable +abstract class CameraEvent {} + +class CameraInit extends CameraEvent {} + +class CameraPlay extends CameraEvent {} + +class CameraPause extends CameraEvent {} + +class CameraSave extends CameraEvent {} diff --git a/lib/bloc/camera/camera_state.dart b/lib/bloc/camera/camera_state.dart new file mode 100644 index 00000000..4569a447 --- /dev/null +++ b/lib/bloc/camera/camera_state.dart @@ -0,0 +1,25 @@ +part of 'camera_bloc.dart'; + +@immutable +abstract class CameraState {} + +class CameraLoading extends CameraState {} + +class CameraRecording extends CameraState { + final IconData icon = CupertinoIcons.square; + final CameraController controller; + final Duration duration; + CameraRecording({required this.controller, required this.duration}); +} + +class CameraStandby extends CameraState { + final IconData icon = CupertinoIcons.circle; + final CameraController controller; + CameraStandby({required this.controller}); +} + +class CameraError extends CameraState { + final IconData icon = CupertinoIcons.restart; + final Exception exception; + CameraError({required this.exception}); +} diff --git a/lib/main.dart b/lib/main.dart index b6cb6db2..378ef4ae 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,7 @@ import 'package:flutter/cupertino.dart'; +import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/_views.dart'; +import 'package:visualpt/views/assessments/_assessments.dart'; void main() { runApp(const VisualPT()); @@ -24,13 +26,24 @@ class VisualPT extends StatelessWidget { textStyle: TextStyle( fontFamily: "Unbounded", color: CupertinoColors.black))), routes: { - "/": (context) => const AuthView(), //TODO make automatic reference based on auth + "/": (context) => + const AuthView(), //TODO make automatic reference based on auth "/auth": (context) => const AuthView(), "/home": (context) => const HomeView(), - "/assessment": (context) => const AssessmentView(), "/analysis": (context) => const AnalysisView(), }, + onGenerateRoute: (settings) { + if (settings.name == "/assessment") { + final assessmentType = settings.arguments as AssessmentType; + return CupertinoPageRoute( + builder: (_) => AssessmentView( + assessmentType: assessmentType, + assessmentViews: AssessmentRouter(assessmentType).assessmentRoute, + ), + ); + } + return null; + }, ); } } - \ No newline at end of file diff --git a/lib/views/assessment_view.dart b/lib/views/assessment_view.dart index 7586df41..73ded53e 100644 --- a/lib/views/assessment_view.dart +++ b/lib/views/assessment_view.dart @@ -1,36 +1,53 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:visualpt/views/components/_components.dart'; +import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/_views.dart'; +import 'package:visualpt/views/components/_components.dart'; import '../bloc/_bloc.dart'; class AssessmentView extends StatelessWidget { - const AssessmentView({super.key}); + final AssessmentType assessmentType; + final List assessmentViews; + const AssessmentView( + {super.key, required this.assessmentType, required this.assessmentViews}); @override Widget build(BuildContext context) { - return BaseView( - pageTitle: "Performing ", - child: BlocProvider( - create: (context) => AssessmentBloc(), - child: BlocBuilder( - builder: (context, state) { - return Text("ToDo"); - if (state is AssessmentTutorial) { - } else if (state is AssessmentActive) { - return VideoRecorder(); - } else if (state is AssessmentAborted) { - //Reset the video recorder / - } else if (state is AssessmentPaused) { - //Pause the video recorder and timer / show a modal over the recorder - } else if (state is AssessmentSuccess) { - //Go to PDF + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => AssessmentBloc(assessmentType, assessmentViews), + ), + BlocProvider( + create: (context) => CameraBloc(), + ) + ], + child: BlocBuilder( + builder: (context, state) { + try { + if (state is AssessmentForm) {} + if (state is AssessmentIntro) { + return BaseView( + pageTitle: "Performing ${assessmentType.name}", + child: state.assessmentView); + } + if (state is AssessmentActive) { + return VideoRecorder(onCaptureSaved: (File f) => {}); + } + if (state is AssessmentSuccess) { + return BaseView( + pageTitle: "Finished ${assessmentType.name}", + child: state.assessmentView); } else { - assert(state is AssessmentError); + throw Exception("Undefined State"); } - }, - ), + } on Exception catch (e) { + return BaseView(pageTitle: "Error", child: Text(e.toString())); + } + }, ), ); } diff --git a/lib/views/assessments/_assessments.dart b/lib/views/assessments/_assessments.dart new file mode 100644 index 00000000..a4009726 --- /dev/null +++ b/lib/views/assessments/_assessments.dart @@ -0,0 +1,25 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/models/ModelProvider.dart'; + +import 'package:visualpt/views/assessments/ctsib.dart'; +import 'package:visualpt/views/assessments/gait.dart'; + +export 'ctsib.dart'; +export 'gait.dart'; + +class AssessmentRouter { + final AssessmentType assessmentType; + late List assessmentRoute; + + AssessmentRouter(this.assessmentType) { + switch (assessmentType) { + case AssessmentType.CTSIB: + assessmentRoute = Ctsib().content; + break; + case AssessmentType.GAIT: + assessmentRoute = Gait().content; + break; + default: + } + } +} diff --git a/lib/views/assessments/ctsib.dart b/lib/views/assessments/ctsib.dart new file mode 100644 index 00000000..5a2282ab --- /dev/null +++ b/lib/views/assessments/ctsib.dart @@ -0,0 +1,23 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/_views.dart'; +import 'package:visualpt/views/components/_components.dart'; + +class Ctsib { + List get content { + return [ + IntroView("Eyes Open, Firm Surface"), + IntroView("Eyes Closed, Firm Surface"), + IntroView("Eyes Open, Foam Surface"), + IntroView("Eyes Closed, Foam Surface"), + ]; + } +} + +Widget IntroView(String title) => Column( + children: [ + Text(title), + const Text("Ensure device is kept still"), + const Text("Record all tests from same relative posiotion to patient"), + const Text("If error is made, press retake") + ], + ); diff --git a/lib/views/assessments/default.dart b/lib/views/assessments/default.dart new file mode 100644 index 00000000..53ba75ac --- /dev/null +++ b/lib/views/assessments/default.dart @@ -0,0 +1,13 @@ +import 'package:flutter/cupertino.dart'; + +class Default { + List get content { + return [ + Text("DEFAULT INTRO"), + Text("Active"), + Text("DEFAULT INTRO 2"), + Text("Active2") + // TODO: implement content + ]; + } +} diff --git a/lib/views/assessments/gait.dart b/lib/views/assessments/gait.dart new file mode 100644 index 00000000..b6dd1cc7 --- /dev/null +++ b/lib/views/assessments/gait.dart @@ -0,0 +1,17 @@ +import 'package:flutter/cupertino.dart'; + +class Gait { + List get content { + return [ + Text("GAIT INTRO"), + Text("Active"), + Text("GAIT INTRO 2"), + Text("Active2") + // TODO: implement content + ]; + } + + Widget IntroSeq1() => Text("TODO"); + + Widget CaptureSeq1() => Text("TODO"); +} diff --git a/lib/views/components/assessment_card.dart b/lib/views/components/assessment_card.dart index 2275a18e..1740b2aa 100644 --- a/lib/views/components/assessment_card.dart +++ b/lib/views/components/assessment_card.dart @@ -1,15 +1,16 @@ import 'package:flutter/cupertino.dart'; +import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/components/patient_form.dart'; class AssessmentCard extends StatelessWidget { final String imagePath; - final String title; + final AssessmentType assessmentType; final String subtitle; const AssessmentCard({ Key? key, required this.imagePath, - required this.title, + required this.assessmentType, required this.subtitle, }) : super(key: key); @@ -18,6 +19,7 @@ class AssessmentCard extends StatelessWidget { return Padding( padding: const EdgeInsets.all(8.0), child: PatientForm( + assessmentType: assessmentType, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: MediaQuery.of(context).size.height / 4, @@ -38,7 +40,7 @@ class AssessmentCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(title, + Text(assessmentType.name, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 32)), Text(subtitle, style: const TextStyle(fontSize: 24)), diff --git a/lib/views/components/patient_form.dart b/lib/views/components/patient_form.dart index 1895c744..a792cc7e 100644 --- a/lib/views/components/patient_form.dart +++ b/lib/views/components/patient_form.dart @@ -5,8 +5,11 @@ import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/styles.dart'; class PatientForm extends StatefulWidget { + final AssessmentType assessmentType; final Widget child; - const PatientForm({Key? key, required this.child}) : super(key: key); + const PatientForm( + {Key? key, required this.assessmentType, required this.child}) + : super(key: key); @override State createState() => _PatientFormState(); @@ -67,7 +70,8 @@ class _PatientFormState extends State { _formKey.currentState!.save(); Navigator.pop(context); //TODO Specify assessment type argument - Navigator.pushNamed(context, "/assessment"); + Navigator.pushNamed(context, "/assessment", + arguments: widget.assessmentType); } }, child: const Text("Next"), diff --git a/lib/views/components/video_recorder.dart b/lib/views/components/video_recorder.dart index 3dee842a..0acfc11e 100644 --- a/lib/views/components/video_recorder.dart +++ b/lib/views/components/video_recorder.dart @@ -1,38 +1,129 @@ +import 'dart:io'; + import 'package:camera/camera.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:visualpt/bloc/camera_cubit.dart'; +import 'package:visualpt/bloc/_bloc.dart'; + +class VideoRecorder extends StatefulWidget { + final CameraController? controller; + final void Function(File f) onCaptureSaved; + const VideoRecorder({Key? key, this.controller, required this.onCaptureSaved}) + : super(key: key); + + @override + State createState() => _VideoRecorderState(); +} + +class _VideoRecorderState extends State + with WidgetsBindingObserver { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + // #docregion AppLifecycle + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = widget.controller; -class VideoRecorder extends StatelessWidget { - const VideoRecorder({Key? key}) : super(key: key); + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; + } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + // onNewCameraSelected(cameraController.description); + } + } + + void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + if (widget.controller == null) { + return; + } + + final CameraController cameraController = widget.controller!; + + final Offset offset = Offset( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + cameraController.setExposurePoint(offset); + cameraController.setFocusPoint(offset); + } + + // Future onNewCameraSelected(CameraDescription cameraDescription) async { + // final CameraController? oldController = widget.controller; + // if (oldController != null) { + // // `controller` needs to be set to null before getting disposed, + // // to avoid a race condition when we use the controller that is being + // // disposed. This happens when camera permission dialog shows up, + // // which triggers `didChangeAppLifecycleState`, which disposes and + // // re-creates the controller. + // widget.controller = null; + // await oldController.dispose(); + // } + + // final CameraController cameraController = CameraController( + // cameraDescription, + // ResolutionPreset.max, + // ); + + // widget.controller = cameraController; + + // // If the controller is updated then update the UI. + // cameraController.addListener(() { + // if (mounted) { + // setState(() {}); + // } + // if (cameraController.value.hasError) { + // print( + // 'Camera error ${cameraController.value.errorDescription}'); + // } + // }); + + // if (mounted) { + // setState(() {}); + // } + // } @override Widget build(BuildContext context) { //TODO Put all this blocProvider jumble in the bloc folder - return BlocProvider( - create: (context) => CameraCubit()..initCamera(), - child: BlocBuilder( - builder: (context, state) { - return Container( - height: MediaQuery.of(context).size.height * 0.8, - width: MediaQuery.of(context).size.width * 0.8, - decoration: BoxDecoration(border: Border.all()), - child: state is CameraError - ? Center( - child: Column( - children: [ - Text(state.exception.toString()), - CupertinoButton( - onPressed: () { - BlocProvider.of(context) - .resetCamera(); - }, - child: const Text("Retry")) - ], - ), - ) - : cameraWidget(context, state)); - }, + return Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration(border: Border.all()), + child: BlocProvider( + create: (context) => CameraBloc()..add(CameraInit()), + child: BlocBuilder( + builder: (context, state) { + return state is CameraError + ? Center( + child: Column( + children: [ + Text(state.exception.toString()), + CupertinoButton( + onPressed: () { + // BlocProvider.of(context) + // .resetCamera(); + }, + child: const Text("Retry")) + ], + ), + ) + : cameraWidget(context, state); + }, + ), ), ); } @@ -74,8 +165,8 @@ class VideoRecorder extends StatelessWidget { Positioned( bottom: 10, child: GestureDetector( - onTap: () => BlocProvider.of(context) - .triggerState(context, state), + onTap: () => + BlocProvider.of(context).add(CameraSave()), child: const Icon( CupertinoIcons.stop, color: CupertinoColors.systemRed, @@ -99,8 +190,8 @@ class VideoRecorder extends StatelessWidget { Positioned( bottom: 10, child: GestureDetector( - onTap: () => BlocProvider.of(context) - .triggerState(context, state), + // onTap: () => BlocProvider.of(context) + // .triggerState(context, state), child: const Icon( CupertinoIcons.circle, color: CupertinoColors.black, diff --git a/lib/views/home_view.dart b/lib/views/home_view.dart index ff92c095..6ee8eb4e 100644 --- a/lib/views/home_view.dart +++ b/lib/views/home_view.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/_views.dart'; import 'components/_components.dart'; @@ -15,11 +16,11 @@ class HomeView extends StatelessWidget { children: const [ AssessmentCard( imagePath: Styles.ctsibLogoPath, - title: "CT-SIB", + assessmentType: AssessmentType.CTSIB, subtitle: "3 minutes"), AssessmentCard( imagePath: Styles.gvLogoPath, - title: "Gait Velocity", + assessmentType: AssessmentType.GAIT, subtitle: "5 minutes"), ], ), From 80c57f68ad770dfed34300e906b74c5cc32cf186 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:03:10 -0500 Subject: [PATCH 25/43] assessment bloc 90% integrating data recorder bloc --- lib/bloc/_bloc.dart | 1 - lib/bloc/assessment/assessment_bloc.dart | 70 +++++++--- lib/bloc/assessment/assessment_event.dart | 8 +- lib/bloc/assessment/assessment_state.dart | 14 +- lib/bloc/camera/camera_bloc.dart | 38 ++++-- lib/main.dart | 9 +- lib/views/assessment_view.dart | 23 ++-- lib/views/assessments/_assessments.dart | 32 +++-- lib/views/assessments/ctsib.dart | 64 +++++++-- lib/views/assessments/default.dart | 13 -- lib/views/assessments/gait.dart | 8 +- lib/views/base_view.dart | 2 +- lib/views/components/assessment_card.dart | 6 +- lib/views/components/patient_form.dart | 154 +++++++++++----------- lib/views/components/video_recorder.dart | 33 +++-- 15 files changed, 278 insertions(+), 197 deletions(-) delete mode 100644 lib/views/assessments/default.dart diff --git a/lib/bloc/_bloc.dart b/lib/bloc/_bloc.dart index 98c64ef5..7b863fdd 100644 --- a/lib/bloc/_bloc.dart +++ b/lib/bloc/_bloc.dart @@ -1,4 +1,3 @@ export 'assessment/assessment_bloc.dart'; export 'auth/auth_bloc.dart'; -export 'form/form_bloc.dart'; export 'camera/camera_bloc.dart'; diff --git a/lib/bloc/assessment/assessment_bloc.dart b/lib/bloc/assessment/assessment_bloc.dart index ed1997df..97237d90 100644 --- a/lib/bloc/assessment/assessment_bloc.dart +++ b/lib/bloc/assessment/assessment_bloc.dart @@ -1,38 +1,66 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:helpers/helpers.dart'; -import 'package:visualpt/bloc/camera/camera_bloc.dart'; import 'package:visualpt/models/ModelProvider.dart'; +import 'package:visualpt/views/assessments/_assessments.dart'; part 'assessment_event.dart'; part 'assessment_state.dart'; class AssessmentBloc extends Bloc { final AssessmentType assessmentType; - final List assessmentViews; + late int assessmentCount; + + final BuildContext context; + late List introViews; + late List captureViews; + late List assessmentAssets; - late List skeletalSequence; - final DateTime assessmentStart = DateTime.now(); - late DateTime assessmentEnd; + late List skeletalSequences; + + late DateTime assessmentStart; + late Duration assessmentDuration; late int index; - AssessmentBloc(this.assessmentType, this.assessmentViews) - : super(AssessmentIntro(assessmentViews[0], 0)) { - on((event, emit) {}); - on((event, emit) { - // TODO: implement event handler - }); - on((event, emit) { - // TODO: implement event handler - }); - on((event, emit) { - // TODO: implement event handler - }); - } + AssessmentBloc(this.context, this.assessmentType) + : super(AssessmentForm(assessmentType)) { + on(_onInit); + on(_onBegin); + on(_onIncrement); - //Store persistent bloc data - void load() { + assessmentStart = DateTime.now(); index = 0; + final assessmentRouter = AssessmentRouter(assessmentType); + assessmentCount = assessmentRouter.assessmentCount; + introViews = assessmentRouter.introWidgets; + captureViews = assessmentRouter.captureWidgets; + } + + FutureOr _onInit(AssessmentInit event, Emitter emit) { + emit(AssessmentIntro(index)); } + + FutureOr _onBegin( + AssessmentBegin event, Emitter emit) { + emit(AssessmentActive(index)); + } + + FutureOr _onIncrement( + AssessmentIncrement event, Emitter emit) { + assessmentAssets.add(event.assessmentData); + index++; + if (index == assessmentCount) { + assessmentDuration = assessmentStart.difference(DateTime.now()); + emit(AssessmentSuccess(assessmentAssets, index)); + } else { + emit(AssessmentIntro(index)); + } + } + + Widget getIntroView(BuildContext context, int index) => + introViews[index](context); + + Widget getCaptureView(BuildContext context, int index) => + captureViews[index](context); } diff --git a/lib/bloc/assessment/assessment_event.dart b/lib/bloc/assessment/assessment_event.dart index 239f9e02..43c1fd4f 100644 --- a/lib/bloc/assessment/assessment_event.dart +++ b/lib/bloc/assessment/assessment_event.dart @@ -3,7 +3,11 @@ part of 'assessment_bloc.dart'; @immutable abstract class AssessmentEvent {} -class AssessmentInit extends AssessmentEvent {} +class AssessmentInit extends AssessmentEvent { + final int index; + + AssessmentInit(this.index); +} class AssessmentBegin extends AssessmentEvent {} @@ -12,5 +16,3 @@ class AssessmentIncrement extends AssessmentEvent { AssessmentIncrement(this.assessmentData); } - -class AssessmentFinish extends AssessmentEvent {} diff --git a/lib/bloc/assessment/assessment_state.dart b/lib/bloc/assessment/assessment_state.dart index 6d04cb71..18bc1140 100644 --- a/lib/bloc/assessment/assessment_state.dart +++ b/lib/bloc/assessment/assessment_state.dart @@ -4,33 +4,27 @@ part of 'assessment_bloc.dart'; abstract class AssessmentState {} class AssessmentForm extends AssessmentState { - final Widget assessmentView; - final int index; final AssessmentType assessmentType; - - AssessmentForm(this.assessmentView, this.index, this.assessmentType); + AssessmentForm(this.assessmentType); } class AssessmentIntro extends AssessmentState { - final Widget assessmentView; final int index; - AssessmentIntro(this.assessmentView, this.index); + AssessmentIntro(this.index); } class AssessmentActive extends AssessmentState { - final Widget assessmentView; final int index; - AssessmentActive(this.assessmentView, this.index); + AssessmentActive(this.index); } class AssessmentSuccess extends AssessmentState { - final Widget assessmentView; final List assessmentAssets; final int index; - AssessmentSuccess(this.assessmentView, this.assessmentAssets, this.index); + AssessmentSuccess(this.assessmentAssets, this.index); } class AssessmentError extends AssessmentState { diff --git a/lib/bloc/camera/camera_bloc.dart b/lib/bloc/camera/camera_bloc.dart index 864b57ea..76a5736e 100644 --- a/lib/bloc/camera/camera_bloc.dart +++ b/lib/bloc/camera/camera_bloc.dart @@ -51,8 +51,10 @@ class CameraBloc extends Bloc { FutureOr _cameraInit( CameraInit event, Emitter emit) async { try { - getCamera(); - emit(CameraStandby(controller: cameraController!)); + await getCamera() + .then((_) => emit(CameraStandby(controller: cameraController!))) + .onError((error, stackTrace) => + emit(CameraError(exception: Exception(error)))); } on CameraException catch (e) { switch (e.code) { case 'CameraAccessDenied': @@ -77,6 +79,8 @@ class CameraBloc extends Bloc { emit(CameraError(exception: e)); break; } + } on Exception catch (e) { + emit(CameraError(exception: e)); } } @@ -95,19 +99,27 @@ class CameraBloc extends Bloc { final XFile data = await cameraController!.stopVideoRecording(); cameraController!.dispose(); getCamera(); - emit(CameraStandby(controller: cameraController!)); } Future getCamera() async { - final camera = await availableCameras().then((cameras) => - cameras.firstWhere( - (camera) => camera.lensDirection == CameraLensDirection.back)); - cameraController = CameraController( - camera, - ResolutionPreset.max, - enableAudio: false, - imageFormatGroup: ImageFormatGroup.bgra8888, - ); - await cameraController!.initialize(); + try { + await availableCameras() + .then((cameras) => cameras.firstWhere( + (camera) => camera.lensDirection == CameraLensDirection.back)) + .then((camera) => CameraController( + camera, + ResolutionPreset.max, + enableAudio: false, + imageFormatGroup: ImageFormatGroup.bgra8888, + )) + .then((controller) { + controller.initialize(); + return controller; + }) + .then((controller) => cameraController = controller) + .onError((error, stackTrace) => throw Exception(error)); + } on Exception catch (e) { + emit(CameraError(exception: e)); + } } } diff --git a/lib/main.dart b/lib/main.dart index 378ef4ae..a2a21923 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/_views.dart'; -import 'package:visualpt/views/assessments/_assessments.dart'; void main() { runApp(const VisualPT()); @@ -36,13 +35,9 @@ class VisualPT extends StatelessWidget { if (settings.name == "/assessment") { final assessmentType = settings.arguments as AssessmentType; return CupertinoPageRoute( - builder: (_) => AssessmentView( - assessmentType: assessmentType, - assessmentViews: AssessmentRouter(assessmentType).assessmentRoute, - ), - ); + builder: (context) => + AssessmentView(assessmentType: assessmentType)); } - return null; }, ); } diff --git a/lib/views/assessment_view.dart b/lib/views/assessment_view.dart index 73ded53e..0009a758 100644 --- a/lib/views/assessment_view.dart +++ b/lib/views/assessment_view.dart @@ -4,22 +4,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/_views.dart'; +import 'package:visualpt/views/assessments/_assessments.dart'; import 'package:visualpt/views/components/_components.dart'; import '../bloc/_bloc.dart'; class AssessmentView extends StatelessWidget { final AssessmentType assessmentType; - final List assessmentViews; - const AssessmentView( - {super.key, required this.assessmentType, required this.assessmentViews}); + const AssessmentView({super.key, required this.assessmentType}); @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider( - create: (context) => AssessmentBloc(assessmentType, assessmentViews), + create: (context) => AssessmentBloc(context, assessmentType), ), BlocProvider( create: (context) => CameraBloc(), @@ -28,19 +27,25 @@ class AssessmentView extends StatelessWidget { child: BlocBuilder( builder: (context, state) { try { - if (state is AssessmentForm) {} + if (state is AssessmentForm) { + return PatientForm(assessmentType: assessmentType); + //return PatientForm(assessmentType: assessmentType,child: ) + } if (state is AssessmentIntro) { return BaseView( pageTitle: "Performing ${assessmentType.name}", - child: state.assessmentView); + child: BlocProvider.of(context) + .getIntroView(context, state.index)); } if (state is AssessmentActive) { - return VideoRecorder(onCaptureSaved: (File f) => {}); + return BlocProvider.of(context) + .getCaptureView(context, state.index); } if (state is AssessmentSuccess) { return BaseView( - pageTitle: "Finished ${assessmentType.name}", - child: state.assessmentView); + pageTitle: "Finished ${assessmentType.name}", + child: Placeholder(), + ); } else { throw Exception("Undefined State"); } diff --git a/lib/views/assessments/_assessments.dart b/lib/views/assessments/_assessments.dart index a4009726..3e7b3786 100644 --- a/lib/views/assessments/_assessments.dart +++ b/lib/views/assessments/_assessments.dart @@ -9,17 +9,31 @@ export 'gait.dart'; class AssessmentRouter { final AssessmentType assessmentType; - late List assessmentRoute; + + late List introWidgets; + late List captureWidgets; + + late int assessmentCount; AssessmentRouter(this.assessmentType) { - switch (assessmentType) { - case AssessmentType.CTSIB: - assessmentRoute = Ctsib().content; - break; - case AssessmentType.GAIT: - assessmentRoute = Gait().content; - break; - default: + try { + switch (assessmentType) { + case AssessmentType.CTSIB: + introWidgets = Ctsib().introContent(); + captureWidgets = Ctsib().captureContent(); + + assessmentCount = Ctsib().sequences; + break; + case AssessmentType.GAIT: + //TODO assessmentWidgets = Gait().content(); + assessmentCount = Ctsib().sequences; + + break; + default: + throw Exception("Invalid Assessment Type"); + } + } catch (e) { + //TODO Dont progress to the assessment page } } } diff --git a/lib/views/assessments/ctsib.dart b/lib/views/assessments/ctsib.dart index 5a2282ab..37b052eb 100644 --- a/lib/views/assessments/ctsib.dart +++ b/lib/views/assessments/ctsib.dart @@ -1,23 +1,63 @@ +import 'dart:io'; + import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/bloc/_bloc.dart'; import 'package:visualpt/views/_views.dart'; import 'package:visualpt/views/components/_components.dart'; class Ctsib { - List get content { + final sequences = 4; + + List introContent() { + return [ + (context) => introView(context, "Eyes Open,\n Firm Surface", 1), + (context) => introView(context, "Eyes Closed,\n Firm Surface", 2), + (context) => introView(context, "Eyes Open,\n Foam Surface", 3), + (context) => introView(context, "Eyes Closed,\n Foam Surface", 4), + ]; + } + + List captureContent() { return [ - IntroView("Eyes Open, Firm Surface"), - IntroView("Eyes Closed, Firm Surface"), - IntroView("Eyes Open, Foam Surface"), - IntroView("Eyes Closed, Foam Surface"), + (context) => captureView(context, "Eyes Open,\n Firm Surface", 1), + (context) => captureView(context, "Eyes Closed,\n Firm Surface", 2), + (context) => captureView(context, "Eyes Open,\n Foam Surface", 3), + (context) => captureView(context, "Eyes Closed,\n Foam Surface", 4), ]; } } -Widget IntroView(String title) => Column( - children: [ - Text(title), - const Text("Ensure device is kept still"), - const Text("Record all tests from same relative posiotion to patient"), - const Text("If error is made, press retake") - ], +Widget introView(BuildContext context, String title, int index) => Builder( + builder: (context) => Column( + children: [ + Text(title, style: Styles.defaultTitleOverride), + Text(index.toString()), + const Text("Ensure device is kept still"), + const Text( + "Record all tests from same relative posiotion to patient"), + const Text("If error is made, press retake"), + Row( + children: [ + CupertinoButton( + onPressed: () => BlocProvider.of(context) + .add(AssessmentInit(index)), + child: const Text("ReInit"), + ), + CupertinoButton( + onPressed: () => BlocProvider.of(context) + .add(AssessmentBegin()), + child: const Text("Begin"), + ), + ], + ) + ], + ), + ); + +Widget captureView(BuildContext context, String title, int index) => Builder( + builder: (context) => VideoRecorder( + title: title, + onCaptureSaved: (File f) => BlocProvider.of(context) + .add(AssessmentIncrement(f))), ); diff --git a/lib/views/assessments/default.dart b/lib/views/assessments/default.dart deleted file mode 100644 index 53ba75ac..00000000 --- a/lib/views/assessments/default.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -class Default { - List get content { - return [ - Text("DEFAULT INTRO"), - Text("Active"), - Text("DEFAULT INTRO 2"), - Text("Active2") - // TODO: implement content - ]; - } -} diff --git a/lib/views/assessments/gait.dart b/lib/views/assessments/gait.dart index b6dd1cc7..a9144c35 100644 --- a/lib/views/assessments/gait.dart +++ b/lib/views/assessments/gait.dart @@ -1,7 +1,9 @@ import 'package:flutter/cupertino.dart'; class Gait { - List get content { + final sequences = 1; + + List content(BuildContext context) { return [ Text("GAIT INTRO"), Text("Active"), @@ -11,7 +13,7 @@ class Gait { ]; } - Widget IntroSeq1() => Text("TODO"); + Widget IntroSeq1(BuildContext context) => Text("TODO"); - Widget CaptureSeq1() => Text("TODO"); + Widget CaptureSeq1(BuildContext context) => Text("TODO"); } diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart index f4e9bbe4..225db6cc 100644 --- a/lib/views/base_view.dart +++ b/lib/views/base_view.dart @@ -39,7 +39,7 @@ class BaseView extends StatelessWidget { )), //TODO make this better and animatable Positioned( - top: screenSize.height / 3, + top: screenSize.height / 4, child: SizedBox( height: screenSize.height * 16 / 24, width: screenSize.width, diff --git a/lib/views/components/assessment_card.dart b/lib/views/components/assessment_card.dart index 1740b2aa..ea9ee499 100644 --- a/lib/views/components/assessment_card.dart +++ b/lib/views/components/assessment_card.dart @@ -1,6 +1,5 @@ import 'package:flutter/cupertino.dart'; import 'package:visualpt/models/ModelProvider.dart'; -import 'package:visualpt/views/components/patient_form.dart'; class AssessmentCard extends StatelessWidget { final String imagePath; @@ -18,8 +17,9 @@ class AssessmentCard extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), - child: PatientForm( - assessmentType: assessmentType, + child: GestureDetector( + onTap: () => Navigator.pushNamed(context, "/assessment", + arguments: assessmentType), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: MediaQuery.of(context).size.height / 4, diff --git a/lib/views/components/patient_form.dart b/lib/views/components/patient_form.dart index a792cc7e..b1bc7586 100644 --- a/lib/views/components/patient_form.dart +++ b/lib/views/components/patient_form.dart @@ -1,15 +1,16 @@ import 'dart:developer'; import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/bloc/_bloc.dart'; import 'package:visualpt/models/ModelProvider.dart'; +import 'package:visualpt/views/_views.dart'; import 'package:visualpt/views/styles.dart'; class PatientForm extends StatefulWidget { final AssessmentType assessmentType; - final Widget child; - const PatientForm( - {Key? key, required this.assessmentType, required this.child}) - : super(key: key); + + const PatientForm({Key? key, required this.assessmentType}) : super(key: key); @override State createState() => _PatientFormState(); @@ -27,59 +28,43 @@ class _PatientFormState extends State { height_in_inches: 0); @override Widget build(BuildContext context) { - return GestureDetector(onTap: showFormDialog, child: widget.child); - } - - Future showFormDialog() { - return showCupertinoModalPopup( - useRootNavigator: false, - context: context, - builder: (BuildContext context) => - StatefulBuilder(builder: (context, setState) { - return CupertinoAlertDialog( - title: const Text('Patient Information', - style: Styles.defaultTitleOverride), - content: Form( - key: _formKey, - child: ConstrainedBox( - constraints: const BoxConstraints( - minHeight: 100, - maxHeight: 400, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - _formInput("First Name"), - _formInput("Last Name"), - _formInput("Birth Date", inputPopup: datePopup), - _formInput("Gender", inputPopup: genderPopup), - _formInput("Height"), - ], - ), - ), + return BaseView( + pageTitle: "Patient Information", + child: Form( + key: _formKey, + child: ConstrainedBox( + constraints: const BoxConstraints( + minHeight: 100, + maxHeight: 400, ), - actions: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: CupertinoButton( - color: CupertinoColors.link, - onPressed: () { - //storeDateTimeData(date); - if (_formKey.currentState!.validate()) { - _formKey.currentState!.save(); - Navigator.pop(context); - //TODO Specify assessment type argument - Navigator.pushNamed(context, "/assessment", - arguments: widget.assessmentType); - } - }, - child: const Text("Next"), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + _formInput("First Name"), + _formInput("Last Name"), + _formInput("Birth Date", inputPopup: datePopup), + _formInput("Gender", inputPopup: genderPopup), + _formInput("Height"), + Padding( + padding: const EdgeInsets.all(8.0), + child: CupertinoButton( + color: CupertinoColors.link, + onPressed: () { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + BlocProvider.of(context) + .add(AssessmentInit(0)); + //TODO store data to backend + } + }, + child: const Text("Next"), + ), ), - ), - ], - ); - }), + ], + ), + ), + ), ); } @@ -117,9 +102,21 @@ class _PatientFormState extends State { builder: (BuildContext context) => Column( mainAxisAlignment: MainAxisAlignment.end, children: [ - CupertinoButton( - child: const Text("Ok"), - onPressed: () => Navigator.pop(context, value), + Container( + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.all(8.0), + color: CupertinoColors.systemBackground.resolveFrom(context), + alignment: Alignment.centerRight, + child: Row( + children: [ + const Text("Date", style: Styles.inputStyle), + const Spacer(), + CupertinoButton( + color: CupertinoColors.link, + onPressed: () => Navigator.pop(context, value), + child: const Text("Save")), + ], + ), ), Container( height: 216, @@ -153,22 +150,23 @@ class _PatientFormState extends State { mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ - //Make this a reusable widget + //TODO Make this a reusable widget Container( - width: MediaQuery.of(context).size.width, - padding: const EdgeInsets.all(8.0), - color: CupertinoColors.systemBackground.resolveFrom(context), - alignment: Alignment.centerRight, - child: Row( - children: [ - const Text("Gender", style: Styles.inputStyle), - const Spacer(), - CupertinoButton( - color: CupertinoColors.link, - onPressed: () => Navigator.pop(context, value), - child: const Text("Save")), - ], - )), + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.all(8.0), + color: CupertinoColors.systemBackground.resolveFrom(context), + alignment: Alignment.centerRight, + child: Row( + children: [ + const Text("Gender", style: Styles.inputStyle), + const Spacer(), + CupertinoButton( + color: CupertinoColors.link, + onPressed: () => Navigator.pop(context, value), + child: const Text("Save")), + ], + ), + ), Container( height: 216, padding: const EdgeInsets.only(top: 6.0), @@ -181,13 +179,9 @@ class _PatientFormState extends State { child: CupertinoPicker( itemExtent: Styles.kItemExtent, onSelectedItemChanged: (int selectedItem) { - setState( - () { - value = selectedItem == 0 - ? "" - : Gender.values[selectedItem - 1].name.toString(); - }, - ); + value = selectedItem == 0 + ? "" + : Gender.values[selectedItem - 1].name.toString(); }, children: List.generate( 3, diff --git a/lib/views/components/video_recorder.dart b/lib/views/components/video_recorder.dart index 0acfc11e..7f367907 100644 --- a/lib/views/components/video_recorder.dart +++ b/lib/views/components/video_recorder.dart @@ -4,11 +4,17 @@ import 'package:camera/camera.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:visualpt/bloc/_bloc.dart'; +import 'package:visualpt/views/_views.dart'; class VideoRecorder extends StatefulWidget { + final String title; final CameraController? controller; final void Function(File f) onCaptureSaved; - const VideoRecorder({Key? key, this.controller, required this.onCaptureSaved}) + const VideoRecorder( + {Key? key, + required this.title, + this.controller, + required this.onCaptureSaved}) : super(key: key); @override @@ -108,17 +114,20 @@ class _VideoRecorderState extends State child: BlocBuilder( builder: (context, state) { return state is CameraError - ? Center( - child: Column( - children: [ - Text(state.exception.toString()), - CupertinoButton( - onPressed: () { - // BlocProvider.of(context) - // .resetCamera(); - }, - child: const Text("Retry")) - ], + ? BaseView( + pageTitle: widget.title, + child: Center( + child: Column( + children: [ + Text(state.exception.toString()), + CupertinoButton( + onPressed: () { + // BlocProvider.of(context) + // .resetCamera(); + }, + child: const Text("Restart Camera")) + ], + ), ), ) : cameraWidget(context, state); From eb17afbf381153283b81ce6b1e38b9d0a9991ed4 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Tue, 14 Feb 2023 00:00:28 -0500 Subject: [PATCH 26/43] start of physical device testing / inputs debugin --- lib/main.dart | 1 - lib/views/components/_components.dart | 1 + lib/views/components/auth_input.dart | 2 +- lib/views/components/form_input.dart | 38 ++++++++ lib/views/components/patient_form.dart | 119 ++++++++++------------- lib/views/components/video_recorder.dart | 10 +- 6 files changed, 97 insertions(+), 74 deletions(-) create mode 100644 lib/views/components/form_input.dart diff --git a/lib/main.dart b/lib/main.dart index a2a21923..1c727540 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,6 @@ void main() { } //TODO VisualPT -//Follow Figma Model //Connect PDF to email (until EMR integration is available) //Asseessment tutorial //Marketing content diff --git a/lib/views/components/_components.dart b/lib/views/components/_components.dart index 4263d7ab..158998d3 100644 --- a/lib/views/components/_components.dart +++ b/lib/views/components/_components.dart @@ -4,3 +4,4 @@ export 'patient_form.dart'; export 'view_background.dart'; export 'video_recorder.dart'; export 'shaker.dart'; +export 'form_input.dart'; diff --git a/lib/views/components/auth_input.dart b/lib/views/components/auth_input.dart index be1629d9..009226e2 100644 --- a/lib/views/components/auth_input.dart +++ b/lib/views/components/auth_input.dart @@ -1,8 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/bloc/_bloc.dart'; import 'package:visualpt/views/styles.dart'; -import '../../bloc/_bloc.dart'; //TODO this needs to become a formfield class AuthInput extends StatefulWidget { final AuthDataType type; diff --git a/lib/views/components/form_input.dart b/lib/views/components/form_input.dart new file mode 100644 index 00000000..434b8660 --- /dev/null +++ b/lib/views/components/form_input.dart @@ -0,0 +1,38 @@ +import 'package:flutter/cupertino.dart'; + +class FormInput extends StatefulWidget { + final String field; + final TextEditingController controller; + final TextInputType? inputType; + Future Function()? inputPopup; + FormInput(this.field, this.controller, + {super.key, this.inputPopup, this.inputType}) + : assert(inputType != null || inputPopup != null); + + @override + State createState() => _FormInputState(); +} + +class _FormInputState extends State { + @override + Widget build(BuildContext context) { + return CupertinoTextFormFieldRow( + controller: widget.controller, + textAlign: TextAlign.center, + autocorrect: false, + keyboardType: widget.inputType ?? TextInputType.none, + decoration: BoxDecoration( + color: CupertinoColors.systemBackground, + border: Border.all(), + borderRadius: BorderRadius.circular(8.0), + ), + textCapitalization: TextCapitalization.words, + placeholder: widget.field, + validator: (value) => value == null || value.isEmpty + ? 'Please enter a valid response' + : null, + //TODO Manage the controller.text (maybe do this after the rest of the UI build?) + onTap: () => widget.inputPopup, + ); + } +} diff --git a/lib/views/components/patient_form.dart b/lib/views/components/patient_form.dart index b1bc7586..d1be5d15 100644 --- a/lib/views/components/patient_form.dart +++ b/lib/views/components/patient_form.dart @@ -1,11 +1,10 @@ -import 'dart:developer'; import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:visualpt/bloc/_bloc.dart'; import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/_views.dart'; -import 'package:visualpt/views/styles.dart'; +import 'package:visualpt/views/components/_components.dart'; class PatientForm extends StatefulWidget { final AssessmentType assessmentType; @@ -20,12 +19,12 @@ class PatientForm extends StatefulWidget { class _PatientFormState extends State { final _formKey = GlobalKey(); DateTime date = DateTime.now(); - Patient patient = Patient( - name: "TODO", - family_name: "TODO", - dob: TemporalDate(DateTime.now()), - gender: Gender.MALE, - height_in_inches: 0); + TextEditingController firstNameController = TextEditingController(); + TextEditingController lastNameController = TextEditingController(); + TextEditingController dobController = TextEditingController(); + TextEditingController genderController = TextEditingController(); + TextEditingController heightController = TextEditingController(); + @override Widget build(BuildContext context) { return BaseView( @@ -41,11 +40,31 @@ class _PatientFormState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - _formInput("First Name"), - _formInput("Last Name"), - _formInput("Birth Date", inputPopup: datePopup), - _formInput("Gender", inputPopup: genderPopup), - _formInput("Height"), + FormInput( + "First Name", + firstNameController, + inputType: TextInputType.name, + ), + FormInput( + "Last Name", + lastNameController, + inputType: TextInputType.name, + ), + FormInput( + "Birth Date", + dobController, + inputPopup: datePopup, + ), + FormInput( + "Gender", + genderController, + inputPopup: genderPopup, + ), + FormInput( + "Height", + heightController, + inputType: TextInputType.number, + ), Padding( padding: const EdgeInsets.all(8.0), child: CupertinoButton( @@ -53,6 +72,13 @@ class _PatientFormState extends State { onPressed: () { if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); + Patient patient = Patient( + name: firstNameController.text, + family_name: lastNameController.text, + dob: TemporalDate(DateTime.parse(dobController.text)), + gender: genderParse(genderController.text), + height_in_inches: double.parse(heightController.text), + ); BlocProvider.of(context) .add(AssessmentInit(0)); //TODO store data to backend @@ -68,33 +94,6 @@ class _PatientFormState extends State { ); } - Widget _formInput(String field, {Future Function()? inputPopup}) { - TextEditingController controller = TextEditingController(); - return CupertinoTextFormFieldRow( - controller: controller, - textAlign: TextAlign.center, - style: Styles.inputStyle, - autocorrect: false, - decoration: BoxDecoration( - color: CupertinoColors.systemBackground, - border: Border.all(), - borderRadius: BorderRadius.circular(8.0), - ), - textCapitalization: TextCapitalization.words, - placeholder: field, - validator: (value) => value == null || value.isEmpty - ? 'Please enter a valid response' - : null, - onSaved: (String? value) { - storeValue(field, value); - }, - //TODO Manage the controller.text (maybe do this after the rest of the UI build?) - onTap: () => inputPopup != null - ? inputPopup() - .then((value) => value != null ? controller.text = value : null) - : null); - } - Future datePopup() { String? value; return showCupertinoModalPopup( @@ -197,33 +196,6 @@ class _PatientFormState extends State { ]), ); } - - void storeValue(String field, String? value) { - try { - switch (field) { - case "First Name": - patient = patient.copyWith(name: value!); - break; - case "Last Name": - patient = patient.copyWith(family_name: value!); - break; - case "Birth Date": - patient = patient.copyWith(dob: TemporalDate.fromString(value!)); - break; - case "Gender": - //TODO FIX WITH BLOC PATTERN - patient = patient.copyWith(gender: Gender.MALE); - break; - case "Height": - patient = patient.copyWith(height_in_inches: double.tryParse(value!)); - break; - default: - throw Exception("Invalid field or value used"); - } - } catch (e) { - log(e.toString()); - } - } } // void storeDateTimeData(DateTime date) { @@ -240,3 +212,16 @@ class _PatientFormState extends State { // patientData.date = nowDate; // patientData.time = nowTime; // } + +Gender genderParse(String text) { + switch (text) { + case "MALE": + return Gender.MALE; + + case "FEMALE": + return Gender.FEMALE; + + default: + return Gender.MALE; + } +} diff --git a/lib/views/components/video_recorder.dart b/lib/views/components/video_recorder.dart index 7f367907..5f495f15 100644 --- a/lib/views/components/video_recorder.dart +++ b/lib/views/components/video_recorder.dart @@ -122,10 +122,10 @@ class _VideoRecorderState extends State Text(state.exception.toString()), CupertinoButton( onPressed: () { - // BlocProvider.of(context) - // .resetCamera(); + BlocProvider.of(context) + .add(CameraInit()); }, - child: const Text("Restart Camera")) + child: const Text("Restart")), ], ), ), @@ -199,8 +199,8 @@ class _VideoRecorderState extends State Positioned( bottom: 10, child: GestureDetector( - // onTap: () => BlocProvider.of(context) - // .triggerState(context, state), + onTap: () => + BlocProvider.of(context).add(CameraPlay()), child: const Icon( CupertinoIcons.circle, color: CupertinoColors.black, From db3d5da0fbc95d2a30ab052c023c52e3b5aa7b6d Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Tue, 14 Feb 2023 00:00:32 -0500 Subject: [PATCH 27/43] Need to start testing --- test/widget_test.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/widget_test.dart b/test/widget_test.dart index f530d473..a75b4e8c 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -5,7 +5,7 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:visualpt/main.dart'; @@ -13,18 +13,18 @@ import 'package:visualpt/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(const VisualPT()); // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); + // expect(find.text('0'), findsOneWidget); + // expect(find.text('1'), findsNothing); // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); + // await tester.tap(find.byIcon(Icons.add)); + // await tester.pump(); // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + // expect(find.text('0'), findsNothing); + // expect(find.text('1'), findsOneWidget); }); } From dbfe8cb5ccc3bdbafe71753c6a5dda91e846206c Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Tue, 14 Feb 2023 22:11:05 -0500 Subject: [PATCH 28/43] streamlining FORM functionality, bug fixes --- lib/main.dart | 1 + lib/views/auth_view.dart | 186 ++++++++++++---------- lib/views/base_view.dart | 6 +- lib/views/components/_components.dart | 1 - lib/views/components/auth_input.dart | 61 ------- lib/views/components/form_input.dart | 86 ++++++++-- lib/views/components/patient_form.dart | 144 ++++++++++++----- lib/views/components/view_background.dart | 35 ++-- lib/views/styles.dart | 2 + 9 files changed, 305 insertions(+), 217 deletions(-) delete mode 100644 lib/views/components/auth_input.dart diff --git a/lib/main.dart b/lib/main.dart index 1c727540..50067c9b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,6 +20,7 @@ class VisualPT extends StatelessWidget { Widget build(BuildContext context) { return CupertinoApp( theme: const CupertinoThemeData( + brightness: Brightness.light, textTheme: CupertinoTextThemeData( textStyle: TextStyle( fontFamily: "Unbounded", color: CupertinoColors.black))), diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart index 1fbbac3a..ee668570 100644 --- a/lib/views/auth_view.dart +++ b/lib/views/auth_view.dart @@ -13,35 +13,35 @@ class AuthView extends StatefulWidget { class _AuthViewState extends State { final GlobalKey _shakeKey = GlobalKey(); + //TODO BLOC FORM WHEN MVP IS DONE + final _formKey = GlobalKey(); @override Widget build(BuildContext context) { - return CupertinoPageScaffold( - child: BaseView( - pageTitle: "Save clicks and minutes", - //TODO print debug to figure out why this shit broke - child: BlocProvider( - create: (context) => AuthBloc(), - child: BlocBuilder( - builder: (context, state) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (state is AuthSuccess) { - //Sync with Amplify services - context - .read() - .close(); //TODO save auth data before closing - Navigator.popAndPushNamed(context, "/home"); - } else if (state is AuthError) { - _shakeKey.currentState?.shake(); - } - }); - if (state is AuthSignUp) { - return signupForm(context); - } else { - return loginForm(context); + return BaseView( + pageTitle: "Save clicks and minutes", + //TODO print debug to figure out why this shit broke + child: BlocProvider( + create: (context) => AuthBloc(), + child: BlocBuilder( + builder: (context, state) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (state is AuthSuccess) { + //Sync with Amplify services + context + .read() + .close(); //TODO save auth data before closing + Navigator.popAndPushNamed(context, "/home"); + } else if (state is AuthError) { + _shakeKey.currentState?.shake(); } - }, - ), + }); + if (state is AuthSignUp) { + return signupForm(context); + } else { + return loginForm(context); + } + }, ), ), ); @@ -50,40 +50,23 @@ class _AuthViewState extends State { Widget loginForm(BuildContext context) { return Shaker( key: _shakeKey, - child: Column( - children: [ - //TODO stylize for ciewability - CupertinoButton( - color: Styles.actionSecondary, - child: const Text( - "Sign up", - style: Styles.subactionText, + child: Form( + key: _formKey, + child: Column( + children: [ + //TODO stylize for ciewability + const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Access your workflows"), ), - onPressed: () => addAuthEvent(context, event: AuthToggle()), - ), - const Padding( - padding: EdgeInsets.all(8.0), - child: Text("or Access your workflows"), - ), - const AuthInput(AuthDataType.email), - const AuthInput(AuthDataType.password, obscure: true), - Padding( - padding: const EdgeInsets.all(32.0), - child: GestureDetector( - child: Container( - alignment: Alignment.center, - height: MediaQuery.of(context).size.width / 2, - width: MediaQuery.of(context).size.width / 2, - decoration: const BoxDecoration( - shape: BoxShape.circle, color: Styles.actionPrimary), - child: const Text( - "Log In", - style: Styles.actionText, - ), - ), - onTap: () => addAuthEvent(context)), - ) - ], + const FormInput("email", + authType: AuthDataType.email, + inputType: TextInputType.emailAddress), + const FormInput("password", + authType: AuthDataType.password, obscure: true), + authButtons(context, true) + ], + ), ), ); } @@ -91,39 +74,66 @@ class _AuthViewState extends State { Widget signupForm(BuildContext context) { return Shaker( key: _shakeKey, - child: Column( - children: [ - CupertinoButton( + child: Form( + key: _formKey, + child: Column( + children: [ + const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Rethink your workflows"), + ), + const FormInput("email", + authType: AuthDataType.email, + inputType: TextInputType.emailAddress), + const FormInput("password", + authType: AuthDataType.password, obscure: true), + const FormInput("confirm password", + authType: AuthDataType.confirmPassword, obscure: true), + authButtons(context, false) + ], + ), + ), + ); + } + + Widget authButtons(BuildContext context, bool isLogin) { + String login = "Log In"; + String signup = "Sign up"; + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: GestureDetector( + child: Container( + alignment: Alignment.center, + height: MediaQuery.of(context).size.width / 2, + width: MediaQuery.of(context).size.width / 2, + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.actionPrimary), + child: Text(isLogin ? login : signup, style: Styles.actionText), + ), + onTap: () { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + addAuthEvent(context); + //TODO store data to backend + } + }, + ), + ), + const Text("or"), + Padding( + padding: const EdgeInsets.all(16.0), + child: CupertinoButton( color: Styles.actionSecondary, - child: const Text("Log in", style: Styles.subactionText), + child: Text( + isLogin ? signup : login, + style: Styles.subactionText, + ), onPressed: () => addAuthEvent(context, event: AuthToggle()), ), - const Padding( - padding: EdgeInsets.all(8.0), - child: Text("or Rethink your workflows"), - ), - const AuthInput(AuthDataType.email), - const AuthInput(AuthDataType.password, obscure: true), - const AuthInput(AuthDataType.confirmPassword, obscure: true), - Padding( - padding: const EdgeInsets.all(32.0), - child: GestureDetector( - child: Container( - alignment: Alignment.center, - height: MediaQuery.of(context).size.width / 2, - width: MediaQuery.of(context).size.width / 2, - decoration: const BoxDecoration( - shape: BoxShape.circle, color: Styles.actionPrimary), - child: const Text( - "Sign Up", - style: Styles.actionText, - ), - ), - onTap: () => addAuthEvent(context), - ), - ) - ], - ), + ), + ], ); } diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart index 225db6cc..b94a9fbd 100644 --- a/lib/views/base_view.dart +++ b/lib/views/base_view.dart @@ -13,6 +13,7 @@ class BaseView extends StatelessWidget { return CupertinoPageScaffold( child: Stack( + alignment: Alignment.center, children: [ const ViewBackground(), SafeArea( @@ -40,9 +41,10 @@ class BaseView extends StatelessWidget { //TODO make this better and animatable Positioned( top: screenSize.height / 4, + width: screenSize.width, child: SizedBox( - height: screenSize.height * 16 / 24, - width: screenSize.width, + //TODO dynamically make the instead of this hard coded whack chat + height: screenSize.height * 20 / 27, child: Container(child: child)), ) ], diff --git a/lib/views/components/_components.dart b/lib/views/components/_components.dart index 158998d3..b4a317d5 100644 --- a/lib/views/components/_components.dart +++ b/lib/views/components/_components.dart @@ -1,4 +1,3 @@ -export 'auth_input.dart'; export 'assessment_card.dart'; export 'patient_form.dart'; export 'view_background.dart'; diff --git a/lib/views/components/auth_input.dart b/lib/views/components/auth_input.dart deleted file mode 100644 index 009226e2..00000000 --- a/lib/views/components/auth_input.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:visualpt/bloc/_bloc.dart'; -import 'package:visualpt/views/styles.dart'; - -//TODO this needs to become a formfield -class AuthInput extends StatefulWidget { - final AuthDataType type; - final bool obscure; - const AuthInput(this.type, {this.obscure = false, super.key}); - - @override - State createState() => _AuthInputState(); -} - -class _AuthInputState extends State { - bool obsController = false; - late TextEditingController controller = TextEditingController(); - - @override - void initState() { - super.initState(); - obsController = widget.obscure; - } - - @override - Widget build(BuildContext context) { - final authBloc = BlocProvider.of(context); - return Stack( - alignment: Alignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - child: CupertinoTextField( - controller: controller, - placeholder: widget.type.name.toString(), - placeholderStyle: Styles.placeholderStyle, - style: Styles.inputStyle, - textAlign: TextAlign.center, - obscureText: obsController, - onChanged: (value) { - authBloc.storeAuthData(widget.type, value); - }, - ), - ), - if (widget.obscure) - Positioned( - right: 24, - child: GestureDetector( - child: Icon( - obsController - ? CupertinoIcons.eye_fill - : CupertinoIcons.eye_slash_fill, - color: CupertinoColors.inactiveGray), - onTap: () => setState(() => obsController = !obsController), - ), - ), - ], - ); - } -} diff --git a/lib/views/components/form_input.dart b/lib/views/components/form_input.dart index 434b8660..7ed2231b 100644 --- a/lib/views/components/form_input.dart +++ b/lib/views/components/form_input.dart @@ -1,38 +1,100 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/bloc/_bloc.dart'; +import 'package:visualpt/views/_views.dart'; class FormInput extends StatefulWidget { final String field; - final TextEditingController controller; + final TextEditingController? controller; final TextInputType? inputType; - Future Function()? inputPopup; - FormInput(this.field, this.controller, - {super.key, this.inputPopup, this.inputType}) - : assert(inputType != null || inputPopup != null); + final Future Function()? inputPopup; + final bool obscure; + final AuthDataType? authType; + const FormInput(this.field, + {super.key, + this.controller, + this.inputPopup, + this.inputType, + this.obscure = false, + this.authType}) + //There must exist a way of distinguishing what type of input board to present and how to display the data + : assert(inputType != null || inputPopup != null || authType != null), + //Either there is a text controller or an auth type specified for which a data controller function exists + assert(controller != null || authType != null); @override State createState() => _FormInputState(); } class _FormInputState extends State { + bool obsController = false; + + @override + void initState() { + super.initState(); + obsController = widget.obscure; + } + + TextInputType keyboardTypeChooser(AuthDataType? authType) { + switch (authType) { + case AuthDataType.email: + return TextInputType.emailAddress; + case AuthDataType.password: + return TextInputType.visiblePassword; + case AuthDataType.confirmPassword: + return TextInputType.visiblePassword; + default: + //Assume inputPopup is the supplied parameter + return TextInputType.none; + } + } + @override Widget build(BuildContext context) { - return CupertinoTextFormFieldRow( + return + //Stack( + // alignment: Alignment.center, + // children: [ + CupertinoTextFormFieldRow( controller: widget.controller, textAlign: TextAlign.center, autocorrect: false, - keyboardType: widget.inputType ?? TextInputType.none, + keyboardType: widget.inputType ?? keyboardTypeChooser(widget.authType), + textCapitalization: TextCapitalization.words, + style: Styles.inputStyle, + placeholder: widget.field, + placeholderStyle: Styles.placeholderStyle, + obscureText: widget.obscure, decoration: BoxDecoration( - color: CupertinoColors.systemBackground, + color: CupertinoColors.white, border: Border.all(), borderRadius: BorderRadius.circular(8.0), ), - textCapitalization: TextCapitalization.words, - placeholder: widget.field, validator: (value) => value == null || value.isEmpty ? 'Please enter a valid response' : null, - //TODO Manage the controller.text (maybe do this after the rest of the UI build?) - onTap: () => widget.inputPopup, + onTap: () => widget.inputPopup != null + ? widget.inputPopup!() + .then((value) => widget.controller!.text = value ?? "") + : null, + onChanged: (value) => widget.authType != null + ? BlocProvider.of(context) + .storeAuthData(widget.authType!, value) + //TODO Manage the controller.text (maybe do this after the rest of the UI build?) + : null, + //) + // Positioned( + // right: 24, + // child: GestureDetector( + // child: Icon( + // widget.obscure + // ? CupertinoIcons.eye_fill + // : CupertinoIcons.eye_slash_fill, + // color: CupertinoColors.inactiveGray), + // onTap: () => setState(() => obsController = !obsController), + // ), + // ), + // ], ); } } diff --git a/lib/views/components/patient_form.dart b/lib/views/components/patient_form.dart index d1be5d15..750c1981 100644 --- a/lib/views/components/patient_form.dart +++ b/lib/views/components/patient_form.dart @@ -34,59 +34,39 @@ class _PatientFormState extends State { child: ConstrainedBox( constraints: const BoxConstraints( minHeight: 100, - maxHeight: 400, + maxHeight: 500, ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + //TODO BLOC this shit UP into a form BLOC and add a reset button + crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ FormInput( "First Name", - firstNameController, + controller: firstNameController, inputType: TextInputType.name, ), FormInput( "Last Name", - lastNameController, + controller: lastNameController, inputType: TextInputType.name, ), FormInput( "Birth Date", - dobController, + controller: dobController, inputPopup: datePopup, ), FormInput( "Gender", - genderController, + controller: genderController, inputPopup: genderPopup, ), FormInput( "Height", - heightController, - inputType: TextInputType.number, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: CupertinoButton( - color: CupertinoColors.link, - onPressed: () { - if (_formKey.currentState!.validate()) { - _formKey.currentState!.save(); - Patient patient = Patient( - name: firstNameController.text, - family_name: lastNameController.text, - dob: TemporalDate(DateTime.parse(dobController.text)), - gender: genderParse(genderController.text), - height_in_inches: double.parse(heightController.text), - ); - BlocProvider.of(context) - .add(AssessmentInit(0)); - //TODO store data to backend - } - }, - child: const Text("Next"), - ), + controller: heightController, + inputPopup: numberPopup, ), + Buttons(context) ], ), ), @@ -104,7 +84,7 @@ class _PatientFormState extends State { Container( width: MediaQuery.of(context).size.width, padding: const EdgeInsets.all(8.0), - color: CupertinoColors.systemBackground.resolveFrom(context), + color: CupertinoColors.white, alignment: Alignment.centerRight, child: Row( children: [ @@ -123,10 +103,11 @@ class _PatientFormState extends State { margin: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), - color: CupertinoColors.systemBackground.resolveFrom(context), + color: CupertinoColors.white, child: SafeArea( top: false, child: CupertinoDatePicker( + backgroundColor: CupertinoColors.white, initialDateTime: date, mode: CupertinoDatePickerMode.date, onDateTimeChanged: (DateTime newDate) => @@ -153,7 +134,7 @@ class _PatientFormState extends State { Container( width: MediaQuery.of(context).size.width, padding: const EdgeInsets.all(8.0), - color: CupertinoColors.systemBackground.resolveFrom(context), + color: CupertinoColors.white, alignment: Alignment.centerRight, child: Row( children: [ @@ -172,10 +153,11 @@ class _PatientFormState extends State { margin: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), - color: CupertinoColors.systemBackground.resolveFrom(context), + color: CupertinoColors.white, child: SafeArea( top: false, child: CupertinoPicker( + backgroundColor: CupertinoColors.white, itemExtent: Styles.kItemExtent, onSelectedItemChanged: (int selectedItem) { value = selectedItem == 0 @@ -186,9 +168,63 @@ class _PatientFormState extends State { 3, (index) => index == 0 ? const Text("") - : Text( - Gender.values[index - 1].name.toString(), - ), + : Text(Gender.values[index - 1].name.toString(), + style: Styles.popupSelectionStyle), + ), + ), + ), + ), + ]), + ); + } + + Future numberPopup() { + List heightList = List.generate( + 80, + (index) => Text("${(index + 36) ~/ 12}'${(index + 36) % 12}''", + style: Styles.popupSelectionStyle)); + String? value; + return showCupertinoModalPopup( + context: context, + builder: (BuildContext context) => Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + //TODO Make this a reusable widget + Container( + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.all(8.0), + color: CupertinoColors.white, + alignment: Alignment.centerRight, + child: Row( + children: [ + const Text("Height", style: Styles.inputStyle), + const Spacer(), + CupertinoButton( + color: CupertinoColors.link, + onPressed: () => Navigator.pop(context, value), + child: const Text("Save")), + ], + ), + ), + Container( + height: 216, + padding: const EdgeInsets.only(top: 6.0), + margin: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + color: CupertinoColors.white, + child: SafeArea( + top: false, + child: CupertinoPicker( + backgroundColor: CupertinoColors.white, + itemExtent: Styles.kItemExtent, + onSelectedItemChanged: (int selectedItem) { + value = heightList[selectedItem].data.toString(); + }, + children: List.generate( + 80, + (index) => heightList[index], ), ), ), @@ -196,6 +232,40 @@ class _PatientFormState extends State { ]), ); } + + Widget Buttons(BuildContext context) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: GestureDetector( + child: Container( + alignment: Alignment.center, + height: MediaQuery.of(context).size.width / 2, + width: MediaQuery.of(context).size.width / 2, + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.actionPrimary), + child: Text("Next", style: Styles.actionText), + ), + onTap: () { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + Patient patient = Patient( + name: firstNameController.text, + family_name: lastNameController.text, + dob: TemporalDate(DateTime.parse(dobController.text)), + gender: genderParse(genderController.text), + height_in_inches: double.parse(heightController.text), + ); + BlocProvider.of(context).add(AssessmentInit(0)); + //TODO store data to backend + } + }, + ), + ), + ], + ); + } } // void storeDateTimeData(DateTime date) { diff --git a/lib/views/components/view_background.dart b/lib/views/components/view_background.dart index 4c009f3a..90676555 100644 --- a/lib/views/components/view_background.dart +++ b/lib/views/components/view_background.dart @@ -1,4 +1,4 @@ -import 'dart:math'; +//import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:visualpt/views/styles.dart'; @@ -6,29 +6,31 @@ class ViewBackground extends StatelessWidget { final int seed; const ViewBackground({super.key, this.seed = 0}); - Offset _generateRandomPosition(BuildContext context, bool topHalf) { - final Random random = seed == 0 ? Random() : Random(seed); + // Offset _generateRandomPosition(BuildContext context, bool topHalf) { + // final Random random = seed == 0 ? Random() : Random(seed); - double y = topHalf - ? random.nextDouble() * (MediaQuery.of(context).size.height / 3) - : random.nextDouble() * (MediaQuery.of(context).size.height / 3) + - (MediaQuery.of(context).size.height / 2); - return Offset( - random.nextDouble() * (MediaQuery.of(context).size.width / 3), y); - } + // double y = topHalf + // ? random.nextDouble() * (MediaQuery.of(context).size.height / 3) + // : random.nextDouble() * (MediaQuery.of(context).size.height / 3) + + // (MediaQuery.of(context).size.height / 2); + // return Offset( + // random.nextDouble() * (MediaQuery.of(context).size.width / 3), y); + // } @override Widget build(BuildContext context) { final containerSize = MediaQuery.of(context).size.height / 2; - final container1Pos = _generateRandomPosition(context, true); - final container2Pos = _generateRandomPosition(context, false); + // final container1Pos = _generateRandomPosition(context, true); + // final container2Pos = _generateRandomPosition(context, false); + return Container( color: Styles.backgroundPrimary, child: Stack( children: [ Positioned( - left: container1Pos.dx, - top: container1Pos.dy, + //If able to prevent rebuilds use container1Pos.dx .dy + left: 90, + top: 70, child: Container( width: containerSize, height: containerSize, @@ -39,8 +41,9 @@ class ViewBackground extends StatelessWidget { ), ), Positioned( - left: container2Pos.dx, - top: container2Pos.dy, + //If able to prevent rebuilds use container2Pos.dx .dy + left: 30, + top: 500, child: Container( width: containerSize, height: containerSize, diff --git a/lib/views/styles.dart b/lib/views/styles.dart index 610a8398..25e9f18c 100644 --- a/lib/views/styles.dart +++ b/lib/views/styles.dart @@ -34,4 +34,6 @@ class Styles { color: CupertinoColors.white, fontSize: 32, fontWeight: FontWeight.bold); static const TextStyle subactionText = TextStyle( color: CupertinoColors.white, fontSize: 18, fontWeight: FontWeight.bold); + static const TextStyle popupSelectionStyle = + TextStyle(color: CupertinoColors.black); } From ba17c56729034ba4969bed263a3f58b365be7d3b Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:05:29 -0500 Subject: [PATCH 29/43] importing packages for PDF --- ios/Podfile.lock | 6 +++ pubspec.lock | 114 ++++++++++++++++++++++++++++++++++++++++++++--- pubspec.yaml | 3 ++ 3 files changed, 118 insertions(+), 5 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5117b1c3..d02b7bef 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -75,6 +75,8 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - printing (1.0.0): + - Flutter - SQLite.swift (0.13.2): - SQLite.swift/standard (= 0.13.2) - SQLite.swift/standard (0.13.2) @@ -95,6 +97,7 @@ DEPENDENCIES: - ffmpeg_kit_flutter_min_gpl (from `.symlinks/plugins/ffmpeg_kit_flutter_min_gpl/ios`) - Flutter (from `Flutter`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) + - printing (from `.symlinks/plugins/printing/ios`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`) @@ -132,6 +135,8 @@ EXTERNAL SOURCES: :path: Flutter path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/ios" + printing: + :path: ".symlinks/plugins/printing/ios" video_player_avfoundation: :path: ".symlinks/plugins/video_player_avfoundation/ios" video_thumbnail: @@ -157,6 +162,7 @@ SPEC CHECKSUMS: libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81 path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 + printing: eafa00acb682c0ca029d4d98d0798f55a1e27102 SQLite.swift: 4fc2be46c36392e3b87afe6fe7f1801c1daa07ef SwiftFormat: fc64190719f3655079b7b4c246182383bd89026f SwiftLint: 284cea64b6187c5d6bd83e9a548a64104d546447 diff --git a/pubspec.lock b/pubspec.lock index f32e9573..587e42b9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -73,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.12" + archive: + dependency: transitive + description: + name: archive + sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + url: "https://pub.dev" + source: hosted + version: "3.3.6" async: dependency: transitive description: @@ -89,6 +97,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.1" + barcode: + dependency: transitive + description: + name: barcode + sha256: "52570564684bbb0240a9f1fdb6bad12adc5e0540103c1c96d6dd550bd928b1c9" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + bidi: + dependency: transitive + description: + name: bidi + sha256: dc00274c7edabae2ab30c676e736ea1eb0b1b7a1b436cb5fe372e431ccb39ab0 + url: "https://pub.dev" + source: hosted + version: "2.0.6" bloc: dependency: transitive description: @@ -169,6 +193,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" cross_file: dependency: transitive description: @@ -201,6 +233,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -312,6 +352,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image: + dependency: transitive + description: + name: image + sha256: "483a389d6ccb292b570c31b3a193779b1b0178e7eb571986d9a49904b6861227" + url: "https://pub.dev" + source: hosted + version: "4.0.15" intl: dependency: transitive description: @@ -384,6 +432,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" + source: hosted + version: "1.0.1" path_provider: dependency: "direct main" description: @@ -412,10 +468,10 @@ packages: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + sha256: "2e32f1640f07caef0d3cb993680f181c79e54a3827b997d5ee221490d131fbd9" url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.1.8" path_provider_platform_interface: dependency: transitive description: @@ -432,6 +488,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + pdf: + dependency: "direct main" + description: + name: pdf + sha256: "6cd57e8e6d052bd1078f18e0dc7cd6701fad6288231c1ce99d66ef5034d14e61" + url: "https://pub.dev" + source: hosted + version: "3.9.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + url: "https://pub.dev" + source: hosted + version: "5.1.0" platform: dependency: transitive description: @@ -448,6 +520,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + url: "https://pub.dev" + source: hosted + version: "3.6.2" + printing: + dependency: "direct main" + description: + name: printing + sha256: fe654363cd0114b50a0815b24e96957c7e9a60eb4e3b7ccfe71cf3f2b7114cb2 + url: "https://pub.dev" + source: hosted + version: "5.10.1" process: dependency: transitive description: @@ -464,6 +552,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.5" + qr: + dependency: transitive + description: + name: qr + sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" quiver: dependency: transitive description: @@ -633,10 +729,18 @@ packages: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + xml: + dependency: transitive + description: + name: xml + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" url: "https://pub.dev" source: hosted - version: "0.2.0+3" + version: "6.2.2" sdks: dart: ">=2.18.5 <4.0.0" - flutter: ">=3.0.0" + flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml index 56cb4d90..21faf854 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,9 @@ dependencies: amplify_datastore: ^0.6.0 amplify_auth_cognito: ^0.6.0 vector_math: ^2.1.4 + equatable: ^2.0.5 + pdf: ^3.9.0 + printing: ^5.10.1 dev_dependencies: flutter_test: From 253e38cd00762ebaae0860c977ee68fc2a1ed119 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:07:38 -0500 Subject: [PATCH 30/43] cleaning forms --- lib/views/components/form_input.dart | 93 ++++++++++++++------------ lib/views/components/patient_form.dart | 33 +++++---- 2 files changed, 68 insertions(+), 58 deletions(-) diff --git a/lib/views/components/form_input.dart b/lib/views/components/form_input.dart index 7ed2231b..48440ffe 100644 --- a/lib/views/components/form_input.dart +++ b/lib/views/components/form_input.dart @@ -51,50 +51,55 @@ class _FormInputState extends State { @override Widget build(BuildContext context) { - return - //Stack( - // alignment: Alignment.center, - // children: [ - CupertinoTextFormFieldRow( - controller: widget.controller, - textAlign: TextAlign.center, - autocorrect: false, - keyboardType: widget.inputType ?? keyboardTypeChooser(widget.authType), - textCapitalization: TextCapitalization.words, - style: Styles.inputStyle, - placeholder: widget.field, - placeholderStyle: Styles.placeholderStyle, - obscureText: widget.obscure, - decoration: BoxDecoration( - color: CupertinoColors.white, - border: Border.all(), - borderRadius: BorderRadius.circular(8.0), - ), - validator: (value) => value == null || value.isEmpty - ? 'Please enter a valid response' - : null, - onTap: () => widget.inputPopup != null - ? widget.inputPopup!() - .then((value) => widget.controller!.text = value ?? "") - : null, - onChanged: (value) => widget.authType != null - ? BlocProvider.of(context) - .storeAuthData(widget.authType!, value) - //TODO Manage the controller.text (maybe do this after the rest of the UI build?) - : null, - //) - // Positioned( - // right: 24, - // child: GestureDetector( - // child: Icon( - // widget.obscure - // ? CupertinoIcons.eye_fill - // : CupertinoIcons.eye_slash_fill, - // color: CupertinoColors.inactiveGray), - // onTap: () => setState(() => obsController = !obsController), - // ), - // ), - // ], + return Stack( + alignment: Alignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(right: 16.0), + child: CupertinoTextFormFieldRow( + controller: widget.controller, + textAlign: TextAlign.center, + autocorrect: false, + keyboardType: + widget.inputType ?? keyboardTypeChooser(widget.authType), + textCapitalization: TextCapitalization.words, + style: Styles.inputStyle, + placeholder: widget.field, + placeholderStyle: Styles.placeholderStyle, + obscureText: obsController, + decoration: BoxDecoration( + color: CupertinoColors.white, + border: Border.all(), + borderRadius: BorderRadius.circular(8.0), + ), + validator: (value) => value == null || value.isEmpty + ? 'Please enter a valid response' + : null, + onTap: () => widget.inputPopup != null + ? widget.inputPopup!() + .then((value) => widget.controller!.text = value ?? "") + : null, + onChanged: (value) => widget.authType != null + ? BlocProvider.of(context) + .storeAuthData(widget.authType!, value) + //TODO Manage the controller.text (maybe do this after the rest of the UI build?) + : null, + ), + ), + widget.obscure + ? Positioned( + right: 36, + child: GestureDetector( + child: Icon( + obsController + ? CupertinoIcons.eye_fill + : CupertinoIcons.eye_slash_fill, + color: CupertinoColors.inactiveGray), + onTap: () => setState(() => obsController = !obsController), + ), + ) + : Container() + ], ); } } diff --git a/lib/views/components/patient_form.dart b/lib/views/components/patient_form.dart index 750c1981..13ced9f8 100644 --- a/lib/views/components/patient_form.dart +++ b/lib/views/components/patient_form.dart @@ -18,7 +18,6 @@ class PatientForm extends StatefulWidget { //TODO FIX WITH BLOC (post nap) class _PatientFormState extends State { final _formKey = GlobalKey(); - DateTime date = DateTime.now(); TextEditingController firstNameController = TextEditingController(); TextEditingController lastNameController = TextEditingController(); TextEditingController dobController = TextEditingController(); @@ -64,9 +63,9 @@ class _PatientFormState extends State { FormInput( "Height", controller: heightController, - inputPopup: numberPopup, + inputPopup: heightPopup, ), - Buttons(context) + buttons(context) ], ), ), @@ -108,7 +107,7 @@ class _PatientFormState extends State { top: false, child: CupertinoDatePicker( backgroundColor: CupertinoColors.white, - initialDateTime: date, + initialDateTime: DateTime.now(), mode: CupertinoDatePickerMode.date, onDateTimeChanged: (DateTime newDate) => value = newDate.toString().split(" ")[0], @@ -169,7 +168,7 @@ class _PatientFormState extends State { (index) => index == 0 ? const Text("") : Text(Gender.values[index - 1].name.toString(), - style: Styles.popupSelectionStyle), + style: Styles.popupSelectionText), ), ), ), @@ -178,11 +177,15 @@ class _PatientFormState extends State { ); } - Future numberPopup() { + Future heightPopup() { + const int minHeight = 36; //Minimum listable height on the picker (inches) + const int maxHeight = 120; + List heightList = List.generate( - 80, - (index) => Text("${(index + 36) ~/ 12}'${(index + 36) % 12}''", - style: Styles.popupSelectionStyle)); + maxHeight - minHeight, + (index) => Text( + "${(index + minHeight) ~/ 12}'${(index + minHeight) % 12}''", + style: Styles.popupSelectionText)); String? value; return showCupertinoModalPopup( context: context, @@ -219,11 +222,11 @@ class _PatientFormState extends State { child: CupertinoPicker( backgroundColor: CupertinoColors.white, itemExtent: Styles.kItemExtent, - onSelectedItemChanged: (int selectedItem) { - value = heightList[selectedItem].data.toString(); + onSelectedItemChanged: (int heightInches) { + value = "${heightInches + minHeight}\""; }, children: List.generate( - 80, + maxHeight - minHeight, (index) => heightList[index], ), ), @@ -233,7 +236,7 @@ class _PatientFormState extends State { ); } - Widget Buttons(BuildContext context) { + Widget buttons(BuildContext context) { return Column( children: [ Padding( @@ -248,6 +251,7 @@ class _PatientFormState extends State { child: Text("Next", style: Styles.actionText), ), onTap: () { + //TODO modal if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); Patient patient = Patient( @@ -255,7 +259,8 @@ class _PatientFormState extends State { family_name: lastNameController.text, dob: TemporalDate(DateTime.parse(dobController.text)), gender: genderParse(genderController.text), - height_in_inches: double.parse(heightController.text), + height_in_inches: + double.parse(heightController.text.split("\"")[0]), ); BlocProvider.of(context).add(AssessmentInit(0)); //TODO store data to backend From 93b0b00186139edbffd3cf98256299d97eb7797d Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:08:01 -0500 Subject: [PATCH 31/43] seperating timer from camera bloc --- lib/bloc/timer/timer_bloc.dart | 68 +++++++++++++++++++++++++++++++++ lib/bloc/timer/timer_event.dart | 18 +++++++++ lib/bloc/timer/timer_state.dart | 27 +++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 lib/bloc/timer/timer_bloc.dart create mode 100644 lib/bloc/timer/timer_event.dart create mode 100644 lib/bloc/timer/timer_state.dart diff --git a/lib/bloc/timer/timer_bloc.dart b/lib/bloc/timer/timer_bloc.dart new file mode 100644 index 00000000..9ca59c60 --- /dev/null +++ b/lib/bloc/timer/timer_bloc.dart @@ -0,0 +1,68 @@ +///Sourced from https://github.com/felangel/bloc/tree/master/examples/flutter_timer + +import 'dart:async'; +import 'dart:developer'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +part 'timer_event.dart'; +part 'timer_state.dart'; + +//TODO This could change depending on the test, may need to specify this when creating the bloc in the future +const Duration _minDuration = Duration(seconds: 0); +const Duration _maxDuration = Duration(seconds: 30); + +class TimerBloc extends Bloc { + TimerBloc() + : _ticker = const Ticker(), + super(const TimerInitial(_minDuration)) { + on(_onStarted); + on(_onReset); + on<_TimerTicked>(_onTicked); + } + + final Ticker _ticker; + StreamSubscription? _tickerSubscription; + + @override + Future close() { + _tickerSubscription?.cancel(); + return super.close(); + } + + void _onStarted(TimerStarted event, Emitter emit) { + emit(const TimerRunInProgress(Duration(milliseconds: 0))); + _tickerSubscription?.cancel(); + _tickerSubscription = _ticker + .tick() + .listen((duration) => add(_TimerTicked(duration: duration))); + } + + void _onReset(TimerReset event, Emitter emit) { + _tickerSubscription?.cancel(); + emit(const TimerInitial(_minDuration)); + } + + void _onTicked(_TimerTicked event, Emitter emit) { + emit( + event.duration < _maxDuration + ? TimerRunInProgress(event.duration) + : const TimerRunComplete(), + ); + } +} + +//Stream class that powers the entire BLOC +class Ticker { + static const increment = 10; + const Ticker(); + Stream tick() { + return Stream.periodic( + const Duration(milliseconds: increment), + (x) => Duration(milliseconds: increment * x), + ).takeWhile( + (duration) => duration <= _maxDuration, + ); + } +} diff --git a/lib/bloc/timer/timer_event.dart b/lib/bloc/timer/timer_event.dart new file mode 100644 index 00000000..88f700b9 --- /dev/null +++ b/lib/bloc/timer/timer_event.dart @@ -0,0 +1,18 @@ +part of 'timer_bloc.dart'; + +abstract class TimerEvent { + const TimerEvent(); +} + +class TimerStarted extends TimerEvent { + const TimerStarted(); +} + +class TimerReset extends TimerEvent { + const TimerReset(); +} + +class _TimerTicked extends TimerEvent { + const _TimerTicked({required this.duration}); + final Duration duration; +} diff --git a/lib/bloc/timer/timer_state.dart b/lib/bloc/timer/timer_state.dart new file mode 100644 index 00000000..0609546a --- /dev/null +++ b/lib/bloc/timer/timer_state.dart @@ -0,0 +1,27 @@ +part of 'timer_bloc.dart'; + +abstract class TimerState extends Equatable { + const TimerState(this.duration); + final Duration duration; + + @override + List get props => [duration]; +} + +class TimerInitial extends TimerState { + const TimerInitial(super.duration); + + @override + String toString() => 'TimerInitial { duration: $duration }'; +} + +class TimerRunInProgress extends TimerState { + const TimerRunInProgress(super.duration); + + @override + String toString() => 'TimerRunInProgress { duration: $duration }'; +} + +class TimerRunComplete extends TimerState { + const TimerRunComplete() : super(_maxDuration); +} From 18207c3fe5736f9102569ebdcb688923c58061fb Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:13:19 -0500 Subject: [PATCH 32/43] connecting assessment components --- lib/bloc/_bloc.dart | 1 + lib/bloc/assessment/assessment_bloc.dart | 13 +- lib/bloc/camera/camera_bloc.dart | 107 ++++------ lib/bloc/camera/camera_event.dart | 2 +- lib/bloc/camera/camera_state.dart | 25 ++- lib/views/assessment_view.dart | 21 +- lib/views/assessments/_assessments.dart | 9 +- lib/views/components/_components.dart | 3 +- lib/views/components/assessment_card.dart | 37 ++-- lib/views/components/video_camera.dart | 206 +++++++++++++++++++ lib/views/components/video_recorder.dart | 233 ---------------------- lib/views/components/video_timer.dart | 46 +++++ 12 files changed, 358 insertions(+), 345 deletions(-) create mode 100644 lib/views/components/video_camera.dart delete mode 100644 lib/views/components/video_recorder.dart create mode 100644 lib/views/components/video_timer.dart diff --git a/lib/bloc/_bloc.dart b/lib/bloc/_bloc.dart index 7b863fdd..121283f9 100644 --- a/lib/bloc/_bloc.dart +++ b/lib/bloc/_bloc.dart @@ -1,3 +1,4 @@ export 'assessment/assessment_bloc.dart'; export 'auth/auth_bloc.dart'; export 'camera/camera_bloc.dart'; +export 'timer/timer_bloc.dart'; diff --git a/lib/bloc/assessment/assessment_bloc.dart b/lib/bloc/assessment/assessment_bloc.dart index 97237d90..4d3d540d 100644 --- a/lib/bloc/assessment/assessment_bloc.dart +++ b/lib/bloc/assessment/assessment_bloc.dart @@ -1,7 +1,9 @@ import 'dart:async'; +import 'dart:developer'; import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/bloc/_bloc.dart'; import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/assessments/_assessments.dart'; @@ -16,8 +18,8 @@ class AssessmentBloc extends Bloc { late List introViews; late List captureViews; - late List assessmentAssets; - late List skeletalSequences; + List assessmentAssets = []; + List skeletalSequences = []; late DateTime assessmentStart; late Duration assessmentDuration; @@ -38,6 +40,7 @@ class AssessmentBloc extends Bloc { } FutureOr _onInit(AssessmentInit event, Emitter emit) { + //TODO transition visual emit(AssessmentIntro(index)); } @@ -52,6 +55,7 @@ class AssessmentBloc extends Bloc { index++; if (index == assessmentCount) { assessmentDuration = assessmentStart.difference(DateTime.now()); + log("Time elapsed: $assessmentDuration\nassests $assessmentAssets"); emit(AssessmentSuccess(assessmentAssets, index)); } else { emit(AssessmentIntro(index)); @@ -64,3 +68,8 @@ class AssessmentBloc extends Bloc { Widget getCaptureView(BuildContext context, int index) => captureViews[index](context); } + +void saveCameraDataToAssessment(AssessmentBloc assessment, CameraBloc camera) { + camera.add(CameraStop()); + camera.getData().then((data) => assessment.add(AssessmentIncrement(data))); +} diff --git a/lib/bloc/camera/camera_bloc.dart b/lib/bloc/camera/camera_bloc.dart index 76a5736e..dc5ea095 100644 --- a/lib/bloc/camera/camera_bloc.dart +++ b/lib/bloc/camera/camera_bloc.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'dart:io'; import 'package:camera/camera.dart'; import 'package:flutter/cupertino.dart'; @@ -11,11 +12,12 @@ class CameraBloc extends Bloc { CameraController? cameraController; Timer? timer; Duration duration = const Duration(seconds: 0); + late File output; CameraBloc() : super(CameraLoading()) { - on(_cameraInit); - on(_cameraPlay); - on(_cameraPause); - on(_cameraSave); + on(_onInit); + on(_onPlay); + on(_onPause); + on(_onStop); } void resetTimer() { @@ -23,38 +25,26 @@ class CameraBloc extends Bloc { duration = const Duration(seconds: 0); } - // void triggerState(BuildContext context, CameraState state) async { - // try { - // if (state is CameraRecording) { - // resetTimer(); - // XFile file = await state.controller.stopVideoRecording(); - // CupertinoPageRoute route = CupertinoPageRoute( - // fullscreenDialog: true, - // builder: (_) => EditorPage(file: File(file.path))); - // Navigator.push(context, route); - // initCamera(); - // } else if (state is CameraStandby) { - // startTimer(state.controller); - // state.controller.startVideoRecording(); - // } - // } on Exception catch (e) { - // resetTimer(); - // emit(CameraError(exception: e)); - // } - // } - - // void resetCamera() async { - // emit(CameraLoading()); - // initCamera(); - // } - - FutureOr _cameraInit( - CameraInit event, Emitter emit) async { + FutureOr _onInit(CameraInit event, Emitter emit) async { + log("camera init"); try { - await getCamera() - .then((_) => emit(CameraStandby(controller: cameraController!))) - .onError((error, stackTrace) => - emit(CameraError(exception: Exception(error)))); + await availableCameras() + .then( + (cameras) => CameraController( + cameras.firstWhere( + (camera) => camera.lensDirection == CameraLensDirection.back), + ResolutionPreset.max, + enableAudio: false, + imageFormatGroup: ImageFormatGroup.bgra8888, + ), + ) + .then((controller) async { + await controller.initialize(); + cameraController = controller; + emit(CameraStandby(cameraController!)); + }); + + log("In standby"); } on CameraException catch (e) { switch (e.code) { case 'CameraAccessDenied': @@ -84,42 +74,29 @@ class CameraBloc extends Bloc { } } - FutureOr _cameraPlay(CameraPlay event, Emitter emit) { - timer = Timer.periodic(const Duration(milliseconds: 10), (_) { - duration = Duration(milliseconds: duration.inMilliseconds + 10); + FutureOr _onPlay(CameraPlay event, Emitter emit) async { + log("camera play"); + await cameraController!.startVideoRecording(); - emit(CameraRecording(controller: cameraController!, duration: duration)); - }); + emit(CameraRecording(cameraController!)); } - FutureOr _cameraPause(CameraPause event, Emitter emit) {} + FutureOr _onPause(CameraPause event, Emitter emit) { + log("camera pause"); + } - FutureOr _cameraSave( - CameraSave event, Emitter emit) async { - final XFile data = await cameraController!.stopVideoRecording(); + FutureOr _onStop(CameraStop event, Emitter emit) async { + output = await cameraController! + .stopVideoRecording() + .then((file) => File(file.path)); + log(output.toString()); + //TODO Pass data up! cameraController!.dispose(); - getCamera(); + emit(const CameraLoading()); } - Future getCamera() async { - try { - await availableCameras() - .then((cameras) => cameras.firstWhere( - (camera) => camera.lensDirection == CameraLensDirection.back)) - .then((camera) => CameraController( - camera, - ResolutionPreset.max, - enableAudio: false, - imageFormatGroup: ImageFormatGroup.bgra8888, - )) - .then((controller) { - controller.initialize(); - return controller; - }) - .then((controller) => cameraController = controller) - .onError((error, stackTrace) => throw Exception(error)); - } on Exception catch (e) { - emit(CameraError(exception: e)); - } + Future getData() async { + //TODO Make more reliable! + return await Future.delayed(const Duration(seconds: 1), () => output); } } diff --git a/lib/bloc/camera/camera_event.dart b/lib/bloc/camera/camera_event.dart index 3941f351..d8773ddb 100644 --- a/lib/bloc/camera/camera_event.dart +++ b/lib/bloc/camera/camera_event.dart @@ -9,4 +9,4 @@ class CameraPlay extends CameraEvent {} class CameraPause extends CameraEvent {} -class CameraSave extends CameraEvent {} +class CameraStop extends CameraEvent {} diff --git a/lib/bloc/camera/camera_state.dart b/lib/bloc/camera/camera_state.dart index 4569a447..a313ab66 100644 --- a/lib/bloc/camera/camera_state.dart +++ b/lib/bloc/camera/camera_state.dart @@ -1,25 +1,28 @@ part of 'camera_bloc.dart'; @immutable -abstract class CameraState {} +abstract class CameraState { + final IconData icon; + final CameraController? controller; + const CameraState(this.icon, this.controller); +} -class CameraLoading extends CameraState {} +class CameraLoading extends CameraState { + const CameraLoading() : super(CupertinoIcons.hourglass, null); +} class CameraRecording extends CameraState { - final IconData icon = CupertinoIcons.square; - final CameraController controller; - final Duration duration; - CameraRecording({required this.controller, required this.duration}); + const CameraRecording(CameraController controller) + : super(CupertinoIcons.square, controller); } class CameraStandby extends CameraState { - final IconData icon = CupertinoIcons.circle; - final CameraController controller; - CameraStandby({required this.controller}); + const CameraStandby(CameraController controller) + : super(CupertinoIcons.circle, controller); } class CameraError extends CameraState { - final IconData icon = CupertinoIcons.restart; final Exception exception; - CameraError({required this.exception}); + const CameraError({required this.exception}) + : super(CupertinoIcons.restart, null); } diff --git a/lib/views/assessment_view.dart b/lib/views/assessment_view.dart index 0009a758..5417b9ec 100644 --- a/lib/views/assessment_view.dart +++ b/lib/views/assessment_view.dart @@ -1,10 +1,8 @@ -import 'dart:io'; - +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/_views.dart'; -import 'package:visualpt/views/assessments/_assessments.dart'; import 'package:visualpt/views/components/_components.dart'; import '../bloc/_bloc.dart'; @@ -30,22 +28,19 @@ class AssessmentView extends StatelessWidget { if (state is AssessmentForm) { return PatientForm(assessmentType: assessmentType); //return PatientForm(assessmentType: assessmentType,child: ) - } - if (state is AssessmentIntro) { + } else if (state is AssessmentIntro) { return BaseView( pageTitle: "Performing ${assessmentType.name}", child: BlocProvider.of(context) .getIntroView(context, state.index)); - } - if (state is AssessmentActive) { + } else if (state is AssessmentActive) { return BlocProvider.of(context) .getCaptureView(context, state.index); - } - if (state is AssessmentSuccess) { - return BaseView( - pageTitle: "Finished ${assessmentType.name}", - child: Placeholder(), - ); + } else if (state is AssessmentSuccess) { + Navigator.pushReplacementNamed(context, "analysis", + arguments: [assessmentType.name, state.assessmentAssets]); + //TODO Return loading Page + return const CupertinoActivityIndicator(); } else { throw Exception("Undefined State"); } diff --git a/lib/views/assessments/_assessments.dart b/lib/views/assessments/_assessments.dart index 3e7b3786..a5e2fae8 100644 --- a/lib/views/assessments/_assessments.dart +++ b/lib/views/assessments/_assessments.dart @@ -1,11 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:visualpt/models/ModelProvider.dart'; -import 'package:visualpt/views/assessments/ctsib.dart'; -import 'package:visualpt/views/assessments/gait.dart'; - -export 'ctsib.dart'; -export 'gait.dart'; +import 'package:visualpt/views/assessments/ctsib/ctsib.dart'; +import 'package:visualpt/views/assessments/gait/gait.dart'; class AssessmentRouter { final AssessmentType assessmentType; @@ -26,7 +23,7 @@ class AssessmentRouter { break; case AssessmentType.GAIT: //TODO assessmentWidgets = Gait().content(); - assessmentCount = Ctsib().sequences; + assessmentCount = Gait().sequences; break; default: diff --git a/lib/views/components/_components.dart b/lib/views/components/_components.dart index b4a317d5..85f8bbc7 100644 --- a/lib/views/components/_components.dart +++ b/lib/views/components/_components.dart @@ -1,6 +1,7 @@ export 'assessment_card.dart'; export 'patient_form.dart'; export 'view_background.dart'; -export 'video_recorder.dart'; +export 'video_camera.dart'; export 'shaker.dart'; export 'form_input.dart'; +export 'video_timer.dart'; diff --git a/lib/views/components/assessment_card.dart b/lib/views/components/assessment_card.dart index ea9ee499..179781e3 100644 --- a/lib/views/components/assessment_card.dart +++ b/lib/views/components/assessment_card.dart @@ -1,32 +1,39 @@ import 'package:flutter/cupertino.dart'; import 'package:visualpt/models/ModelProvider.dart'; +import '../styles.dart'; + class AssessmentCard extends StatelessWidget { final String imagePath; final AssessmentType assessmentType; final String subtitle; + final bool available; - const AssessmentCard({ - Key? key, - required this.imagePath, - required this.assessmentType, - required this.subtitle, - }) : super(key: key); + const AssessmentCard( + {Key? key, + required this.imagePath, + required this.assessmentType, + required this.subtitle, + this.available = true}) + : super(key: key); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), child: GestureDetector( - onTap: () => Navigator.pushNamed(context, "/assessment", - arguments: assessmentType), + onTap: () => available + ? Navigator.pushNamed(context, "/assessment", + arguments: assessmentType) + : null, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: MediaQuery.of(context).size.height / 4, width: MediaQuery.of(context).size.height / 4, child: Container( decoration: BoxDecoration( - color: CupertinoColors.white, + color: + available ? CupertinoColors.white : Styles.comingSoonColor, borderRadius: BorderRadius.circular(10.0), ), child: Padding( @@ -40,10 +47,8 @@ class AssessmentCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(assessmentType.name, - style: const TextStyle( - fontWeight: FontWeight.bold, fontSize: 32)), - Text(subtitle, style: const TextStyle(fontSize: 24)), + Text(assessmentType.name, style: Styles.actionText), + Text(subtitle, style: Styles.subtitle), ], ), ), @@ -52,3 +57,9 @@ class AssessmentCard extends StatelessWidget { ); } } + +Widget comingSoon(Widget child) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + child: Container(color: Styles.comingSoonColor, child: child)); +} diff --git a/lib/views/components/video_camera.dart b/lib/views/components/video_camera.dart new file mode 100644 index 00000000..aee4dabb --- /dev/null +++ b/lib/views/components/video_camera.dart @@ -0,0 +1,206 @@ +import 'dart:io'; + +import 'package:camera/camera.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/bloc/_bloc.dart'; +import 'package:visualpt/views/_views.dart'; +import 'package:visualpt/views/components/_components.dart'; + +class VideoRecorder extends StatefulWidget { + final String title; + final CameraController? controller; + final void Function(File f) onCaptureSaved; + const VideoRecorder( + {Key? key, + required this.title, + this.controller, + required this.onCaptureSaved}) + : super(key: key); + + @override + State createState() => _VideoRecorderState(); +} + +class _VideoRecorderState extends State + with WidgetsBindingObserver { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + // #docregion AppLifecycle + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = widget.controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; + } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + // onNewCameraSelected(cameraController.description); + } + } + + void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + if (widget.controller == null) { + return; + } + + final CameraController cameraController = widget.controller!; + + final Offset offset = Offset( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + cameraController.setExposurePoint(offset); + cameraController.setFocusPoint(offset); + } + + // Future onNewCameraSelected(CameraDescription cameraDescription) async { + // final CameraController? oldController = widget.controller; + // if (oldController != null) { + // // `controller` needs to be set to null before getting disposed, + // // to avoid a race condition when we use the controller that is being + // // disposed. This happens when camera permission dialog shows up, + // // which triggers `didChangeAppLifecycleState`, which disposes and + // // re-creates the controller. + // widget.controller = null; + // await oldController.dispose(); + // } + + // final CameraController cameraController = CameraController( + // cameraDescription, + // ResolutionPreset.max, + // ); + + // widget.controller = cameraController; + + // // If the controller is updated then update the UI. + // cameraController.addListener(() { + // if (mounted) { + // setState(() {}); + // } + // if (cameraController.value.hasError) { + // print( + // 'Camera error ${cameraController.value.errorDescription}'); + // } + // }); + + // if (mounted) { + // setState(() {}); + // } + // } + + @override + Widget build(BuildContext context) { + //TODO Put all this blocProvider jumble in the bloc folder AND delay load until cameraController is ready + return Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration(border: Border.all()), + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => CameraBloc()..add(CameraInit()), + ), + BlocProvider( + create: (context) => TimerBloc(), + ), + ], + child: BlocBuilder( + builder: (context, state) { + try { + if (state is CameraStandby || state is CameraRecording) { + return cameraWidget(context, state); + } else { + //TODO Loading View or throws error message + return BaseView( + pageTitle: widget.title, + child: Center( + child: Column( + children: const [ + Text("Loading"), + CupertinoActivityIndicator() + ], + ), + ), + ); + } + } on Exception catch (e) { + return BaseView( + pageTitle: widget.title, + child: Center( + child: Column( + children: [ + Text(e.toString()), + CupertinoButton( + onPressed: () { + BlocProvider.of(context) + .add(CameraInit()); + }, + child: const Text("Restart")), + ], + ), + ), + ); + } + }, + ), + ), + ); + } + + Widget cameraWidget(BuildContext context, CameraState state) { + assert(state is CameraRecording || state is CameraStandby); + + final assessmentBloc = BlocProvider.of(context); + final cameraBloc = BlocProvider.of(context); + final timerBloc = BlocProvider.of(context); + + return Stack(alignment: Alignment.center, children: [ + const ViewBackground(), + SizedBox( + width: MediaQuery.of(context).size.width, + child: CameraPreview( + state.controller!, + child: LayoutBuilder( + builder: (_, constraints) => GestureDetector( + onTapDown: (details) => + onViewFinderTap(details, constraints), + )), + ), + ), + Positioned( + bottom: 10, + child: GestureDetector( + onTap: () { + if (state is CameraRecording) { + timerBloc.add(const TimerReset()); + saveCameraDataToAssessment(assessmentBloc, cameraBloc); + } else { + cameraBloc.add(CameraPlay()); + timerBloc.add(const TimerStarted()); + } + }, + child: state is CameraRecording + ? const Icon(CupertinoIcons.stop, + color: Styles.backgroundComplementory, size: 80) + : const Icon(CupertinoIcons.circle, + color: Styles.actionPrimary, size: 80)), + ), + const Positioned(top: 10, child: VideoTimer()) + ]); + } +} diff --git a/lib/views/components/video_recorder.dart b/lib/views/components/video_recorder.dart deleted file mode 100644 index 5f495f15..00000000 --- a/lib/views/components/video_recorder.dart +++ /dev/null @@ -1,233 +0,0 @@ -import 'dart:io'; - -import 'package:camera/camera.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:visualpt/bloc/_bloc.dart'; -import 'package:visualpt/views/_views.dart'; - -class VideoRecorder extends StatefulWidget { - final String title; - final CameraController? controller; - final void Function(File f) onCaptureSaved; - const VideoRecorder( - {Key? key, - required this.title, - this.controller, - required this.onCaptureSaved}) - : super(key: key); - - @override - State createState() => _VideoRecorderState(); -} - -class _VideoRecorderState extends State - with WidgetsBindingObserver { - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addObserver(this); - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - // #docregion AppLifecycle - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - final CameraController? cameraController = widget.controller; - - // App state changed before we got the chance to initialize. - if (cameraController == null || !cameraController.value.isInitialized) { - return; - } - - if (state == AppLifecycleState.inactive) { - cameraController.dispose(); - } else if (state == AppLifecycleState.resumed) { - // onNewCameraSelected(cameraController.description); - } - } - - void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { - if (widget.controller == null) { - return; - } - - final CameraController cameraController = widget.controller!; - - final Offset offset = Offset( - details.localPosition.dx / constraints.maxWidth, - details.localPosition.dy / constraints.maxHeight, - ); - cameraController.setExposurePoint(offset); - cameraController.setFocusPoint(offset); - } - - // Future onNewCameraSelected(CameraDescription cameraDescription) async { - // final CameraController? oldController = widget.controller; - // if (oldController != null) { - // // `controller` needs to be set to null before getting disposed, - // // to avoid a race condition when we use the controller that is being - // // disposed. This happens when camera permission dialog shows up, - // // which triggers `didChangeAppLifecycleState`, which disposes and - // // re-creates the controller. - // widget.controller = null; - // await oldController.dispose(); - // } - - // final CameraController cameraController = CameraController( - // cameraDescription, - // ResolutionPreset.max, - // ); - - // widget.controller = cameraController; - - // // If the controller is updated then update the UI. - // cameraController.addListener(() { - // if (mounted) { - // setState(() {}); - // } - // if (cameraController.value.hasError) { - // print( - // 'Camera error ${cameraController.value.errorDescription}'); - // } - // }); - - // if (mounted) { - // setState(() {}); - // } - // } - - @override - Widget build(BuildContext context) { - //TODO Put all this blocProvider jumble in the bloc folder - return Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - decoration: BoxDecoration(border: Border.all()), - child: BlocProvider( - create: (context) => CameraBloc()..add(CameraInit()), - child: BlocBuilder( - builder: (context, state) { - return state is CameraError - ? BaseView( - pageTitle: widget.title, - child: Center( - child: Column( - children: [ - Text(state.exception.toString()), - CupertinoButton( - onPressed: () { - BlocProvider.of(context) - .add(CameraInit()); - }, - child: const Text("Restart")), - ], - ), - ), - ) - : cameraWidget(context, state); - }, - ), - ), - ); - } - - Widget stopwatchWidget(BuildContext context, CameraState state) { - return SizedBox( - height: MediaQuery.of(context).size.height / 8, - width: MediaQuery.of(context).size.width / 2, - child: DecoratedBox( - decoration: BoxDecoration( - color: CupertinoColors.systemRed, - borderRadius: BorderRadius.circular(4), - ), - child: Center( - child: Text( - (() { - if (state is CameraRecording) { - return formatter(state.duration); - } else { - return formatter(const Duration(seconds: 0)); - } - }()), - style: const TextStyle(color: Color(0xFF000000))), - ), - ), - ); - } - - Widget cameraWidget(BuildContext context, CameraState state) { - return SizedBox( - height: MediaQuery.of(context).size.height * 0.8, - width: MediaQuery.of(context).size.width * 0.8, - child: Stack( - alignment: Alignment.center, - children: (() { - if (state is CameraRecording) { - return [ - CameraPreview(state.controller), - Positioned( - bottom: 10, - child: GestureDetector( - onTap: () => - BlocProvider.of(context).add(CameraSave()), - child: const Icon( - CupertinoIcons.stop, - color: CupertinoColors.systemRed, - size: 80, - ), - ), - ), - Positioned( - top: 20, - right: 20, - child: Row( - children: [ - stopwatchWidget(context, state), - ], - ), - ), - ]; - } else if (state is CameraStandby) { - return [ - CameraPreview(state.controller), - Positioned( - bottom: 10, - child: GestureDetector( - onTap: () => - BlocProvider.of(context).add(CameraPlay()), - child: const Icon( - CupertinoIcons.circle, - color: CupertinoColors.black, - size: 80, - ), - ), - ), - Positioned( - top: 20, - right: 20, - child: Row( - children: [ - stopwatchWidget(context, state), - ], - ), - ), - ]; - } else { - return [const Center(child: CupertinoActivityIndicator())]; - } - }()), - ), - ); - } - - String formatter(Duration duration) => [ - duration.inSeconds.remainder(60).toString().padLeft(2, '0'), - duration.inMilliseconds.remainder(1000).toString().padLeft(3, '0')[0] - ].join("."); -} diff --git a/lib/views/components/video_timer.dart b/lib/views/components/video_timer.dart new file mode 100644 index 00000000..c77d7064 --- /dev/null +++ b/lib/views/components/video_timer.dart @@ -0,0 +1,46 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/bloc/_bloc.dart'; +import 'package:visualpt/views/_views.dart'; + +///Requires that a BlocProvider wrap it to function +///TODO verify how to do this without sacrificing DX +class VideoTimer extends StatelessWidget { + const VideoTimer({super.key}); + + @override + Widget build(BuildContext context) { + final assessmentBloc = BlocProvider.of(context); + final cameraBloc = BlocProvider.of(context); + + return SafeArea( + child: Container( + width: MediaQuery.of(context).size.width / + 4, //TODO ensure this works well on different screens + decoration: BoxDecoration( + color: Styles.backgroundComplementory, + borderRadius: BorderRadius.circular(4), + ), + child: Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: BlocBuilder( + builder: (context, state) { + if (state is TimerRunComplete) { + saveCameraDataToAssessment(assessmentBloc, cameraBloc); + } + return Text(formatter(state.duration), style: Styles.subtitle); + //TODO this style should be the font that has even spacing + }, + ), + ), + ), + ), + ); + } +} + +String formatter(Duration duration) => [ + duration.inSeconds.remainder(60).toString().padLeft(2, '0'), + duration.inMilliseconds.remainder(1000).toString().padLeft(3, '0')[0] + ].join("."); From ac7c544363d8027a5431a76c275f0f47d42f5764 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:14:12 -0500 Subject: [PATCH 33/43] beginning assessment to analysis traisition --- lib/views/analysis_view.dart | 17 +- lib/views/assessments/{ => ctsib}/ctsib.dart | 0 lib/views/assessments/gait.dart | 19 - lib/views/assessments/gait/_gait.dart | 2 + lib/views/assessments/gait/gait.dart | 57 +++ lib/views/assessments/gait/pdf.dart | 397 +++++++++++++++++++ lib/views/components/view_background.dart | 2 +- 7 files changed, 471 insertions(+), 23 deletions(-) rename lib/views/assessments/{ => ctsib}/ctsib.dart (100%) delete mode 100644 lib/views/assessments/gait.dart create mode 100644 lib/views/assessments/gait/_gait.dart create mode 100644 lib/views/assessments/gait/gait.dart create mode 100644 lib/views/assessments/gait/pdf.dart diff --git a/lib/views/analysis_view.dart b/lib/views/analysis_view.dart index 486daf2a..0c1a7ee7 100644 --- a/lib/views/analysis_view.dart +++ b/lib/views/analysis_view.dart @@ -1,10 +1,21 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:visualpt/views/_views.dart'; class AnalysisView extends StatelessWidget { - const AnalysisView({super.key}); + final String assessmentType; + final List assessmentAssets; + const AnalysisView( + {super.key, + required this.assessmentType, + required this.assessmentAssets}); @override Widget build(BuildContext context) { - return Container(); + return BaseView( + pageTitle: "$assessmentType analysis", + child: const Placeholder(), + ); } -} \ No newline at end of file +} diff --git a/lib/views/assessments/ctsib.dart b/lib/views/assessments/ctsib/ctsib.dart similarity index 100% rename from lib/views/assessments/ctsib.dart rename to lib/views/assessments/ctsib/ctsib.dart diff --git a/lib/views/assessments/gait.dart b/lib/views/assessments/gait.dart deleted file mode 100644 index a9144c35..00000000 --- a/lib/views/assessments/gait.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -class Gait { - final sequences = 1; - - List content(BuildContext context) { - return [ - Text("GAIT INTRO"), - Text("Active"), - Text("GAIT INTRO 2"), - Text("Active2") - // TODO: implement content - ]; - } - - Widget IntroSeq1(BuildContext context) => Text("TODO"); - - Widget CaptureSeq1(BuildContext context) => Text("TODO"); -} diff --git a/lib/views/assessments/gait/_gait.dart b/lib/views/assessments/gait/_gait.dart new file mode 100644 index 00000000..0907b5c7 --- /dev/null +++ b/lib/views/assessments/gait/_gait.dart @@ -0,0 +1,2 @@ +export 'gait.dart'; +export 'pdf.dart'; diff --git a/lib/views/assessments/gait/gait.dart b/lib/views/assessments/gait/gait.dart new file mode 100644 index 00000000..637b0b41 --- /dev/null +++ b/lib/views/assessments/gait/gait.dart @@ -0,0 +1,57 @@ +import 'package:flutter/cupertino.dart'; + +class Gait { + final sequences = 1; + + List content(BuildContext context) { + return [ + Text("GAIT INTRO"), + Text("Active"), + Text("GAIT INTRO 2"), + Text("Active2") + // TODO: implement content + ]; + } + + Widget IntroSeq1(BuildContext context) => Text("TODO"); + + Widget CaptureSeq1(BuildContext context) => Text("TODO"); +} + +class GaitNorms { + GaitNorms._(); + //Numbers are in m/s and start in the 6-12, then 13-19, then 20-29, 30-39... to 80+ + static const maleNorm = [1.19, 1.2, 1.4, 1.48, 1.48, 1.4, 1.39, 1.34, 1.1]; + static const femaleNorm = [1.14, 1.2, 1.4, 1.4, 1.4, 1.39, 1.22, 1.19, 1.0]; + + static double getVelocityFromAge(bool isMale, int age) { + final int index = (age / 10).round().clamp(0, 8); + return isMale ? maleNorm[index] : femaleNorm[index]; + } + + static double getPercentDifferenceFromNorm( + double patientVelocity, bool isMale, int age) { + final double normVelocity = getVelocityFromAge(isMale, age); + return (((patientVelocity - normVelocity) / + ((patientVelocity + normVelocity) / 2)) * + 100) + .roundToDouble(); + } +} + +class GaitPatientData { + late String managingtherapistEmail = 'sample@example.com'; + late String firstname = 'sample'; + late String lastname = 'patient'; + late String bday = '01/01/1901'; + late bool isMale = true; + late String age = '0'; + late String date = '01/01/1901'; + late String time = '00:00'; + late String velocity = "1.0"; + late double measurementDuration = 0.0; + late bool isVideo = true; + late String rawReportData = 'sample'; +} + +GaitPatientData gaitData = GaitPatientData(); diff --git a/lib/views/assessments/gait/pdf.dart b/lib/views/assessments/gait/pdf.dart new file mode 100644 index 00000000..19ed3425 --- /dev/null +++ b/lib/views/assessments/gait/pdf.dart @@ -0,0 +1,397 @@ +//DO NOT IMPORT material.dart without renaming it with keyword, will conflict with pdf/widgets.dart +import 'package:flutter/services.dart'; +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart'; +import 'package:printing/printing.dart'; +import 'package:visualpt/views/assessments/gait/_gait.dart'; + +class GaitPdf { + late Font baseFont; + late Font boldFont; + late Font italicFont; + late Font boldItalicFont; + + late PdfColor primaryColor; + late PdfColor secondaryColor; + late PdfColor black; + + late BorderSide borderSide; + + final double inch = 72.0; + final double halfinch = 36.0; + + late TextStyle header; + late TextStyle headerItalic; + late TextStyle subTitle; + late TextStyle detail; + late TextStyle detailBold; + late TextStyle subHeader; + late TextStyle disclaimer; + + Future configPdfStyles() async { + baseFont = await PdfGoogleFonts.openSansRegular(); + boldFont = await PdfGoogleFonts.openSansBold(); + italicFont = await PdfGoogleFonts.openSansItalic(); + boldItalicFont = await PdfGoogleFonts.openSansBoldItalic(); + + primaryColor = PdfColor.fromHex('#008ed6'); + secondaryColor = PdfColor.fromHex('#24446c'); + black = PdfColors.black; + + borderSide = BorderSide(width: 2, color: secondaryColor); + + header = TextStyle(font: boldFont, fontSize: 30.0, lineSpacing: 2.0); + headerItalic = + TextStyle(font: boldItalicFont, fontSize: 30.0, lineSpacing: 2.0); + subTitle = TextStyle(font: boldFont, fontSize: 20.0, lineSpacing: 2.0); + detail = TextStyle(font: baseFont, fontSize: 15.0, lineSpacing: 2.0); + detailBold = TextStyle(font: boldFont, fontSize: 15.0, lineSpacing: 2.0); + subHeader = TextStyle(font: boldFont, fontSize: 50.0, lineSpacing: 2.0); + disclaimer = TextStyle(font: baseFont, fontSize: 10.0, lineSpacing: 2.0); + return; + } + + Future generatePdf(GaitPatientData patientData) async { + await configPdfStyles(); + //final fyzLogo = await rootBundle.loadString('assets/fyzical-logo.svg'); + final gaitrLogo = await rootBundle.loadString('assets/gaitr-logo.svg'); + + final pdf = Document( + title: patientData.lastname + + patientData.firstname + + patientData.age.toString(), + creator: "gaitr Systems"); + + pdf.addPage( + Page( + pageTheme: PageTheme( + pageFormat: PdfPageFormat.letter, + margin: EdgeInsets.all(halfinch), + ), + build: (Context context) => SizedBox( + height: 11 * inch, + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Header( + child: Column(children: [ + SizedBox( + height: inch, + width: 8.5 * inch, + child: Center( + child: SvgImage(svg: gaitrLogo, fit: BoxFit.fitHeight), + ), + ), + RichText( + text: TextSpan( + children: [ + TextSpan(text: "Gait Velocity Analysis", style: header), + ], + ), + overflow: TextOverflow.span), + ])), + SizedBox( + height: 3 * inch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: disclaimer, + text: + "A distance of 10 meters is measured over a level surface with 2 meters for acceleration and 2 meters for deceleration.")), + Spacer(flex: 2), + SizedBox( + height: 0.65 * inch, + child: Row(children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + text: TextSpan(children: [ + TextSpan( + text: "Patient Name: ", style: detail), + TextSpan( + text: + "${patientData.firstname} ${patientData.lastname}", + style: detailBold), + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan( + text: "Birth Date: ", style: detail), + TextSpan( + text: patientData.bday, + style: detailBold), + ]), + ), + ]), + Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + RichText( + text: TextSpan(children: [ + TextSpan(text: "Date: ", style: detail), + TextSpan( + text: patientData.date, + style: detailBold), + ])), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Time: ", style: detail), + TextSpan( + text: patientData.time, + style: detailBold), + ])), + ]), + ])), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Age: ", style: detail), + TextSpan(text: patientData.age, style: detailBold), + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Gender: ", style: detail), + TextSpan( + text: patientData.isMale ? "Male" : "Female", + style: detailBold), + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Measurement Method: ", style: detail), + TextSpan( + text: patientData.isVideo ? "Video" : "Stopwatch", + style: detailBold), + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Measurement Duration: ", style: detail), + TextSpan( + text: patientData.measurementDuration + .toStringAsPrecision(3), + style: detailBold), + TextSpan(text: " seconds", style: detail), + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Gait Velocity: ", style: detail), + TextSpan(text: patientData.velocity, style: detailBold), + TextSpan(text: " meters/second", style: detail), + ])), + ]), + ), + Spacer(), + SizedBox( + height: 0.75 * inch, + child: Row(children: [ + Stack( + alignment: Alignment.centerLeft, + children: [ + SizedBox( + height: inch * 1, + width: inch * 5, + child: Column(children: [ + Container( + height: inch * 0.25, + width: inch * 5, + foregroundDecoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + PdfColor.fromHex("#1b365d"), + PdfColor.fromHex("#1d3d68"), + PdfColor.fromHex("#1f4573"), + PdfColor.fromHex("#214d7e"), + PdfColor.fromHex("#225589"), + PdfColor.fromHex("#22659f"), + PdfColor.fromHex("#206eab"), + PdfColor.fromHex("#1d76b7"), + PdfColor.fromHex("#197fc2"), + PdfColor.fromHex("#1088ce"), + PdfColor.fromHex("#0091da"), + ], + ), + ), + ), + Row( + children: axisLabel(), + ), + RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: disclaimer, text: "Gait Velocity (m/s)"), + ), + Spacer(), + ]), + ), + Positioned( + top: 0.1, + left: positioner(patientData.velocity), + child: Container( + height: 0.25 * inch, + width: 0.25 * inch, + decoration: BoxDecoration( + color: PdfColor.fromRYB(1, 0, 0), + shape: BoxShape.circle), + ), + ), + Positioned( + top: 0.1, + left: positioner(GaitNorms.getVelocityFromAge( + patientData.isMale, int.parse(patientData.age)) + .toString()), + child: Container( + height: 0.25 * inch, + width: 0.25 * inch, + decoration: BoxDecoration( + color: PdfColor.fromRYB(0, 1, 1), + shape: BoxShape.circle), + ), + ), + ], + ), + Spacer(), + Container( + decoration: BoxDecoration( + border: Border.all(), + borderRadius: BorderRadius.circular(2.0)), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(children: [ + Container( + height: 0.25 * inch, + width: 0.25 * inch, + decoration: BoxDecoration( + color: PdfColor.fromRYB(1, 0, 0), + shape: BoxShape.circle), + ), + Text(" - Patient"), + ]), + Spacer(), + Row(children: [ + Container( + height: 0.25 * inch, + width: 0.25 * inch, + decoration: BoxDecoration( + color: PdfColor.fromRYB(0, 1, 1), + shape: BoxShape.circle), + ), + Text(" - Average Peer"), + ]), + Spacer(), + ]), + )) + ]), + ), + Spacer(), + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: detailBold, + text: getComparisonMessage(double.parse(patientData.velocity), + patientData.isMale, int.parse(patientData.age)), + ), + ), + ), + Spacer(flex: 2), + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: subTitle, + text: double.parse(patientData.velocity) < 1 + ? "Needs intervention to reduce risk of fall" + : "Low likelihood of experiencing a fall"), + ), + ), + Spacer(flex: 4), + Footer( + padding: EdgeInsets.zero, + margin: EdgeInsets.zero, + leading: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText(text: const TextSpan(text: " ")), + RichText( + text: TextSpan( + style: disclaimer, + text: "© 2022 Gaitr Measurement Softwares")), + RichText( + text: TextSpan( + style: disclaimer, + text: "No Distribution without Permission")), + RichText( + text: TextSpan( + style: disclaimer, + text: + "Reference: Fritz, Stacy & Lusardi, Michelle. (2009).\n Walking speed: The sixth vital sign. J Geriatr Phys Ther. 32. 46-49. ")) + ]), + trailing: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + RichText( + text: TextSpan( + style: disclaimer, + text: "Minimal Detectable Change: 0.1m/sec")), + RichText( + text: TextSpan( + style: disclaimer, + text: + "Minimum Clinically Important Difference: 0.1m/sec")), + RichText( + text: TextSpan( + style: disclaimer, + text: "Test/Re-test Reliability: ICC > 0.7")) + ], + ), + ), + ]), + ), + ), + ); + return pdf.save(); + } + + List axisLabel() { + final values = List.generate(9, (i) => i * 0.2) + .map((value) => value >= 1.0 + ? Text(value.toStringAsPrecision(2)) + : Text(value.toStringAsPrecision(1))) + .toList(); + return List.generate( + 17, (i) => i % 2 == 0 ? values[i ~/ 2] : Spacer()); + } + + double positioner(String velocity) => + (2.95 * inch * double.parse(velocity)).clamp(0 * inch, 4.75 * inch); +} + +String getComparisonMessage(double velocity, bool isMale, int age) { + double percent = + GaitNorms.getPercentDifferenceFromNorm(velocity, isMale, age); + int index = (age / 10).round(); + String speedComparison = percent > 0 ? "faster" : "slower"; + String ageRange = index > 8 ? "80+" : "${index}0 - ${index}9"; + return percent.abs() > 5.0 + ? "Among $ageRange year olds, you are ${percent.abs()}% $speedComparison than average." + : "Your gait velocity matches your peer group aged $ageRange."; +} diff --git a/lib/views/components/view_background.dart b/lib/views/components/view_background.dart index 90676555..352a172f 100644 --- a/lib/views/components/view_background.dart +++ b/lib/views/components/view_background.dart @@ -28,7 +28,7 @@ class ViewBackground extends StatelessWidget { child: Stack( children: [ Positioned( - //If able to prevent rebuilds use container1Pos.dx .dy + //TODO If able to prevent rebuilds use container1Pos.dx .dy left: 90, top: 70, child: Container( From d090e150a47e69488f56864351c0cd25409327db Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:14:42 -0500 Subject: [PATCH 34/43] generated analysis route --- lib/main.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index 50067c9b..196a25fe 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -29,7 +29,6 @@ class VisualPT extends StatelessWidget { const AuthView(), //TODO make automatic reference based on auth "/auth": (context) => const AuthView(), "/home": (context) => const HomeView(), - "/analysis": (context) => const AnalysisView(), }, onGenerateRoute: (settings) { if (settings.name == "/assessment") { @@ -38,6 +37,14 @@ class VisualPT extends StatelessWidget { builder: (context) => AssessmentView(assessmentType: assessmentType)); } + if (settings.name == "/analysis") { + final data = settings.arguments as List; + final type = data[0]; + final assets = data[1]; + return CupertinoPageRoute( + builder: (context) => + AnalysisView(assessmentType: type, assessmentAssets: assets)); + } }, ); } From 6e9a3cbb5ffce02c18e11c1fd18f1ce6afdc9f82 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:14:57 -0500 Subject: [PATCH 35/43] minor style changes --- lib/views/auth_view.dart | 5 ++--- lib/views/base_view.dart | 4 ++-- lib/views/home_view.dart | 2 +- lib/views/styles.dart | 6 ++++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart index ee668570..21cf7290 100644 --- a/lib/views/auth_view.dart +++ b/lib/views/auth_view.dart @@ -31,7 +31,7 @@ class _AuthViewState extends State { context .read() .close(); //TODO save auth data before closing - Navigator.popAndPushNamed(context, "/home"); + Navigator.pushReplacementNamed(context, "/home"); } else if (state is AuthError) { _shakeKey.currentState?.shake(); } @@ -54,7 +54,6 @@ class _AuthViewState extends State { key: _formKey, child: Column( children: [ - //TODO stylize for ciewability const Padding( padding: EdgeInsets.all(8.0), child: Text("Access your workflows"), @@ -115,9 +114,9 @@ class _AuthViewState extends State { onTap: () { if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); - addAuthEvent(context); //TODO store data to backend } + addAuthEvent(context); }, ), ), diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart index b94a9fbd..b584faa7 100644 --- a/lib/views/base_view.dart +++ b/lib/views/base_view.dart @@ -13,7 +13,7 @@ class BaseView extends StatelessWidget { return CupertinoPageScaffold( child: Stack( - alignment: Alignment.center, + //alignment: Alignment.center, children: [ const ViewBackground(), SafeArea( @@ -32,7 +32,7 @@ class BaseView extends StatelessWidget { padding: const EdgeInsets.only(left: 21.0), child: Text( pageTitle, - style: Styles.subtitleStyle, + style: Styles.subtitle, overflow: TextOverflow.visible, ), ) diff --git a/lib/views/home_view.dart b/lib/views/home_view.dart index 6ee8eb4e..63919516 100644 --- a/lib/views/home_view.dart +++ b/lib/views/home_view.dart @@ -10,7 +10,7 @@ class HomeView extends StatelessWidget { Widget build(BuildContext context) { return CupertinoPageScaffold( child: BaseView( - pageTitle: "", + pageTitle: "Select an assessment", child: ListView( scrollDirection: Axis.horizontal, children: const [ diff --git a/lib/views/styles.dart b/lib/views/styles.dart index 25e9f18c..5f039486 100644 --- a/lib/views/styles.dart +++ b/lib/views/styles.dart @@ -17,13 +17,15 @@ class Styles { static const Color actionPrimary = Color(0xFF1F7A97); static const Color actionSecondary = Color(0x551F7A97); + static const Color comingSoonColor = Color(0x66000000); + //Asset Paths static const String mainLogoPath = "assets/vpt_mainlogo.png"; static const String ctsibLogoPath = "assets/ctsib_mainlogo.png"; static const String gvLogoPath = "assets/gv_mainlogo.png"; //Text Styles - static const TextStyle subtitleStyle = TextStyle(fontSize: 24); + static const TextStyle subtitle = TextStyle(fontSize: 24); static const TextStyle defaultTitleOverride = TextStyle( fontFamily: "Unbounded", fontSize: 32, fontWeight: FontWeight.bold); static const TextStyle placeholderStyle = @@ -34,6 +36,6 @@ class Styles { color: CupertinoColors.white, fontSize: 32, fontWeight: FontWeight.bold); static const TextStyle subactionText = TextStyle( color: CupertinoColors.white, fontSize: 18, fontWeight: FontWeight.bold); - static const TextStyle popupSelectionStyle = + static const TextStyle popupSelectionText = TextStyle(color: CupertinoColors.black); } From 0a07a3a664457a7ac41a770b4182c62353d9cd9f Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Wed, 15 Feb 2023 18:23:45 -0500 Subject: [PATCH 36/43] modifying availability of assessment choices --- lib/views/assessments/ctsib/ctsib.dart | 2 +- lib/views/components/assessment_card.dart | 13 ++++++++++--- lib/views/home_view.dart | 8 +++++--- lib/views/styles.dart | 11 ++++++++--- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/lib/views/assessments/ctsib/ctsib.dart b/lib/views/assessments/ctsib/ctsib.dart index 37b052eb..9522d0cf 100644 --- a/lib/views/assessments/ctsib/ctsib.dart +++ b/lib/views/assessments/ctsib/ctsib.dart @@ -31,7 +31,7 @@ class Ctsib { Widget introView(BuildContext context, String title, int index) => Builder( builder: (context) => Column( children: [ - Text(title, style: Styles.defaultTitleOverride), + Text(title, style: Styles.title), Text(index.toString()), const Text("Ensure device is kept still"), const Text( diff --git a/lib/views/components/assessment_card.dart b/lib/views/components/assessment_card.dart index 179781e3..abea4e2a 100644 --- a/lib/views/components/assessment_card.dart +++ b/lib/views/components/assessment_card.dart @@ -38,7 +38,10 @@ class AssessmentCard extends StatelessWidget { ), child: Padding( padding: const EdgeInsets.all(8), - child: Image(image: AssetImage(imagePath), fit: BoxFit.contain), + child: Image( + image: AssetImage(imagePath), + fit: BoxFit.contain, + ), ), ), ), @@ -47,8 +50,12 @@ class AssessmentCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(assessmentType.name, style: Styles.actionText), - Text(subtitle, style: Styles.subtitle), + Text(assessmentType.name, + style: available ? Styles.title : Styles.comingSoonTitle), + Text(subtitle, + style: available + ? Styles.subtitle + : Styles.comingSoonSubtitle), ], ), ), diff --git a/lib/views/home_view.dart b/lib/views/home_view.dart index 63919516..3e071673 100644 --- a/lib/views/home_view.dart +++ b/lib/views/home_view.dart @@ -19,9 +19,11 @@ class HomeView extends StatelessWidget { assessmentType: AssessmentType.CTSIB, subtitle: "3 minutes"), AssessmentCard( - imagePath: Styles.gvLogoPath, - assessmentType: AssessmentType.GAIT, - subtitle: "5 minutes"), + imagePath: Styles.gvLogoPath, + assessmentType: AssessmentType.GAIT, + subtitle: "5 minutes", + available: false, + ), ], ), ), diff --git a/lib/views/styles.dart b/lib/views/styles.dart index 5f039486..891cc664 100644 --- a/lib/views/styles.dart +++ b/lib/views/styles.dart @@ -17,7 +17,7 @@ class Styles { static const Color actionPrimary = Color(0xFF1F7A97); static const Color actionSecondary = Color(0x551F7A97); - static const Color comingSoonColor = Color(0x66000000); + static const Color comingSoonColor = Color(0x77777777); //Asset Paths static const String mainLogoPath = "assets/vpt_mainlogo.png"; @@ -25,9 +25,9 @@ class Styles { static const String gvLogoPath = "assets/gv_mainlogo.png"; //Text Styles - static const TextStyle subtitle = TextStyle(fontSize: 24); - static const TextStyle defaultTitleOverride = TextStyle( + static const TextStyle title = TextStyle( fontFamily: "Unbounded", fontSize: 32, fontWeight: FontWeight.bold); + static const TextStyle subtitle = TextStyle(fontSize: 24); static const TextStyle placeholderStyle = TextStyle(color: CupertinoColors.inactiveGray, fontSize: 24); static const TextStyle inputStyle = @@ -38,4 +38,9 @@ class Styles { color: CupertinoColors.white, fontSize: 18, fontWeight: FontWeight.bold); static const TextStyle popupSelectionText = TextStyle(color: CupertinoColors.black); + + static const TextStyle comingSoonTitle = TextStyle( + color: comingSoonColor, fontSize: 32, fontWeight: FontWeight.bold); + static const TextStyle comingSoonSubtitle = TextStyle( + color: comingSoonColor, fontSize: 24, fontWeight: FontWeight.bold); } From b13727945ae6e7a3b565858975571112411bccee Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Fri, 17 Feb 2023 23:59:19 -0500 Subject: [PATCH 37/43] cleaning layouts/warnings --- analysis_options.yaml | 4 ++ lib/views/assessment_view.dart | 4 +- lib/views/components/video_camera.dart | 93 +++++++++++++------------- 3 files changed, 51 insertions(+), 50 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 61b6c4de..4f3d45f7 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -9,6 +9,10 @@ # packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml +analyzer: + exclude: + - lib/models/* #Amplify autogenerated files + linter: # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` diff --git a/lib/views/assessment_view.dart b/lib/views/assessment_view.dart index 5417b9ec..e2a1740c 100644 --- a/lib/views/assessment_view.dart +++ b/lib/views/assessment_view.dart @@ -1,12 +1,10 @@ import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:visualpt/bloc/_bloc.dart'; import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/_views.dart'; import 'package:visualpt/views/components/_components.dart'; -import '../bloc/_bloc.dart'; - class AssessmentView extends StatelessWidget { final AssessmentType assessmentType; const AssessmentView({super.key, required this.assessmentType}); diff --git a/lib/views/components/video_camera.dart b/lib/views/components/video_camera.dart index aee4dabb..8c6540ac 100644 --- a/lib/views/components/video_camera.dart +++ b/lib/views/components/video_camera.dart @@ -9,14 +9,10 @@ import 'package:visualpt/views/components/_components.dart'; class VideoRecorder extends StatefulWidget { final String title; - final CameraController? controller; - final void Function(File f) onCaptureSaved; - const VideoRecorder( - {Key? key, - required this.title, - this.controller, - required this.onCaptureSaved}) - : super(key: key); + const VideoRecorder({ + Key? key, + required this.title, + }) : super(key: key); @override State createState() => _VideoRecorderState(); @@ -36,37 +32,37 @@ class _VideoRecorderState extends State super.dispose(); } - // #docregion AppLifecycle - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - final CameraController? cameraController = widget.controller; - - // App state changed before we got the chance to initialize. - if (cameraController == null || !cameraController.value.isInitialized) { - return; - } - - if (state == AppLifecycleState.inactive) { - cameraController.dispose(); - } else if (state == AppLifecycleState.resumed) { - // onNewCameraSelected(cameraController.description); - } - } + // // #docregion AppLifecycle + // @override + // void didChangeAppLifecycleState(AppLifecycleState state) { + // final CameraController? cameraController = widget.controller; - void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { - if (widget.controller == null) { - return; - } + // // App state changed before we got the chance to initialize. + // if (cameraController == null || !cameraController.value.isInitialized) { + // return; + // } - final CameraController cameraController = widget.controller!; + // if (state == AppLifecycleState.inactive) { + // cameraController.dispose(); + // } else if (state == AppLifecycleState.resumed) { + // // onNewCameraSelected(cameraController.description); + // } + // } - final Offset offset = Offset( - details.localPosition.dx / constraints.maxWidth, - details.localPosition.dy / constraints.maxHeight, - ); - cameraController.setExposurePoint(offset); - cameraController.setFocusPoint(offset); - } + // void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + // if (widget.controller == null) { + // return; + // } + + // final CameraController cameraController = widget.controller!; + + // final Offset offset = Offset( + // details.localPosition.dx / constraints.maxWidth, + // details.localPosition.dy / constraints.maxHeight, + // ); + // cameraController.setExposurePoint(offset); + // cameraController.setFocusPoint(offset); + // } // Future onNewCameraSelected(CameraDescription cameraDescription) async { // final CameraController? oldController = widget.controller; @@ -124,7 +120,7 @@ class _VideoRecorderState extends State try { if (state is CameraStandby || state is CameraRecording) { return cameraWidget(context, state); - } else { + } else if (state is CameraLoading) { //TODO Loading View or throws error message return BaseView( pageTitle: widget.title, @@ -137,6 +133,8 @@ class _VideoRecorderState extends State ), ), ); + } else { + throw Exception("Invalid State"); } } on Exception catch (e) { return BaseView( @@ -146,11 +144,12 @@ class _VideoRecorderState extends State children: [ Text(e.toString()), CupertinoButton( - onPressed: () { - BlocProvider.of(context) - .add(CameraInit()); - }, - child: const Text("Restart")), + onPressed: () { + BlocProvider.of(context) + .add(CameraInit()); + }, + child: const Text("Restart"), + ), ], ), ), @@ -175,11 +174,11 @@ class _VideoRecorderState extends State width: MediaQuery.of(context).size.width, child: CameraPreview( state.controller!, - child: LayoutBuilder( - builder: (_, constraints) => GestureDetector( - onTapDown: (details) => - onViewFinderTap(details, constraints), - )), + // child: LayoutBuilder( + // builder: (_, constraints) => GestureDetector( + // onTapDown: (details) => + // onViewFinderTap(details, constraints), + // )), ), ), Positioned( From 4a92d77a468eb0c94d3ea3915b3d2169e31afc1a Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 18 Feb 2023 05:49:10 -0500 Subject: [PATCH 38/43] updated bundle identifier --- ios/Runner.xcodeproj/project.pbxproj | 42 +---------- ios/Runner/Info.plist | 106 +++++++++++++-------------- 2 files changed, 57 insertions(+), 91 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 03a43bf8..4e4148dc 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -14,7 +14,6 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - DF9913B344CB67476283E499 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F24DF8C1B03080865541129 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -39,7 +38,6 @@ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 8344BFF53557D3E087D322A4 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 7C613CAEA31C604B21DC511E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -48,10 +46,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9F24DF8C1B03080865541129 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EA0411E3AF748213EFDFBDA0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - F8BB7CD19972C49A7D9CC042 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - DA543580F25D8B34350CB87F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + DA543580F25D8B34350CB87F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -66,17 +61,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 295C2E2AB62C11FDFA17AF2A /* Pods */ = { - isa = PBXGroup; - children = ( - EA0411E3AF748213EFDFBDA0 /* Pods-Runner.debug.xcconfig */, - 8344BFF53557D3E087D322A4 /* Pods-Runner.release.xcconfig */, - F8BB7CD19972C49A7D9CC042 /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; 02CAF58E4D1358DBC9A19049 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -137,7 +121,6 @@ DA543580F25D8B34350CB87F /* Pods-Runner.release.xcconfig */, 0269A0F4EB1031AC6BF050C0 /* Pods-Runner.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -283,23 +266,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - 9F4EC215FE88CD9D17F4CA0F /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -398,7 +364,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.visualpt; + PRODUCT_BUNDLE_IDENTIFIER = com.prod.visualpt; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -527,7 +493,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.visualpt; + PRODUCT_BUNDLE_IDENTIFIER = com.prod.visualpt; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -550,7 +516,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.visualpt; + PRODUCT_BUNDLE_IDENTIFIER = com.prod.visualpt; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index dcb1e9c3..c3c348e2 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,57 +1,57 @@ - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Visualpt - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - visualpt - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - NSCameraUsageDescription - VisualPT needs access to the camera to take photos and videos - NSBonjourServices - - _dartobservatory._tcp - - + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Visualpt + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + visualpt + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + NSBonjourServices + + _dartobservatory._tcp + + NSCameraUsageDescription + VisualPT needs access to the camera to take photos and videos + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + From b716c5d360a0b82f359abe4d4f3b5847ff8d2e9c Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 18 Feb 2023 05:52:00 -0500 Subject: [PATCH 39/43] assessment interface standardization --- lib/bloc/assessment/assessment_bloc.dart | 16 ++-- lib/views/assessment_view.dart | 24 ++++-- lib/views/assessments/_assessments.dart | 6 +- lib/views/components/video_camera.dart | 101 +++++++++++++++-------- lib/views/components/video_timer.dart | 32 ++----- 5 files changed, 102 insertions(+), 77 deletions(-) diff --git a/lib/bloc/assessment/assessment_bloc.dart b/lib/bloc/assessment/assessment_bloc.dart index 4d3d540d..ff39a687 100644 --- a/lib/bloc/assessment/assessment_bloc.dart +++ b/lib/bloc/assessment/assessment_bloc.dart @@ -15,8 +15,8 @@ class AssessmentBloc extends Bloc { late int assessmentCount; final BuildContext context; - late List introViews; - late List captureViews; + late List introViews; + late List captureViews; List assessmentAssets = []; List skeletalSequences = []; @@ -62,14 +62,16 @@ class AssessmentBloc extends Bloc { } } - Widget getIntroView(BuildContext context, int index) => - introViews[index](context); + Widget getIntroView(BuildContext context, String assessmentName, int index) => + introViews[index](context, assessmentName); - Widget getCaptureView(BuildContext context, int index) => - captureViews[index](context); + Widget getCaptureView( + BuildContext context, String assessmentName, int index) => + captureViews[index](context, assessmentName); } -void saveCameraDataToAssessment(AssessmentBloc assessment, CameraBloc camera) { +void saveCameraDataToAssessment( + BuildContext context, AssessmentBloc assessment, CameraBloc camera) { camera.add(CameraStop()); camera.getData().then((data) => assessment.add(AssessmentIncrement(data))); } diff --git a/lib/views/assessment_view.dart b/lib/views/assessment_view.dart index e2a1740c..8cbf1f25 100644 --- a/lib/views/assessment_view.dart +++ b/lib/views/assessment_view.dart @@ -27,22 +27,30 @@ class AssessmentView extends StatelessWidget { return PatientForm(assessmentType: assessmentType); //return PatientForm(assessmentType: assessmentType,child: ) } else if (state is AssessmentIntro) { - return BaseView( - pageTitle: "Performing ${assessmentType.name}", - child: BlocProvider.of(context) - .getIntroView(context, state.index)); + WidgetsBinding.instance.addPostFrameCallback( + (_) => Navigator.pop(context)); //Clear the loading screen + + return BlocProvider.of(context) + .getIntroView(context, assessmentType.name, state.index); } else if (state is AssessmentActive) { + WidgetsBinding.instance.addPostFrameCallback( + (_) => Navigator.pop(context)); //Clear the loading screen return BlocProvider.of(context) - .getCaptureView(context, state.index); + .getCaptureView(context, assessmentType.name, state.index); } else if (state is AssessmentSuccess) { - Navigator.pushReplacementNamed(context, "analysis", - arguments: [assessmentType.name, state.assessmentAssets]); + WidgetsBinding.instance.addPostFrameCallback((_) => + Navigator.pushReplacementNamed(context, "/analysis", + arguments: [ + assessmentType.name, + state.assessmentAssets + ])); + //TODO Return loading Page return const CupertinoActivityIndicator(); } else { throw Exception("Undefined State"); } - } on Exception catch (e) { + } catch (e) { return BaseView(pageTitle: "Error", child: Text(e.toString())); } }, diff --git a/lib/views/assessments/_assessments.dart b/lib/views/assessments/_assessments.dart index a5e2fae8..bd09f096 100644 --- a/lib/views/assessments/_assessments.dart +++ b/lib/views/assessments/_assessments.dart @@ -7,8 +7,8 @@ import 'package:visualpt/views/assessments/gait/gait.dart'; class AssessmentRouter { final AssessmentType assessmentType; - late List introWidgets; - late List captureWidgets; + late List introWidgets; + late List captureWidgets; late int assessmentCount; @@ -19,7 +19,7 @@ class AssessmentRouter { introWidgets = Ctsib().introContent(); captureWidgets = Ctsib().captureContent(); - assessmentCount = Ctsib().sequences; + assessmentCount = Ctsib.sequences; break; case AssessmentType.GAIT: //TODO assessmentWidgets = Gait().content(); diff --git a/lib/views/components/video_camera.dart b/lib/views/components/video_camera.dart index 8c6540ac..4fe4bbaf 100644 --- a/lib/views/components/video_camera.dart +++ b/lib/views/components/video_camera.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:camera/camera.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -8,10 +6,10 @@ import 'package:visualpt/views/_views.dart'; import 'package:visualpt/views/components/_components.dart'; class VideoRecorder extends StatefulWidget { - final String title; + final int sequence; const VideoRecorder({ Key? key, - required this.title, + required this.sequence, }) : super(key: key); @override @@ -122,36 +120,29 @@ class _VideoRecorderState extends State return cameraWidget(context, state); } else if (state is CameraLoading) { //TODO Loading View or throws error message - return BaseView( - pageTitle: widget.title, - child: Center( - child: Column( - children: const [ - Text("Loading"), - CupertinoActivityIndicator() - ], - ), + return Center( + child: Column( + children: const [ + Text("Loading"), + CupertinoActivityIndicator() + ], ), ); } else { throw Exception("Invalid State"); } } on Exception catch (e) { - return BaseView( - pageTitle: widget.title, - child: Center( - child: Column( - children: [ - Text(e.toString()), - CupertinoButton( - onPressed: () { - BlocProvider.of(context) - .add(CameraInit()); - }, - child: const Text("Restart"), - ), - ], - ), + return Center( + child: Column( + children: [ + Text(e.toString()), + CupertinoButton( + onPressed: () { + BlocProvider.of(context).add(CameraInit()); + }, + child: const Text("Restart"), + ), + ], ), ); } @@ -169,7 +160,6 @@ class _VideoRecorderState extends State final timerBloc = BlocProvider.of(context); return Stack(alignment: Alignment.center, children: [ - const ViewBackground(), SizedBox( width: MediaQuery.of(context).size.width, child: CameraPreview( @@ -182,12 +172,52 @@ class _VideoRecorderState extends State ), ), Positioned( - bottom: 10, - child: GestureDetector( + bottom: 0, + child: Container( + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.0)), + color: Styles.backgroundAccent), + child: Column( + children: [ + SizedBox( + width: MediaQuery.of(context).size.width, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: GestureDetector( + onTap: () => BlocProvider.of(context) + .add(AssessmentInit(widget.sequence)), + child: const Icon(CupertinoIcons.restart, + color: CupertinoColors.black, size: 40)), + ), + const Spacer(), + const Padding( + padding: EdgeInsets.all(8.0), + child: VideoTimer(), + ) + ], + ), + ), + const SizedBox( + height: 20, + ) + ], + ), + ), + ), + Positioned( + bottom: 20, + child: Center( + child: GestureDetector( onTap: () { if (state is CameraRecording) { + showCupertinoDialog( + context: context, builder: (context) => const AppLoading()); timerBloc.add(const TimerReset()); - saveCameraDataToAssessment(assessmentBloc, cameraBloc); + saveCameraDataToAssessment(context, assessmentBloc, cameraBloc); } else { cameraBloc.add(CameraPlay()); timerBloc.add(const TimerStarted()); @@ -197,9 +227,10 @@ class _VideoRecorderState extends State ? const Icon(CupertinoIcons.stop, color: Styles.backgroundComplementory, size: 80) : const Icon(CupertinoIcons.circle, - color: Styles.actionPrimary, size: 80)), - ), - const Positioned(top: 10, child: VideoTimer()) + color: Styles.actionPrimary, size: 80), + ), + ), + ) ]); } } diff --git a/lib/views/components/video_timer.dart b/lib/views/components/video_timer.dart index c77d7064..72126b42 100644 --- a/lib/views/components/video_timer.dart +++ b/lib/views/components/video_timer.dart @@ -13,33 +13,17 @@ class VideoTimer extends StatelessWidget { final assessmentBloc = BlocProvider.of(context); final cameraBloc = BlocProvider.of(context); - return SafeArea( - child: Container( - width: MediaQuery.of(context).size.width / - 4, //TODO ensure this works well on different screens - decoration: BoxDecoration( - color: Styles.backgroundComplementory, - borderRadius: BorderRadius.circular(4), - ), - child: Center( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: BlocBuilder( - builder: (context, state) { - if (state is TimerRunComplete) { - saveCameraDataToAssessment(assessmentBloc, cameraBloc); - } - return Text(formatter(state.duration), style: Styles.subtitle); - //TODO this style should be the font that has even spacing - }, - ), - ), - ), - ), + return BlocBuilder( + builder: (context, state) { + if (state is TimerRunComplete) { + saveCameraDataToAssessment(context, assessmentBloc, cameraBloc); + } + return Text(formatter(state.duration), style: Styles.subtitle); + //TODO this style should be the font that has even spacing + }, ); } } - String formatter(Duration duration) => [ duration.inSeconds.remainder(60).toString().padLeft(2, '0'), duration.inMilliseconds.remainder(1000).toString().padLeft(3, '0')[0] From 76ecca17ca6d1317fc1dc59e1cad9b4bf7dbe73c Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 18 Feb 2023 05:53:21 -0500 Subject: [PATCH 40/43] inital loading modal --- lib/views/components/_components.dart | 1 + lib/views/components/app_loading.dart | 69 ++++++++++++++++++++++++++ lib/views/components/patient_form.dart | 2 + 3 files changed, 72 insertions(+) create mode 100644 lib/views/components/app_loading.dart diff --git a/lib/views/components/_components.dart b/lib/views/components/_components.dart index 85f8bbc7..438f0de2 100644 --- a/lib/views/components/_components.dart +++ b/lib/views/components/_components.dart @@ -5,3 +5,4 @@ export 'video_camera.dart'; export 'shaker.dart'; export 'form_input.dart'; export 'video_timer.dart'; +export 'app_loading.dart'; diff --git a/lib/views/components/app_loading.dart b/lib/views/components/app_loading.dart new file mode 100644 index 00000000..f37900be --- /dev/null +++ b/lib/views/components/app_loading.dart @@ -0,0 +1,69 @@ +import 'package:flutter/cupertino.dart'; +import 'package:visualpt/views/_views.dart'; +import 'package:visualpt/views/components/_components.dart'; + +class AppLoading extends StatefulWidget { + const AppLoading({super.key}); + + @override + _AppLoadingState createState() => _AppLoadingState(); +} + +class _AppLoadingState extends State with TickerProviderStateMixin { + late AnimationController breathingController; + var breathe = 0.0; + + // @override + // void initState() { + // super.initState(); + + // breathingController = AnimationController( + // vsync: this, duration: const Duration(milliseconds: 1000)); + // breathingController.addStatusListener((status) { + // if (status == AnimationStatus.completed) { + // breathingController.reverse(); + // } else if (status == AnimationStatus.dismissed) { + // breathingController.forward(); + // } + // }); + + // breathingController.addListener(() { + // setState(() { + // breathe = breathingController.value; + // }); + // }); + // breathingController.forward(); + // } + + // @override + // void dispose() { + // super.dispose(); + // breathingController.dispose(); + // } + + @override + Widget build(BuildContext context) { + final size = 200.0 - 100.0 * breathe; + return Center( + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: RadialGradient( + center: Alignment.center, + colors: [ + Styles.backgroundPrimary.withOpacity(1.0), + Styles.backgroundPrimary.withOpacity(0.8), + Styles.backgroundPrimary.withOpacity(0.6), + Styles.backgroundPrimary.withOpacity(0.4), + Styles.backgroundPrimary.withOpacity(0.2), + Styles.backgroundPrimary.withOpacity(0.0), + ], + ), + ), + height: size, + child: const Image( + image: AssetImage(Styles.mainLogoPath), fit: BoxFit.contain), + ), + ); + } +} diff --git a/lib/views/components/patient_form.dart b/lib/views/components/patient_form.dart index 13ced9f8..f94a6f17 100644 --- a/lib/views/components/patient_form.dart +++ b/lib/views/components/patient_form.dart @@ -263,6 +263,8 @@ class _PatientFormState extends State { double.parse(heightController.text.split("\"")[0]), ); BlocProvider.of(context).add(AssessmentInit(0)); + showCupertinoDialog( + context: context, builder: (_) => const AppLoading()); //TODO store data to backend } }, From 475c88557c1b8a48cc36386c51e8c4fe0fdc8f78 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 18 Feb 2023 05:55:36 -0500 Subject: [PATCH 41/43] assessment abstraction/cleanup --- lib/views/assessments/ctsib/ctsib.dart | 163 ++++++++--- lib/views/assessments/ctsib/pdf.dart | 358 +++++++++++++++++++++++++ lib/views/assessments/gait/gait.dart | 16 +- lib/views/assessments/gait/pdf.dart | 101 +++---- lib/views/assessments/pdf.dart | 55 ++++ 5 files changed, 579 insertions(+), 114 deletions(-) create mode 100644 lib/views/assessments/ctsib/pdf.dart create mode 100644 lib/views/assessments/pdf.dart diff --git a/lib/views/assessments/ctsib/ctsib.dart b/lib/views/assessments/ctsib/ctsib.dart index 9522d0cf..a63ea2e3 100644 --- a/lib/views/assessments/ctsib/ctsib.dart +++ b/lib/views/assessments/ctsib/ctsib.dart @@ -1,63 +1,146 @@ -import 'dart:io'; - +import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:visualpt/bloc/_bloc.dart'; +import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/_views.dart'; import 'package:visualpt/views/components/_components.dart'; class Ctsib { - final sequences = 4; + static const sequences = 4; + Map norms = { + for (int i = 20; i <= 49; i++) i: 179.7, + for (int i = 50; i <= 59; i++) i: 180.0, + for (int i = 60; i <= 69; i++) i: 179.9, + for (int i = 70; i <= 79; i++) i: 177.7, + }; - List introContent() { +//TODO REMOVE THE INFORMATION FROM BEING PASSED ALL AROUND THE PLACE (ONLY PASS CONTEXT) + List introContent() { return [ - (context) => introView(context, "Eyes Open,\n Firm Surface", 1), - (context) => introView(context, "Eyes Closed,\n Firm Surface", 2), - (context) => introView(context, "Eyes Open,\n Foam Surface", 3), - (context) => introView(context, "Eyes Closed,\n Foam Surface", 4), + (context, name) => introView(context, name, "Eyes Open\nFirm Surface", 1), + (context, name) => + introView(context, name, "Eyes Closed\nFirm Surface", 2), + (context, name) => introView(context, name, "Eyes Open\nFoam Surface", 3), + (context, name) => + introView(context, name, "Eyes Closed\nFoam Surface", 4), ]; } - List captureContent() { + List captureContent() { return [ - (context) => captureView(context, "Eyes Open,\n Firm Surface", 1), - (context) => captureView(context, "Eyes Closed,\n Firm Surface", 2), - (context) => captureView(context, "Eyes Open,\n Foam Surface", 3), - (context) => captureView(context, "Eyes Closed,\n Foam Surface", 4), + (context, name) => + captureView(context, name, "Eyes Open\nFirm Surface", 1), + (context, name) => + captureView(context, name, "Eyes Closed\nFirm Surface", 2), + (context, name) => + captureView(context, name, "Eyes Open\nFoam Surface", 3), + (context, name) => + captureView(context, name, "Eyes Closed\nFoam Surface", 4), ]; } } -Widget introView(BuildContext context, String title, int index) => Builder( - builder: (context) => Column( +Widget introView( + BuildContext context, + String assessmentName, + String title, + int index, +) => + BaseView( + pageTitle: assessmentName, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(title, style: Styles.title), - Text(index.toString()), - const Text("Ensure device is kept still"), - const Text( - "Record all tests from same relative posiotion to patient"), - const Text("If error is made, press retake"), - Row( - children: [ - CupertinoButton( - onPressed: () => BlocProvider.of(context) - .add(AssessmentInit(index)), - child: const Text("ReInit"), - ), - CupertinoButton( - onPressed: () => BlocProvider.of(context) - .add(AssessmentBegin()), - child: const Text("Begin"), - ), - ], - ) + Text( + title, + style: Styles.title, + textAlign: TextAlign.center, + ), + Text("Criteria ${index.toString()} of ${Ctsib.sequences.toString()}", + style: Styles.subactionText), + const Spacer(), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text("Ensure device is kept still"), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + "Record all tests from same relative posiotion to patient", + textAlign: TextAlign.center), + ), + const Spacer(), + Padding( + padding: const EdgeInsets.all(16.0), + child: GestureDetector( + child: Container( + alignment: Alignment.center, + height: MediaQuery.of(context).size.width / 2, + width: MediaQuery.of(context).size.width / 2, + decoration: const BoxDecoration( + shape: BoxShape.circle, color: Styles.actionPrimary), + child: const Text("Begin", style: Styles.actionText), + ), + onTap: () { + showCupertinoDialog( + context: context, builder: (_) => const AppLoading()); + BlocProvider.of(context) + .add(AssessmentBegin()); + }), + ), + const Spacer() ], ), ); -Widget captureView(BuildContext context, String title, int index) => Builder( - builder: (context) => VideoRecorder( - title: title, - onCaptureSaved: (File f) => BlocProvider.of(context) - .add(AssessmentIncrement(f))), +Widget captureView( + BuildContext context, String assessmentName, String title, int index) => + Builder( + builder: (context) => Stack( + children: [ + VideoRecorder(sequence: index), + Positioned( + top: 0, + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: Container( + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.0)), + color: Styles.backgroundAccent), + child: SafeArea( + child: Text(title.replaceAll("\n", " "), + style: Styles.subtitle, textAlign: TextAlign.center), + ), + ), + ), + ), + ], + ), ); + +class CtsibNorms { + CtsibNorms._(); + //Numbers are in m/s and start in the 6-12, then 13-19, then 20-29, 30-39... to 80+ + static const maleNorm = [1.19, 1.2, 1.4, 1.48, 1.48, 1.4, 1.39, 1.34, 1.1]; + static const femaleNorm = [1.14, 1.2, 1.4, 1.4, 1.4, 1.39, 1.22, 1.19, 1.0]; + + static double getVelocityFromAge(Gender gender, TemporalDate dob) { + int age = getAge(dob); + final int index = (age / 10).round().clamp(0, 8); + return gender == Gender.MALE ? maleNorm[index] : femaleNorm[index]; + } + + static double getPercentDifferenceFromNorm( + double patientVelocity, Gender gender, TemporalDate dob) { + final double normVelocity = getVelocityFromAge(gender, dob); + return (((patientVelocity - normVelocity) / + ((patientVelocity + normVelocity) / 2)) * + 100) + .roundToDouble(); + } +} + +int getAge(TemporalDate dob) { + return 10; //TODO implement +} diff --git a/lib/views/assessments/ctsib/pdf.dart b/lib/views/assessments/ctsib/pdf.dart new file mode 100644 index 00000000..c9030cfd --- /dev/null +++ b/lib/views/assessments/ctsib/pdf.dart @@ -0,0 +1,358 @@ +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:flutter/services.dart'; +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart'; +import 'package:visualpt/models/ModelProvider.dart'; +import 'package:visualpt/views/assessments/ctsib/ctsib.dart'; + +import '../pdf.dart'; + +class CtsibPDF extends PDF { + @override + Future generatePdf(Patient patient, Assessment assessment) async { + await configPdfStyles(); + //final fyzLogo = await rootBundle.loadString('assets/fyzical-logo.svg'); + final gaitrLogo = await rootBundle.loadString('assets/gaitr-logo.svg'); + + final pdf = Document( + title: patient.family_name + patient.name + patient.dob.toString(), + creator: "VisualPT Systems"); + + pdf.addPage( + Page( + pageTheme: PageTheme( + pageFormat: PdfPageFormat.letter, + margin: EdgeInsets.all(halfinch), + ), + build: (Context context) => SizedBox( + height: 11 * inch, + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Header( + child: Column(children: [ + SizedBox( + height: inch, + width: 8.5 * inch, + child: Center( + child: SvgImage(svg: gaitrLogo, fit: BoxFit.fitHeight), + ), + ), + RichText( + text: TextSpan( + children: [ + TextSpan(text: "Gait Velocity Analysis", style: header), + ], + ), + overflow: TextOverflow.span), + ])), + SizedBox( + height: 3 * inch, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: disclaimer, + text: + "A distance of 10 meters is measured over a level surface with 2 meters for acceleration and 2 meters for deceleration.")), + Spacer(flex: 2), + SizedBox( + height: 0.65 * inch, + child: Row(children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + text: TextSpan(children: [ + TextSpan( + text: "Patient Name: ", style: detail), + TextSpan( + text: + "${patient.name} ${patient.family_name}", + style: detailBold), + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan( + text: "Birth Date: ", style: detail), + TextSpan( + text: patient.dob.toString(), + style: detailBold), + ]), + ), + ]), + Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + RichText( + text: TextSpan(children: [ + TextSpan(text: "Date: ", style: detail), + TextSpan( + text: assessment.timestamp + .toString(), //TODO timestamp=>datetime + style: detailBold), + ])), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Time: ", style: detail), + TextSpan( + text: assessment.timestamp.toString(), + style: detailBold), + ])), + ]), + ])), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Age: ", style: detail), + TextSpan( + text: patient.dob.toString(), + style: detailBold), //TODO DOB to age + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Gender: ", style: detail), + TextSpan( + text: patient.gender.toString(), style: detailBold), + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Measurement Method: ", style: detail), + TextSpan( + text: + "Video", //TODO add measurement method or fix to video + style: detailBold), + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Measurement Duration: ", style: detail), + TextSpan( + text: assessment.timestamp + .toSeconds() + .toString(), //TODO add duration type to + style: detailBold), + TextSpan(text: " seconds", style: detail), + ]), + ), + Spacer(), + RichText( + text: TextSpan(children: [ + TextSpan(text: "Gait Velocity: ", style: detail), + TextSpan( + text: "<<>>", style: detailBold), + TextSpan(text: " meters/second", style: detail), + ])), + ]), + ), + Spacer(), + SizedBox( + height: 0.75 * inch, + child: Row(children: [ + Stack( + alignment: Alignment.centerLeft, + children: [ + SizedBox( + height: inch * 1, + width: inch * 5, + child: Column(children: [ + Container( + height: inch * 0.25, + width: inch * 5, + foregroundDecoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + PdfColor.fromHex("#1b365d"), + PdfColor.fromHex("#1d3d68"), + PdfColor.fromHex("#1f4573"), + PdfColor.fromHex("#214d7e"), + PdfColor.fromHex("#225589"), + PdfColor.fromHex("#22659f"), + PdfColor.fromHex("#206eab"), + PdfColor.fromHex("#1d76b7"), + PdfColor.fromHex("#197fc2"), + PdfColor.fromHex("#1088ce"), + PdfColor.fromHex("#0091da"), + ], + ), + ), + ), + Row( + children: axisLabel(), + ), + RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: disclaimer, text: "Gait Velocity (m/s)"), + ), + Spacer(), + ]), + ), + Positioned( + top: 0.1, + left: positioner("<<>>"), + child: Container( + height: 0.25 * inch, + width: 0.25 * inch, + decoration: BoxDecoration( + color: PdfColor.fromRYB(1, 0, 0), + shape: BoxShape.circle), + ), + ), + Positioned( + top: 0.1, + left: positioner(CtsibNorms.getVelocityFromAge( + patient.gender, patient.dob) + .toString()), + child: Container( + height: 0.25 * inch, + width: 0.25 * inch, + decoration: BoxDecoration( + color: PdfColor.fromRYB(0, 1, 1), + shape: BoxShape.circle), + ), + ), + ], + ), + Spacer(), + Container( + decoration: BoxDecoration( + border: Border.all(), + borderRadius: BorderRadius.circular(2.0)), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(children: [ + Container( + height: 0.25 * inch, + width: 0.25 * inch, + decoration: BoxDecoration( + color: PdfColor.fromRYB(1, 0, 0), + shape: BoxShape.circle), + ), + Text(" - Patient"), + ]), + Spacer(), + Row(children: [ + Container( + height: 0.25 * inch, + width: 0.25 * inch, + decoration: BoxDecoration( + color: PdfColor.fromRYB(0, 1, 1), + shape: BoxShape.circle), + ), + Text(" - Average Peer"), + ]), + Spacer(), + ]), + )) + ]), + ), + Spacer(), + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: detailBold, + text: getComparisonMessage(30, patient.gender, patient.dob), + ), + ), + ), + Spacer(flex: 2), + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: subTitle, + text: 10 < 1 // TODO FIX WITH MODEL OUTPUT + ? "Needs intervention to reduce risk of fall" + : "Low likelihood of experiencing a fall"), + ), + ), + Spacer(flex: 4), + Footer( + padding: EdgeInsets.zero, + margin: EdgeInsets.zero, + leading: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText(text: const TextSpan(text: " ")), + RichText( + text: TextSpan( + style: disclaimer, + text: "© 2022 Gaitr Measurement Softwares")), + RichText( + text: TextSpan( + style: disclaimer, + text: "No Distribution without Permission")), + RichText( + text: TextSpan( + style: disclaimer, + text: + "Reference: Fritz, Stacy & Lusardi, Michelle. (2009).\n Walking speed: The sixth vital sign. J Geriatr Phys Ther. 32. 46-49. ")) + ]), + trailing: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + RichText( + text: TextSpan( + style: disclaimer, + text: "Minimal Detectable Change: 0.1m/sec")), + RichText( + text: TextSpan( + style: disclaimer, + text: + "Minimum Clinically Important Difference: 0.1m/sec")), + RichText( + text: TextSpan( + style: disclaimer, + text: "Test/Re-test Reliability: ICC > 0.7")) + ], + ), + ), + ]), + ), + ), + ); + return pdf.save(); + } + + List axisLabel() { + final values = List.generate(9, (i) => i * 0.2) + .map((value) => value >= 1.0 + ? Text(value.toStringAsPrecision(2)) + : Text(value.toStringAsPrecision(1))) + .toList(); + return List.generate( + 17, (i) => i % 2 == 0 ? values[i ~/ 2] : Spacer()); + } + + double positioner(String velocity) => + (2.95 * inch * double.parse(velocity)).clamp(0 * inch, 4.75 * inch); +} + +String getComparisonMessage(double velocity, Gender gender, TemporalDate dob) { + double percent = + CtsibNorms.getPercentDifferenceFromNorm(velocity, gender, dob); + int age = getAge(dob); + int index = (age / 10).round(); + String speedComparison = percent > 0 ? "faster" : "slower"; + String ageRange = index > 8 ? "80+" : "${index}0 - ${index}9"; + return percent.abs() > 5.0 + ? "Among $ageRange year olds, you are ${percent.abs()}% $speedComparison than average." + : "Your gait velocity matches your peer group aged $ageRange."; +} diff --git a/lib/views/assessments/gait/gait.dart b/lib/views/assessments/gait/gait.dart index 637b0b41..8c686fa8 100644 --- a/lib/views/assessments/gait/gait.dart +++ b/lib/views/assessments/gait/gait.dart @@ -1,4 +1,6 @@ +import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:flutter/cupertino.dart'; +import 'package:visualpt/models/ModelProvider.dart'; class Gait { final sequences = 1; @@ -24,14 +26,15 @@ class GaitNorms { static const maleNorm = [1.19, 1.2, 1.4, 1.48, 1.48, 1.4, 1.39, 1.34, 1.1]; static const femaleNorm = [1.14, 1.2, 1.4, 1.4, 1.4, 1.39, 1.22, 1.19, 1.0]; - static double getVelocityFromAge(bool isMale, int age) { + static double getVelocityFromAge(Gender gender, TemporalDate dob) { + int age = getAge(dob); final int index = (age / 10).round().clamp(0, 8); - return isMale ? maleNorm[index] : femaleNorm[index]; + return gender == Gender.MALE ? maleNorm[index] : femaleNorm[index]; } static double getPercentDifferenceFromNorm( - double patientVelocity, bool isMale, int age) { - final double normVelocity = getVelocityFromAge(isMale, age); + double patientVelocity, Gender gender, TemporalDate dob) { + final double normVelocity = getVelocityFromAge(gender, dob); return (((patientVelocity - normVelocity) / ((patientVelocity + normVelocity) / 2)) * 100) @@ -39,6 +42,11 @@ class GaitNorms { } } +int getAge(TemporalDate dob) { + return 10; //TODO implement +} + +//TOOD PHASE OUT ASAP class GaitPatientData { late String managingtherapistEmail = 'sample@example.com'; late String firstname = 'sample'; diff --git a/lib/views/assessments/gait/pdf.dart b/lib/views/assessments/gait/pdf.dart index 19ed3425..ae8a605e 100644 --- a/lib/views/assessments/gait/pdf.dart +++ b/lib/views/assessments/gait/pdf.dart @@ -1,66 +1,22 @@ //DO NOT IMPORT material.dart without renaming it with keyword, will conflict with pdf/widgets.dart +import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:flutter/services.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart'; -import 'package:printing/printing.dart'; +import 'package:visualpt/models/ModelProvider.dart'; import 'package:visualpt/views/assessments/gait/_gait.dart'; -class GaitPdf { - late Font baseFont; - late Font boldFont; - late Font italicFont; - late Font boldItalicFont; +import '../pdf.dart'; - late PdfColor primaryColor; - late PdfColor secondaryColor; - late PdfColor black; - - late BorderSide borderSide; - - final double inch = 72.0; - final double halfinch = 36.0; - - late TextStyle header; - late TextStyle headerItalic; - late TextStyle subTitle; - late TextStyle detail; - late TextStyle detailBold; - late TextStyle subHeader; - late TextStyle disclaimer; - - Future configPdfStyles() async { - baseFont = await PdfGoogleFonts.openSansRegular(); - boldFont = await PdfGoogleFonts.openSansBold(); - italicFont = await PdfGoogleFonts.openSansItalic(); - boldItalicFont = await PdfGoogleFonts.openSansBoldItalic(); - - primaryColor = PdfColor.fromHex('#008ed6'); - secondaryColor = PdfColor.fromHex('#24446c'); - black = PdfColors.black; - - borderSide = BorderSide(width: 2, color: secondaryColor); - - header = TextStyle(font: boldFont, fontSize: 30.0, lineSpacing: 2.0); - headerItalic = - TextStyle(font: boldItalicFont, fontSize: 30.0, lineSpacing: 2.0); - subTitle = TextStyle(font: boldFont, fontSize: 20.0, lineSpacing: 2.0); - detail = TextStyle(font: baseFont, fontSize: 15.0, lineSpacing: 2.0); - detailBold = TextStyle(font: boldFont, fontSize: 15.0, lineSpacing: 2.0); - subHeader = TextStyle(font: boldFont, fontSize: 50.0, lineSpacing: 2.0); - disclaimer = TextStyle(font: baseFont, fontSize: 10.0, lineSpacing: 2.0); - return; - } - - Future generatePdf(GaitPatientData patientData) async { +class GaitPDF extends PDF { + @override + Future generatePdf(Patient patient, Assessment assessment) async { await configPdfStyles(); - //final fyzLogo = await rootBundle.loadString('assets/fyzical-logo.svg'); final gaitrLogo = await rootBundle.loadString('assets/gaitr-logo.svg'); final pdf = Document( - title: patientData.lastname + - patientData.firstname + - patientData.age.toString(), - creator: "gaitr Systems"); + title: patient.family_name + patient.name + patient.dob.toString(), + creator: "VisualPT Systems"); pdf.addPage( Page( @@ -113,7 +69,7 @@ class GaitPdf { text: "Patient Name: ", style: detail), TextSpan( text: - "${patientData.firstname} ${patientData.lastname}", + "${patient.name} ${patient.family_name}", style: detailBold), ]), ), @@ -123,7 +79,7 @@ class GaitPdf { TextSpan( text: "Birth Date: ", style: detail), TextSpan( - text: patientData.bday, + text: patient.dob.toString(), style: detailBold), ]), ), @@ -136,7 +92,8 @@ class GaitPdf { text: TextSpan(children: [ TextSpan(text: "Date: ", style: detail), TextSpan( - text: patientData.date, + text: assessment.timestamp + .toString(), //TODO timestamp=>datetime style: detailBold), ])), Spacer(), @@ -144,7 +101,7 @@ class GaitPdf { text: TextSpan(children: [ TextSpan(text: "Time: ", style: detail), TextSpan( - text: patientData.time, + text: assessment.timestamp.toString(), style: detailBold), ])), ]), @@ -153,7 +110,9 @@ class GaitPdf { RichText( text: TextSpan(children: [ TextSpan(text: "Age: ", style: detail), - TextSpan(text: patientData.age, style: detailBold), + TextSpan( + text: patient.dob.toString(), + style: detailBold), //TODO DOB to age ]), ), Spacer(), @@ -161,8 +120,7 @@ class GaitPdf { text: TextSpan(children: [ TextSpan(text: "Gender: ", style: detail), TextSpan( - text: patientData.isMale ? "Male" : "Female", - style: detailBold), + text: patient.gender.toString(), style: detailBold), ]), ), Spacer(), @@ -170,7 +128,8 @@ class GaitPdf { text: TextSpan(children: [ TextSpan(text: "Measurement Method: ", style: detail), TextSpan( - text: patientData.isVideo ? "Video" : "Stopwatch", + text: + "Video", //TODO add measurement method or fix to video style: detailBold), ]), ), @@ -179,8 +138,9 @@ class GaitPdf { text: TextSpan(children: [ TextSpan(text: "Measurement Duration: ", style: detail), TextSpan( - text: patientData.measurementDuration - .toStringAsPrecision(3), + text: assessment.timestamp + .toSeconds() + .toString(), //TODO add duration type to style: detailBold), TextSpan(text: " seconds", style: detail), ]), @@ -189,7 +149,8 @@ class GaitPdf { RichText( text: TextSpan(children: [ TextSpan(text: "Gait Velocity: ", style: detail), - TextSpan(text: patientData.velocity, style: detailBold), + TextSpan( + text: "<<>>", style: detailBold), TextSpan(text: " meters/second", style: detail), ])), ]), @@ -239,7 +200,7 @@ class GaitPdf { ), Positioned( top: 0.1, - left: positioner(patientData.velocity), + left: positioner("<<>>"), child: Container( height: 0.25 * inch, width: 0.25 * inch, @@ -251,7 +212,7 @@ class GaitPdf { Positioned( top: 0.1, left: positioner(GaitNorms.getVelocityFromAge( - patientData.isMale, int.parse(patientData.age)) + patient.gender, patient.dob) .toString()), child: Container( height: 0.25 * inch, @@ -305,8 +266,7 @@ class GaitPdf { textAlign: TextAlign.center, text: TextSpan( style: detailBold, - text: getComparisonMessage(double.parse(patientData.velocity), - patientData.isMale, int.parse(patientData.age)), + text: getComparisonMessage(30, patient.gender, patient.dob), ), ), ), @@ -316,7 +276,7 @@ class GaitPdf { textAlign: TextAlign.center, text: TextSpan( style: subTitle, - text: double.parse(patientData.velocity) < 1 + text: 10 < 1 // TODO FIX WITH MODEL OUTPUT ? "Needs intervention to reduce risk of fall" : "Low likelihood of experiencing a fall"), ), @@ -385,9 +345,10 @@ class GaitPdf { (2.95 * inch * double.parse(velocity)).clamp(0 * inch, 4.75 * inch); } -String getComparisonMessage(double velocity, bool isMale, int age) { +String getComparisonMessage(double velocity, Gender gender, TemporalDate dob) { double percent = - GaitNorms.getPercentDifferenceFromNorm(velocity, isMale, age); + GaitNorms.getPercentDifferenceFromNorm(velocity, gender, dob); + int age = getAge(dob); int index = (age / 10).round(); String speedComparison = percent > 0 ? "faster" : "slower"; String ageRange = index > 8 ? "80+" : "${index}0 - ${index}9"; diff --git a/lib/views/assessments/pdf.dart b/lib/views/assessments/pdf.dart new file mode 100644 index 00000000..2a1c7245 --- /dev/null +++ b/lib/views/assessments/pdf.dart @@ -0,0 +1,55 @@ +import 'dart:typed_data'; + +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart'; +import 'package:printing/printing.dart'; +import 'package:visualpt/models/ModelProvider.dart'; + +abstract class PDF { + late Font baseFont; + late Font boldFont; + late Font italicFont; + late Font boldItalicFont; + + late PdfColor primaryColor; + late PdfColor secondaryColor; + late PdfColor black; + + late BorderSide borderSide; + + final double inch = 72.0; + final double halfinch = 36.0; + + late TextStyle header; + late TextStyle headerItalic; + late TextStyle subTitle; + late TextStyle detail; + late TextStyle detailBold; + late TextStyle subHeader; + late TextStyle disclaimer; + + Future configPdfStyles() async { + baseFont = await PdfGoogleFonts.openSansRegular(); + boldFont = await PdfGoogleFonts.openSansBold(); + italicFont = await PdfGoogleFonts.openSansItalic(); + boldItalicFont = await PdfGoogleFonts.openSansBoldItalic(); + + primaryColor = PdfColor.fromHex('#008ed6'); + secondaryColor = PdfColor.fromHex('#24446c'); + black = PdfColors.black; + + borderSide = BorderSide(width: 2, color: secondaryColor); + + header = TextStyle(font: boldFont, fontSize: 30.0, lineSpacing: 2.0); + headerItalic = + TextStyle(font: boldItalicFont, fontSize: 30.0, lineSpacing: 2.0); + subTitle = TextStyle(font: boldFont, fontSize: 20.0, lineSpacing: 2.0); + detail = TextStyle(font: baseFont, fontSize: 15.0, lineSpacing: 2.0); + detailBold = TextStyle(font: boldFont, fontSize: 15.0, lineSpacing: 2.0); + subHeader = TextStyle(font: boldFont, fontSize: 50.0, lineSpacing: 2.0); + disclaimer = TextStyle(font: baseFont, fontSize: 10.0, lineSpacing: 2.0); + return; + } + + Future generatePdf(Patient patient, Assessment assessment); +} From df92198a8fbc997517640ed23108883d569b1759 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 18 Feb 2023 05:56:58 -0500 Subject: [PATCH 42/43] init pdf process --- lib/bloc/pdf/pdf_cubit.dart | 49 +++++++++ lib/bloc/pdf/pdf_state.dart | 16 +++ lib/views/analysis_view.dart | 205 ++++++++++++++++++++++++++++++++++- lib/views/styles.dart | 33 +++++- pubspec.lock | 16 +++ pubspec.yaml | 2 + 6 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 lib/bloc/pdf/pdf_cubit.dart create mode 100644 lib/bloc/pdf/pdf_state.dart diff --git a/lib/bloc/pdf/pdf_cubit.dart b/lib/bloc/pdf/pdf_cubit.dart new file mode 100644 index 00000000..bba0cb9a --- /dev/null +++ b/lib/bloc/pdf/pdf_cubit.dart @@ -0,0 +1,49 @@ +import 'dart:developer'; +import 'package:meta/meta.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_pdfview/flutter_pdfview.dart'; +import 'package:visualpt/models/ModelProvider.dart'; +import 'package:visualpt/views/assessments/ctsib/pdf.dart'; +import 'package:visualpt/views/assessments/gait/_gait.dart'; + +part 'pdf_state.dart'; + +///TODO BUGGGY CODE +class PdfCubit extends Cubit { + PdfCubit() : super(PdfLoading()); + bool viewCreated = false; + + void initPDFView(Patient patient, Assessment assessment) async { + var document; + emit(PdfLoading()); + switch (assessment.type) { + case AssessmentType.CTSIB: + document = CtsibPDF(); + break; + case AssessmentType.GAIT: + document = GaitPDF(); + + break; + default: + } + try { + await document + .configPdfStyles() + .then((_) => document.generatePdf(patient, assessment)) + .then((pdfData) { + PDFView view = PDFView( + enableSwipe: false, + pdfData: pdfData, + pageFling: false, + onError: (error) => emit(PdfError(exception: error)), + onPageError: (page, error) => emit(PdfError(exception: error)), + pageSnap: false); + emit(PdfLoaded(pdfView: view)); + }); + } on Exception catch (error) { + emit(PdfError(exception: error)); + } catch (e) { + log(e.toString()); + } + } +} diff --git a/lib/bloc/pdf/pdf_state.dart b/lib/bloc/pdf/pdf_state.dart new file mode 100644 index 00000000..f09a7a74 --- /dev/null +++ b/lib/bloc/pdf/pdf_state.dart @@ -0,0 +1,16 @@ +part of 'pdf_cubit.dart'; + +@immutable +abstract class PdfState {} + +class PdfLoading extends PdfState {} + +class PdfLoaded extends PdfState { + final PDFView pdfView; + PdfLoaded({required this.pdfView}); +} + +class PdfError extends PdfState { + final Exception exception; + PdfError({required this.exception}); +} diff --git a/lib/views/analysis_view.dart b/lib/views/analysis_view.dart index 0c1a7ee7..63169c38 100644 --- a/lib/views/analysis_view.dart +++ b/lib/views/analysis_view.dart @@ -1,8 +1,15 @@ +import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; -import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:http/http.dart' as http; import 'package:visualpt/views/_views.dart'; +import '../bloc/pdf/pdf_cubit.dart'; + class AnalysisView extends StatelessWidget { final String assessmentType; final List assessmentAssets; @@ -10,6 +17,7 @@ class AnalysisView extends StatelessWidget { {super.key, required this.assessmentType, required this.assessmentAssets}); + final double height = 60; @override Widget build(BuildContext context) { @@ -19,3 +27,198 @@ class AnalysisView extends StatelessWidget { ); } } + +// class PdfPage extends StatefulWidget { +// const PdfPage({Key? key}) : super(key: key); + +// @override +// State createState() => _PdfPageState(); +// } + +// class _PdfPageState extends State { +// @override +// Widget build(BuildContext context) { +// final screenSize = MediaQuery.of(context).size; +// return CupertinoPageScaffold( +// child: Stack( +// alignment: Alignment.topCenter, +// children: [ +// BlocProvider( +// create: (context) => PdfCubit()..initPDFView(), +// child: BlocBuilder( +// builder: (context, state) { +// if (state is PdfLoaded) { +// return SafeArea( +// child: Stack(alignment: Alignment.center, children: [ +// SizedBox( +// height: screenSize.height - 30, +// width: screenSize.width - 20), +// Positioned( +// top: 20, +// child: Container( +// padding: const EdgeInsets.all(10.0), +// decoration: BoxDecoration( +// color: CupertinoColors.systemBackground, +// border: Border.all(), +// borderRadius: BorderRadius.circular(8.0), +// ), +// child: const Text("Generated PDF", +// style: Styles.titleTextStyle)), +// ), +// SizedBox( +// width: screenSize.width - 20, +// child: AspectRatio( +// aspectRatio: 8.5 / 11, +// child: state.pdfView, +// ), +// ), +// Positioned( +// bottom: 0, +// left: 0, +// child: SizedBox( +// width: screenSize.width - 20, +// child: Row(children: [ +// CupertinoButton( +// padding: const EdgeInsets.all(13.0), +// color: +// Styles.brandTertiaryOrange.withOpacity(0.8), +// onPressed: () { +// consentDialog( +// context, +// "Clear patient data", +// "Do you want to repeat the analysis?", +// "No", +// "Yes", +// () => Navigator.pop(context), +// () => Navigator.pushNamedAndRemoveUntil( +// context, +// '/measurement', +// ModalRoute.withName('/'), +// arguments: patient.isVideo)); +// }, +// child: +// const Icon(CupertinoIcons.restart, size: 35), +// ), +// const Spacer(), +// SizedBox( +// child: CupertinoButton( +// padding: EdgeInsets.symmetric( +// vertical: 13, +// horizontal: (screenSize.width / 4) - 50), +// color: CupertinoColors.link, +// onPressed: () => ensureEmail().then((value) => +// consentDialog( +// context, +// "Save patient gait data", +// "Email will be sent to: ${patient.managingtherapistEmail}", +// "No", +// "Yes", +// () => Navigator.pop(context), () { +// Navigator.of(context).push( +// CupertinoPageRoute( +// builder: (BuildContext context) => +// const LoadingPage( +// message: "Sending email", +// isLoading: true)), +// ); +// try { +// prepareEmail(context, state); +// } catch (e) { +// log(e.toString()); +// } +// })), +// child: const Text("Save to email", +// style: Styles.buttonLabelStyle), +// ), +// ) +// ]), +// ), +// ) +// ]), +// ); +// } else if (state is PdfLoading) { +// return const Center( +// child: CupertinoActivityIndicator(), +// ); +// } else if (state is PdfError) { +// return Center( +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Text(state.exception.toString()), +// CupertinoButton( +// child: const Text("Retry"), +// onPressed: () => BlocProvider.of(context) +// .initPDFView()) +// ], +// ), +// ); +// } else { +// return const Center( +// child: Text("Invalid PDF State"), +// ); +// } +// }, +// ), +// ), +// ], +// ), +// ); +// } + +// void prepareEmail(BuildContext context, PdfLoaded state) { +// patientData.rawReportData = +// uint8ListTob64(state.pdfView.pdfData!).toString(); + +// sendEmail().then( +// (response) { +// log(response.body); +// if (response.body == 'OK') { +// Navigator.pushNamedAndRemoveUntil( +// context, +// '/', +// ModalRoute.withName('/'), +// ); +// } +// }, +// ); +// } +// } + +// Future sendEmail() async { +// const serviceId = "service_1j62fhe"; +// const templateId = "template_95smzbm"; +// const userId = "_SUuxjVW52hmODTRs"; + +// final url = Uri.parse("https://api.emailjs.com/api/v1.0/email/send"); +// try { +// return await http.post( +// url, +// headers: { +// 'origin': 'http://localhost', +// 'Content-type': 'application/json' +// }, +// body: json.encode( +// { +// 'service_id': serviceId, +// 'template_id': templateId, +// 'user_id': userId, +// 'template_params': { +// 'patient_name': "${patientData.firstname} ${patientData.lastname}", +// 'patient_dob': patientData.bday, +// 'user_email': patientData.managingtherapistEmail, +// 'patient_pdf': patientData.rawReportData, +// } +// }, +// ), +// ); +// } catch (e) { +// return http.Response(e.toString(), 404); +// } +// } + +// String uint8ListTob64(Uint8List uint8list) { +// String base64String = base64Encode(uint8list); +// String header = 'data:application/pdf;base64,'; +// return header + base64String; +// } diff --git a/lib/views/styles.dart b/lib/views/styles.dart index 891cc664..caf72e1c 100644 --- a/lib/views/styles.dart +++ b/lib/views/styles.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:google_fonts/google_fonts.dart'; ///Singleton class for consistency of styles across the app. Access styles through Styles. class Styles { @@ -26,7 +27,10 @@ class Styles { //Text Styles static const TextStyle title = TextStyle( - fontFamily: "Unbounded", fontSize: 32, fontWeight: FontWeight.bold); + fontFamily: "Unbounded", + fontSize: 32, + fontWeight: FontWeight.bold, + ); static const TextStyle subtitle = TextStyle(fontSize: 24); static const TextStyle placeholderStyle = TextStyle(color: CupertinoColors.inactiveGray, fontSize: 24); @@ -43,4 +47,31 @@ class Styles { color: comingSoonColor, fontSize: 32, fontWeight: FontWeight.bold); static const TextStyle comingSoonSubtitle = TextStyle( color: comingSoonColor, fontSize: 24, fontWeight: FontWeight.bold); + + //////////?GAITR STYLES//////////// + static const Color transparent = Color(0x00000000); + static const Color black = CupertinoColors.black; + static const Color white = CupertinoColors.white; + static const Color brandPrimary = Color(0xFF0091DA); + static const Color brandSecondaryDark = Color(0xFF1B365D); + static const Color brandTertiaryOrange = Color(0xFFEC7723); + + static const TextStyle inputPlaceholderStyle = + TextStyle(fontWeight: FontWeight.w200, color: black); + + static const TextStyle inputTextStyle = + TextStyle(fontWeight: FontWeight.w400, color: black, fontSize: 15); + + static const TextStyle inputPromptStyle = TextStyle( + fontWeight: FontWeight.bold, + color: CupertinoColors.label, + fontSize: 15.0); + + static const TextStyle buttonLabelStyle = TextStyle(fontSize: 30); + + static const TextStyle titleTextStyle = + TextStyle(fontWeight: FontWeight.bold, fontSize: 20); + + static TextStyle timerTextStyle = GoogleFonts.robotoMono( + fontWeight: FontWeight.bold, fontSize: 20, color: white.withOpacity(0.8)); } diff --git a/pubspec.lock b/pubspec.lock index 587e42b9..8b990f07 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -302,6 +302,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + flutter_pdfview: + dependency: "direct main" + description: + name: flutter_pdfview + sha256: d819ff9b5497da744edb41f73b2b827adf5214d5f0a68923a60f7fa30d0d3f16 + url: "https://pub.dev" + source: hosted + version: "1.2.5" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -320,6 +328,14 @@ packages: description: flutter source: sdk version: "0.0.0" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + sha256: "927573f2e8a8d65c17931e21918ad0ab0666b1b636537de7c4932bdb487b190f" + url: "https://pub.dev" + source: hosted + version: "4.0.3" helpers: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 21faf854..74f8cfe5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,8 @@ dependencies: equatable: ^2.0.5 pdf: ^3.9.0 printing: ^5.10.1 + flutter_pdfview: ^1.2.5 + google_fonts: ^4.0.3 dev_dependencies: flutter_test: From 51448017544c7c323f289cd29690f899032bca32 Mon Sep 17 00:00:00 2001 From: charlieforward9 <62311337+charlieforward9@users.noreply.github.com> Date: Sat, 18 Feb 2023 05:57:10 -0500 Subject: [PATCH 43/43] minor changes --- lib/views/auth_view.dart | 24 +++++++++++++----------- lib/views/base_view.dart | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/views/auth_view.dart b/lib/views/auth_view.dart index 21cf7290..c0fa11ac 100644 --- a/lib/views/auth_view.dart +++ b/lib/views/auth_view.dart @@ -25,17 +25,19 @@ class _AuthViewState extends State { create: (context) => AuthBloc(), child: BlocBuilder( builder: (context, state) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (state is AuthSuccess) { - //Sync with Amplify services - context - .read() - .close(); //TODO save auth data before closing - Navigator.pushReplacementNamed(context, "/home"); - } else if (state is AuthError) { - _shakeKey.currentState?.shake(); - } - }); + // WidgetsBinding.instance.addPostFrameCallback((_) { + if (state is AuthSuccess) { + Future(() async { + Navigator.pushNamedAndRemoveUntil( + context, "/home", (_) => false); + }); + //Sync with Amplify services + // context + // .read() + // .close(); //TODO save auth data before closing + } else if (state is AuthError) { + _shakeKey.currentState?.shake(); + } if (state is AuthSignUp) { return signupForm(context); } else { diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart index b584faa7..1709d11e 100644 --- a/lib/views/base_view.dart +++ b/lib/views/base_view.dart @@ -40,7 +40,7 @@ class BaseView extends StatelessWidget { )), //TODO make this better and animatable Positioned( - top: screenSize.height / 4, + top: screenSize.height / 5, width: screenSize.width, child: SizedBox( //TODO dynamically make the instead of this hard coded whack chat