├── assets ├── .gitkeep ├── map.png ├── upload.png ├── battery.png ├── pipeline.png ├── prediction.png └── architecture.png ├── source ├── .gitkeep ├── deploy │ ├── assets │ │ ├── .gitkeep │ │ ├── battery-locations.csv │ │ └── connected-batteries.json │ ├── cdk.json │ ├── tsconfig.json │ ├── src │ │ ├── config │ │ │ ├── index.ts │ │ │ └── git-context.ts │ │ ├── constructs │ │ │ ├── CONSTRUCT.template │ │ │ ├── map-construct.ts │ │ │ ├── ddb-table-construct.ts │ │ │ ├── wafv2-attachments.ts │ │ │ ├── lambda-function-construct.ts │ │ │ ├── apigatewayv2-lambda-construct.ts │ │ │ ├── schema.graphql │ │ │ ├── ssm-parameter-reader-construct.ts │ │ │ ├── wafv2-basic-construct.ts │ │ │ ├── eventbridge-lambda-contruct.ts │ │ │ └── s3-library-construct.ts │ │ ├── cf-waf-stack.ts │ │ ├── cdk-nag-suppressions.ts │ │ └── app.ts │ ├── build.py │ └── package.json ├── web-app │ ├── src │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ ├── icons │ │ │ │ ├── car.png │ │ │ │ ├── popin.png │ │ │ │ ├── popout.png │ │ │ │ ├── power.png │ │ │ │ ├── battery.png │ │ │ │ ├── capacity.png │ │ │ │ ├── current.png │ │ │ │ ├── impedance.png │ │ │ │ ├── voltage.png │ │ │ │ ├── battery-alt.png │ │ │ │ ├── temperature.png │ │ │ │ ├── charging-time.png │ │ │ │ ├── empty-battery.png │ │ │ │ ├── steering-wheel.png │ │ │ │ └── battery-charging.png │ │ │ ├── images │ │ │ │ ├── car.png │ │ │ │ ├── doc.png │ │ │ │ ├── charging.png │ │ │ │ ├── launch.png │ │ │ │ ├── persona.png │ │ │ │ ├── pipeline.png │ │ │ │ ├── upload.png │ │ │ │ ├── back-arrow.png │ │ │ │ ├── error-step.png │ │ │ │ ├── need-charging.png │ │ │ │ ├── not-charging.png │ │ │ │ ├── tracker-step1.png │ │ │ │ ├── tracker-step2.png │ │ │ │ ├── tracker-step3.png │ │ │ │ ├── tracker-step4.png │ │ │ │ ├── tracker-step5.png │ │ │ │ ├── tracker-step6.png │ │ │ │ ├── tracker-step7.png │ │ │ │ ├── completed-step.png │ │ │ │ ├── inprogress-step.png │ │ │ │ ├── notstarted-step.png │ │ │ │ ├── battery-charging.png │ │ │ │ ├── inprogress-half-step.png │ │ │ │ ├── inprogress-step-alt.png │ │ │ │ ├── tracker-disabled-step2.png │ │ │ │ ├── tracker-disabled-step3.png │ │ │ │ ├── tracker-disabled-step4.png │ │ │ │ ├── tracker-disabled-step5.png │ │ │ │ ├── tracker-disabled-step6.png │ │ │ │ └── tracker-disabled-step7.png │ │ │ ├── logos │ │ │ │ └── logo.png │ │ │ └── fonts │ │ │ │ ├── AmazonEmber_Bd.ttf │ │ │ │ ├── AmazonEmberMono_Bd.ttf │ │ │ │ ├── AmazonEmberDisplay_Bd.ttf │ │ │ │ ├── AmazonEmberDisplay_Rg.ttf │ │ │ │ ├── AmazonEmberDisplay_BdIt.ttf │ │ │ │ └── AmazonEmberDisplay_RgIt.ttf │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── modules │ │ │ │ └── shared │ │ │ │ │ ├── components │ │ │ │ │ ├── breadcrumb │ │ │ │ │ │ ├── breadcrumb.component.html │ │ │ │ │ │ ├── breadcrumb.component.scss │ │ │ │ │ │ └── breadcrumb.component.ts │ │ │ │ │ ├── footer │ │ │ │ │ │ ├── footer.component.html │ │ │ │ │ │ ├── footer.component.scss │ │ │ │ │ │ └── footer.component.ts │ │ │ │ │ ├── spinner │ │ │ │ │ │ ├── spinner.component.html │ │ │ │ │ │ ├── spinner.component.ts │ │ │ │ │ │ └── spinner.component.scss │ │ │ │ │ └── header │ │ │ │ │ │ ├── header.component.html │ │ │ │ │ │ ├── header.component.scss │ │ │ │ │ │ └── header.component.ts │ │ │ │ │ └── shared.module.ts │ │ │ ├── components │ │ │ │ ├── login │ │ │ │ │ ├── login.component.scss │ │ │ │ │ ├── login.component.html │ │ │ │ │ └── login.component.ts │ │ │ │ ├── pipeline │ │ │ │ │ ├── components │ │ │ │ │ │ ├── tabs │ │ │ │ │ │ │ ├── tabs.component.html │ │ │ │ │ │ │ ├── tabs.component.scss │ │ │ │ │ │ │ └── tabs.component.ts │ │ │ │ │ │ ├── dataset-selection │ │ │ │ │ │ │ ├── dataset-selection.component.html │ │ │ │ │ │ │ └── dataset-selection.component.ts │ │ │ │ │ │ └── plugin-selection │ │ │ │ │ │ │ ├── plugin-selection.component.html │ │ │ │ │ │ │ └── plugin-selection.component.ts │ │ │ │ │ └── pipeline.component.html │ │ │ │ ├── dashboard │ │ │ │ │ └── components │ │ │ │ │ │ ├── battery-charge │ │ │ │ │ │ ├── battery-charge.component.html │ │ │ │ │ │ ├── battery-charge.component.scss │ │ │ │ │ │ └── battery-charge.component.ts │ │ │ │ │ │ └── indicator │ │ │ │ │ │ ├── indicator.component.html │ │ │ │ │ │ ├── indicator.component.ts │ │ │ │ │ │ └── indicator.component.scss │ │ │ │ ├── map │ │ │ │ │ ├── map.component.html │ │ │ │ │ ├── map.component.scss │ │ │ │ │ └── map.component.ts │ │ │ │ ├── tracker │ │ │ │ │ ├── tracker.component.html │ │ │ │ │ └── tracker.component.scss │ │ │ │ └── history │ │ │ │ │ ├── history.component.scss │ │ │ │ │ └── history.component.html │ │ │ ├── models │ │ │ │ ├── user-persona.ts │ │ │ │ └── pipeline-status.ts │ │ │ ├── services │ │ │ │ ├── user-preference.service.ts │ │ │ │ ├── config.service.ts │ │ │ │ ├── auth.service.ts │ │ │ │ └── data.service.ts │ │ │ ├── interceptor │ │ │ │ └── auth.interceptor.ts │ │ │ ├── guards │ │ │ │ ├── persona.guard.ts │ │ │ │ └── auth.guard.ts │ │ │ ├── app.component.ts │ │ │ └── app-routing.module.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── graphql │ │ │ ├── subscriptions.graphql │ │ │ ├── mutations.graphql │ │ │ └── queries.graphql │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── main.ts │ │ ├── test.ts │ │ └── polyfills.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ ├── .eslintrc.js │ ├── custom-webpack.config.ts │ ├── tsconfig.json │ ├── build.py │ ├── README.md │ ├── karma.conf.js │ ├── package.json │ └── angular.json ├── tsconfig.json ├── package.json ├── api │ ├── package.json │ ├── build.py │ └── lib │ │ ├── locationDataFn.ts │ │ ├── initPipelineFn.ts │ │ ├── closePipelineFn.ts │ │ ├── cleanExportsFn.ts │ │ ├── trainModelFn.ts │ │ ├── processDataFn.ts │ │ └── exportPredsFn.ts └── build.py ├── deployment └── .gitkeep ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── .github ├── solutionid_validator.sh └── workflows │ └── maintainer_workflows.yml ├── .gitignore ├── LICENSE └── CONTRIBUTING.md /assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /deployment/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/deploy/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/web-app/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/web-app/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /assets/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/assets/map.png -------------------------------------------------------------------------------- /source/web-app/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | .main-container { 2 | width: 100%; 3 | height: 100%; 4 | background: #2D343D; 5 | overflow-x: hidden; 6 | } -------------------------------------------------------------------------------- /assets/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/assets/upload.png -------------------------------------------------------------------------------- /assets/battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/assets/battery.png -------------------------------------------------------------------------------- /assets/pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/assets/pipeline.png -------------------------------------------------------------------------------- /assets/prediction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/assets/prediction.png -------------------------------------------------------------------------------- /assets/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/assets/architecture.png -------------------------------------------------------------------------------- /source/web-app/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/favicon.ico -------------------------------------------------------------------------------- /source/deploy/assets/battery-locations.csv: -------------------------------------------------------------------------------- 1 | b1,13.3833,52.5167,Germany,Berlin,VSTG4323PMC000011 2 | b2,10.0000,53.5500,Germany,Hamburg,VSTG4323GT4565611 3 | b3,11.5755,48.1372,Germany,Munich,VSTG4323PMC8768711 -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/car.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/car.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/doc.png -------------------------------------------------------------------------------- /source/web-app/src/assets/logos/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/logos/logo.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/popin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/popin.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/popout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/popout.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/power.png -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/breadcrumb/breadcrumb.component.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/battery.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/capacity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/capacity.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/current.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/impedance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/impedance.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/voltage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/voltage.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/charging.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/launch.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/persona.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/persona.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/pipeline.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/upload.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/battery-alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/battery-alt.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/temperature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/temperature.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/back-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/back-arrow.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/error-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/error-step.png -------------------------------------------------------------------------------- /source/web-app/src/assets/fonts/AmazonEmber_Bd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/fonts/AmazonEmber_Bd.ttf -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/charging-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/charging-time.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/empty-battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/empty-battery.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/steering-wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/steering-wheel.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/need-charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/need-charging.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/not-charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/not-charging.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-step1.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-step2.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-step3.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-step4.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-step5.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-step6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-step6.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-step7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-step7.png -------------------------------------------------------------------------------- /source/web-app/src/assets/icons/battery-charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/icons/battery-charging.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/completed-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/completed-step.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/inprogress-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/inprogress-step.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/notstarted-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/notstarted-step.png -------------------------------------------------------------------------------- /source/web-app/src/assets/fonts/AmazonEmberMono_Bd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/fonts/AmazonEmberMono_Bd.ttf -------------------------------------------------------------------------------- /source/web-app/src/assets/images/battery-charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/battery-charging.png -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | CODEOWNERS @aws-solutions-library-samples/maintainers 2 | /.github/workflows/maintainer_workflows.yml @aws-solutions-library-samples/maintainers 3 | /.github/solutionid_validator.sh @aws-solutions-library-samples/maintainers 4 | -------------------------------------------------------------------------------- /source/web-app/src/assets/fonts/AmazonEmberDisplay_Bd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/fonts/AmazonEmberDisplay_Bd.ttf -------------------------------------------------------------------------------- /source/web-app/src/assets/fonts/AmazonEmberDisplay_Rg.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/fonts/AmazonEmberDisplay_Rg.ttf -------------------------------------------------------------------------------- /source/web-app/src/assets/images/inprogress-half-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/inprogress-half-step.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/inprogress-step-alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/inprogress-step-alt.png -------------------------------------------------------------------------------- /source/web-app/src/assets/fonts/AmazonEmberDisplay_BdIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/fonts/AmazonEmberDisplay_BdIt.ttf -------------------------------------------------------------------------------- /source/web-app/src/assets/fonts/AmazonEmberDisplay_RgIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/fonts/AmazonEmberDisplay_RgIt.ttf -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-disabled-step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-disabled-step2.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-disabled-step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-disabled-step3.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-disabled-step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-disabled-step4.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-disabled-step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-disabled-step5.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-disabled-step6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-disabled-step6.png -------------------------------------------------------------------------------- /source/web-app/src/assets/images/tracker-disabled-step7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions-library-samples/guidance-for-electric-vehicle-battery-health-prediction-on-aws/HEAD/source/web-app/src/assets/images/tracker-disabled-step7.png -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/breadcrumb/breadcrumb.component.scss: -------------------------------------------------------------------------------- 1 | .navigation { 2 | cursor: pointer; 3 | padding: 2em 2em 0 1em; 4 | 5 | span { 6 | font-size: 24px; 7 | } 8 | img { 9 | padding-right: 16px; 10 | } 11 | } -------------------------------------------------------------------------------- /source/web-app/src/app/components/login/login.component.scss: -------------------------------------------------------------------------------- 1 | .login-container { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | margin: auto; 7 | height: 100%; 8 | width: 600px; 9 | 10 | .copyright h4 { 11 | font-weight: 300;; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/pipeline/components/tabs/tabs.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
{{tab.text}} 6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /source/web-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /source/web-app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.github/solutionid_validator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #set -e 3 | 4 | echo "checking solution id $1" 5 | echo "grep -nr --exclude-dir='.github' "$1" ./.." 6 | result=$(grep -nr --exclude-dir='.github' "$1" ./..) 7 | if [ $? -eq 0 ] 8 | then 9 | echo "Solution ID $1 found\n" 10 | echo "$result" 11 | exit 0 12 | else 13 | echo "Solution ID $1 not found" 14 | exit 1 15 | fi 16 | 17 | export result 18 | -------------------------------------------------------------------------------- /source/web-app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### vsCode ### 2 | .vscode 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | # Files that might appear in the root of a volume 10 | .DocumentRevisions-V100 11 | .fseventsd 12 | .Spotlight-V100 13 | .TemporaryItems 14 | .Trashes 15 | .VolumeIcon.icns 16 | .com.apple.timemachine.donotpresent 17 | 18 | **/node_modules 19 | **/dist 20 | **/build 21 | **/.DS_Store 22 | **/.angular 23 | **/.idea 24 | cdk.out 25 | **/data/**.csv 26 | 27 | .env 28 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/login/login.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 8 | 9 | 12 |
-------------------------------------------------------------------------------- /source/deploy/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts ./src/app.ts", 3 | "watch": { 4 | "include": ["./src"] 5 | }, 6 | "context": { 7 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 8 | "@aws-cdk/core:stackRelativeExports": true, 9 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 10 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 11 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/spinner/spinner.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |

8 |
9 |
10 |
-------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/footer/footer.component.scss: -------------------------------------------------------------------------------- 1 | footer { 2 | position: fixed; 3 | bottom: 0; 4 | width: 100%; 5 | z-index: 10; 6 | } 7 | .footer-container { 8 | display: flex; 9 | justify-content: space-between; 10 | align-items: center; 11 | color: #fff; 12 | background: #1B1E20; 13 | height: 40px; 14 | padding: 0 2em; 15 | font-size: 14px; 16 | left: 0; 17 | right: 0; 18 | bottom: 0; 19 | position: absolute; 20 | border-top: 1px solid #2D343D; 21 | 22 | 23 | h4 { 24 | font-weight: 300; 25 | } 26 | span { 27 | font-weight: 400; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/dashboard/components/battery-charge/battery-charge.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{['FSOC', 'SOC'].includes(metric) ? 'State of Charge' : 'RUL'}} 4 | {{mValue}}% 5 |
6 |
7 |
8 |
9 | 0%50%100% 10 |
11 |
12 |
-------------------------------------------------------------------------------- /.github/workflows/maintainer_workflows.yml: -------------------------------------------------------------------------------- 1 | # Workflows managed by aws-solutions-library-samples maintainers 2 | name: Maintainer Workflows 3 | on: 4 | # Triggers the workflow on push or pull request events but only for the "main" branch 5 | push: 6 | branches: [ "main" ] 7 | pull_request: 8 | branches: [ "main" ] 9 | types: [opened, reopened, edited] 10 | 11 | jobs: 12 | CheckSolutionId: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Run solutionid validator 17 | run: | 18 | chmod u+x ./.github/solutionid_validator.sh 19 | ./.github/solutionid_validator.sh ${{ vars.SOLUTIONID }} -------------------------------------------------------------------------------- /source/web-app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended" 9 | ], 10 | "overrides": [], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": "latest", 14 | "sourceType": "module" 15 | }, 16 | "plugins": [ 17 | "@typescript-eslint" 18 | ], 19 | "rules": { 20 | "@typescript-eslint/no-explicit-any": "off", 21 | "@typescript-eslint/ban-ts-comment": "off", 22 | "@typescript-eslint/no-var-requires": "off" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/web-app/src/graphql/subscriptions.graphql: -------------------------------------------------------------------------------- 1 | # this is an auto generated file. This will be overwritten 2 | subscription PipelineSub { 3 | pipelineSub { 4 | Id 5 | PipelineStatus 6 | StatusUpdatedAt 7 | UserId 8 | DataUploadedAt 9 | PluginUploadedAt 10 | ProcFinishedAt 11 | DataImportedAt 12 | TrainingFinishedAt 13 | ForecastGeneratedAt 14 | PredictionsExportedAt 15 | DataGroupArn 16 | PreProcessingId 17 | RawDataSize 18 | RawDataUri 19 | PluginScriptUri 20 | TrainingDataUri 21 | PredictorArn 22 | ForecastArn 23 | ModelDrift 24 | ExportJobArn 25 | PostProcessingId 26 | OriginalDatasetName 27 | OriginalPluginName 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/dashboard/components/indicator/indicator.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{metric.includes('SOH') ? 'State of Health' : 'Remaining Useful Life'}} 4 | {{mValue}}% 5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 0%50%100% 14 |
15 |
-------------------------------------------------------------------------------- /source/web-app/src/app/models/user-persona.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | export enum UserPersona { 17 | FLEET_OPERATOR = 1, 18 | DEVELOPER 19 | } 20 | -------------------------------------------------------------------------------- /source/web-app/src/graphql/mutations.graphql: -------------------------------------------------------------------------------- 1 | # this is an auto generated file. This will be overwritten 2 | mutation Pipeline($input: PipelineRequestInput!) { 3 | pipeline(input: $input) { 4 | Id 5 | PipelineStatus 6 | StatusUpdatedAt 7 | UserId 8 | DataUploadedAt 9 | PluginUploadedAt 10 | ProcFinishedAt 11 | DataImportedAt 12 | TrainingFinishedAt 13 | ForecastGeneratedAt 14 | PredictionsExportedAt 15 | DataGroupArn 16 | PreProcessingId 17 | RawDataSize 18 | RawDataUri 19 | PluginScriptUri 20 | TrainingDataUri 21 | PredictorArn 22 | ForecastArn 23 | ModelDrift 24 | ExportJobArn 25 | PostProcessingId 26 | OriginalDatasetName 27 | OriginalPluginName 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-root", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "start": "cd web-app && npm run start", 6 | "build": "python3 build.py", 7 | "build.api": "python3 build.py --api", 8 | "build.web": "python3 build.py --web", 9 | "build.deploy": "python3 build.py --deploy", 10 | "deploy.bootstrap": "cd deploy && npm install && npm run bootstrap", 11 | "deploy.cicd": "cd cicd && npm install && npm run cdk deploy -- --require-approval never", 12 | "destroy.cicd": "cd cicd && npm install && npm run cdk destroy", 13 | "deploy": "cd deploy && npm run cdk deploy -- -c stack_name=\"${STACK_NAME:-}\" --all --require-approval never", 14 | "destroy": "cd deploy && npm run cdk destroy -- -c stack_name=\"${STACK_NAME:-}\" --all" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /source/web-app/custom-webpack.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | const DotEnv = require('dotenv-webpack'); 17 | 18 | module.exports = { 19 | plugins: [ 20 | new DotEnv({systemvars: true}) 21 | ] 22 | } -------------------------------------------------------------------------------- /source/deploy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ES2018", 5 | "module": "commonjs", 6 | "lib": ["es2018"], 7 | "declaration": true, 8 | "strict": true, 9 | "noImplicitAny": true, 10 | "strictNullChecks": true, 11 | "noImplicitThis": true, 12 | "alwaysStrict": true, 13 | "noUnusedLocals": false, 14 | "noUnusedParameters": false, 15 | "noImplicitReturns": true, 16 | "noFallthroughCasesInSwitch": false, 17 | "inlineSourceMap": true, 18 | "inlineSources": true, 19 | "experimentalDecorators": true, 20 | "strictPropertyInitialization": false, 21 | "typeRoots": ["./node_modules/@types"] 22 | }, 23 | "type": "module", 24 | "include": ["./src"] 25 | } 26 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/login/login.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Component} from "@angular/core"; 17 | 18 | @Component({ 19 | selector: "app-login", 20 | templateUrl: "./login.component.html", 21 | styleUrls: ["./login.component.scss"], 22 | }) 23 | export class LoginComponent { 24 | } 25 | -------------------------------------------------------------------------------- /source/web-app/src/app/models/pipeline-status.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | export enum PipelineStatus { 17 | DATASET_UPLOADED = 1, 18 | PROCESSING_DATASET, 19 | IMPORTING_DATASET, 20 | TRAINING_PREDICTOR, 21 | GENERATING_FORECASTS, 22 | EXPORTING_PREDICTIONS, 23 | CLEANING_EXPORTS, 24 | PIPELINE_FINISHED 25 | } 26 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Component} from '@angular/core'; 17 | 18 | @Component({ 19 | selector: 'app-footer', 20 | templateUrl: './footer.component.html', 21 | styleUrls: ['./footer.component.scss'] 22 | }) 23 | export class FooterComponent { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /source/web-app/src/app/services/user-preference.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import { Injectable } from '@angular/core'; 17 | import {UserPersona} from "../models/user-persona"; 18 | 19 | @Injectable({ 20 | providedIn: 'root' 21 | }) 22 | export class UserPreferenceService { 23 | 24 | currentPersona = UserPersona.DEVELOPER; 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /source/web-app/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | export const environment = { 17 | production: true, 18 | NG_APP_NAME: process.env['CUSTOMER_NAME'] ?? 'Smart Battery Management System', 19 | NG_APP_LOGO: process.env['CUSTOMER_LOGO'] ?? '/assets/logos/logo.png', 20 | NG_APP_API: "", 21 | NG_APP_REGION: "", 22 | NG_APP_USER_POOL_ID: "", 23 | NG_APP_IDENTITY_POOL_ID: "", 24 | NG_APP_APP_CLIENT_ID: "", 25 | }; 26 | -------------------------------------------------------------------------------- /source/web-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "lib": [ 22 | "ES2022", 23 | "dom" 24 | ], 25 | "allowSyntheticDefaultImports": true 26 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/deploy/src/config/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import { getGitContext } from "./git-context"; 17 | 18 | // returns configuration provider functions by name 19 | export const getConfigProvider = (configType: "git") => { 20 | switch (configType) { 21 | case "git": 22 | return getGitContext; 23 | 24 | default: 25 | throw new Error(`Config provider ${configType} does not exist. Please choose another`); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /source/web-app/src/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import { enableProdMode } from "@angular/core"; 17 | import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; 18 | 19 | import { AppModule } from "./app/app.module"; 20 | import { environment } from "./environments/environment"; 21 | 22 | if (environment.production) { 23 | enableProdMode(); 24 | } 25 | 26 | platformBrowserDynamic() 27 | .bootstrapModule(AppModule) 28 | .catch((err) => console.error(err)); 29 | -------------------------------------------------------------------------------- /source/web-app/src/app/services/config.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | import {Injectable} from "@angular/core"; 16 | import {HttpClient} from "@angular/common/http"; 17 | import {environment} from "../../environments/environment"; 18 | 19 | @Injectable({ 20 | providedIn: "root", 21 | }) 22 | export class ConfigService { 23 | constructor(private http: HttpClient) {} 24 | 25 | getConfig() { 26 | return this.http.get(`${environment.NG_APP_API}/api/amplify-config`); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "apis for smart battery manager", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "watch": "tsc -w", 9 | "test": "jest", 10 | "cdk": "cdk", 11 | "lint": "eslint src/" 12 | }, 13 | "devDependencies": { 14 | "@types/ws": "^8.5.8", 15 | "@typescript-eslint/eslint-plugin": "^6.9.1", 16 | "@typescript-eslint/parser": "^6.9.1", 17 | "aws-sdk": "^2.1486.0", 18 | "constructs": "^10.3.0", 19 | "eslint": "^8.52.0", 20 | "eslint-config-prettier": "^9.0.0", 21 | "eslint-plugin-prettier": "^5.0.1", 22 | "source-map-support": "^0.5.21", 23 | "ts-node": "^10.9.1", 24 | "typescript": "^4.9.5" 25 | }, 26 | "dependencies": { 27 | "@aws-sdk/client-dynamodb": "^3.441.0", 28 | "@aws-sdk/client-forecast": "^3.441.0", 29 | "@aws-sdk/client-glue": "^3.441.0", 30 | "@aws-sdk/client-s3": "^3.441.0", 31 | "@aws-sdk/credential-providers": "^3.441.0", 32 | "@aws-sdk/s3-request-presigner": "^3.441.0", 33 | "csv-parser": "^3.0.0", 34 | "ws": "^8.14.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/web-app/build.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | """ 15 | 16 | import os 17 | import subprocess 18 | import sys 19 | import shutil 20 | 21 | 22 | def exit_on_failure(exit_code, msg): 23 | if exit_code != 0: 24 | print(msg) 25 | exit(exit_code) 26 | 27 | npm_cmd = shutil.which("npm") 28 | cmd = [npm_cmd, "install"] 29 | proc = subprocess.run(cmd, stderr=subprocess.STDOUT) 30 | exit_on_failure(proc.returncode, "Web app npm install failed") 31 | 32 | cmd = [npm_cmd, "run", "build"] 33 | proc = subprocess.run(cmd, stderr=subprocess.STDOUT) 34 | exit_on_failure(proc.returncode, "Web app build failed") -------------------------------------------------------------------------------- /source/web-app/src/app/components/pipeline/components/tabs/tabs.component.scss: -------------------------------------------------------------------------------- 1 | .tabs-container { 2 | .tabs { 3 | display: flex; 4 | justify-content: center; 5 | margin: 0; 6 | 7 | .tab { 8 | color: #FF9900; 9 | width: 137px; 10 | height: 36px; 11 | padding: 0 16px; 12 | border: 2px solid #FF9900; 13 | font-size: 14px; 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | 18 | &:hover { 19 | cursor: pointer; 20 | background: #EC7211; 21 | border-color: #EC7211; 22 | color: #fff; 23 | } 24 | 25 | &:first-child { 26 | border-top-left-radius: 4px; 27 | border-bottom-left-radius: 4px; 28 | //border-right: 0; 29 | } 30 | &:last-child { 31 | border-top-right-radius: 4px; 32 | border-bottom-right-radius: 4px; 33 | //border-left: 0; 34 | } 35 | &:not(:first-child):not(:last-child) { 36 | //border-left: 0; 37 | //border-right: 0; 38 | } 39 | } 40 | .active { 41 | background: #FF9900; 42 | color: #fff; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /source/deploy/build.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | """ 15 | 16 | import os 17 | import subprocess 18 | import sys 19 | import shutil 20 | 21 | 22 | def exit_on_failure(exit_code, msg): 23 | if exit_code != 0: 24 | print(msg) 25 | exit(exit_code) 26 | 27 | 28 | npm_cmd = shutil.which("npm") 29 | npx_cmd = shutil.which("npx") 30 | 31 | cmd = [npm_cmd, "install"] 32 | proc = subprocess.run(cmd, stderr=subprocess.STDOUT) 33 | exit_on_failure(proc.returncode, "Cdk npm install failed") 34 | 35 | cmd = [npx_cmd, "cdk", "synth"] 36 | proc = subprocess.run(cmd, stderr=subprocess.STDOUT) 37 | exit_on_failure(proc.returncode, "Cdk synth failed") -------------------------------------------------------------------------------- /source/web-app/src/app/components/pipeline/components/tabs/tabs.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Component, EventEmitter, Input, Output} from '@angular/core'; 17 | 18 | @Component({ 19 | selector: 'app-tabs', 20 | templateUrl: './tabs.component.html', 21 | styleUrls: ['./tabs.component.scss'] 22 | }) 23 | export class TabsComponent { 24 | selectedTab = 1; 25 | 26 | @Input('tabs')tabs: any[] | undefined; 27 | @Output() tabSelectionNotifier = new EventEmitter(); 28 | 29 | notifySelection(tabId: number) { 30 | this.selectedTab = tabId; 31 | this.tabSelectionNotifier.emit(this.selectedTab); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/map/map.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Connected Vehicles

5 | Click on a vehicle to view battery details 6 |
7 |
8 |
9 |
10 |
11 | Charging 12 | 01 13 |
14 |
15 |
16 |
17 |
18 | Below 20% 19 | 01 20 |
21 |
22 |
23 |
24 |
25 | Not Charging 26 | 00 27 |
28 |
29 |
30 |
31 |
32 | Total Vehicles 33 | {{locations.length < 10 ? 0 : ''}}{{locations.length}} 34 |
35 |
36 |
-------------------------------------------------------------------------------- /source/deploy/src/constructs/CONSTRUCT.template: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | import { Construct } from "constructs"; 18 | 19 | /* eslint-disable @typescript-eslint/no-empty-interface */ 20 | export interface {REPLACE}ConstructProps extends cdk.StackProps { 21 | 22 | } 23 | 24 | const defaultProps: Partial<{REPLACE}ConstructProps> = {} 25 | 26 | /** 27 | * Deploys the {REPLACE} construct 28 | */ 29 | export class {REPLACE}Construct extends Construct { 30 | constructor(parent: Construct, name: string, props: {REPLACE}ConstructProps) { 31 | super(parent, name); 32 | 33 | /* eslint-disable @typescript-eslint/no-unused-vars */ 34 | props = { ...defaultProps, ...props }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/spinner/spinner.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import { Component, Input, OnChanges, SimpleChanges } from "@angular/core"; 17 | 18 | @Component({ 19 | selector: "app-spinner", 20 | templateUrl: "./spinner.component.html", 21 | styleUrls: ["./spinner.component.scss"], 22 | }) 23 | export class SpinnerComponent implements OnChanges { 24 | @Input() displayText: string | undefined; 25 | @Input() position: 'fixed' | 'inline' = 'fixed'; 26 | status = ""; 27 | 28 | ngOnChanges(changes: SimpleChanges) { 29 | if (changes["displayText"].currentValue !== changes["displayText"].previousValue) { 30 | this.status += `

${changes["displayText"].currentValue}

`; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/deploy/src/constructs/map-construct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | import { Construct } from "constructs"; 18 | 19 | const map = cdk.aws_location; 20 | 21 | export class MapConstruct extends Construct { 22 | public cfnMap: cdk.aws_location.CfnMap; 23 | 24 | constructor(parent: Construct, name: string, props: any) { 25 | super(parent, name); 26 | 27 | const stack = cdk.Stack.of(this); 28 | const ts = new Date().getTime(); 29 | this.cfnMap = new map.CfnMap(this, name, { 30 | mapName: `${stack}-battery-location-map-${ts}`, 31 | configuration: { 32 | style: "VectorEsriNavigation", 33 | }, 34 | description: "Locations of each battery", 35 | pricingPlan: "RequestBasedUsage", 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /source/deploy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "build": "tsc", 6 | "watch": "tsc -w", 7 | "test": "jest", 8 | "cdk": "cdk", 9 | "bootstrap": "cdk bootstrap --cloudformation-execution-policies 'arn:aws:iam::aws:policy/PowerUserAccess,arn:aws:iam::aws:policy/IAMFullAccess'", 10 | "lint": "eslint src/" 11 | }, 12 | "devDependencies": { 13 | "@aws-cdk/aws-apigatewayv2-alpha": "^2.63.2-alpha.0", 14 | "@aws-cdk/aws-apigatewayv2-authorizers-alpha": "^2.63.2-alpha.0", 15 | "@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.63.2-alpha.0", 16 | "@aws-cdk/aws-lambda-python-alpha": "^2.63.2-alpha.0", 17 | "@types/git-branch": "^2.0.4", 18 | "@types/git-repo-name": "^1.0.3", 19 | "@types/jest": "^27.5.2", 20 | "@types/node": "^16.18.60", 21 | "@typescript-eslint/eslint-plugin": "5.22.0", 22 | "@typescript-eslint/parser": "5.22.0", 23 | "aws-cdk": "^2.103.1", 24 | "aws-cdk-lib": "^2.103.1", 25 | "aws-sdk": "^2.1486.0", 26 | "cdk-nag": "^2.27.179", 27 | "constructs": "^10.3.0", 28 | "eslint": "^8.52.0", 29 | "eslint-config-prettier": "8.5.0", 30 | "eslint-plugin-prettier": "4.0.0", 31 | "git-branch": "^2.0.1", 32 | "git-repo-name": "^1.0.1", 33 | "jest": "^27.5.1", 34 | "source-map-support": "^0.5.21", 35 | "ts-node": "^10.9.1", 36 | "typescript": "^4.9.5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/breadcrumb/breadcrumb.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Component, Input} from '@angular/core'; 17 | import {Router} from "@angular/router"; 18 | 19 | type ClickHandler = () => void; 20 | 21 | @Component({ 22 | selector: 'app-breadcrumb', 23 | templateUrl: './breadcrumb.component.html', 24 | styleUrls: ['./breadcrumb.component.scss'] 25 | }) 26 | export class BreadcrumbComponent { 27 | 28 | @Input() text: string | undefined; 29 | @Input() route: any; 30 | @Input() clickHandler: ClickHandler | undefined 31 | constructor(private router: Router) { } 32 | 33 | navigate() { 34 | if (this.route) { 35 | this.router.navigateByUrl(this.route).then(); 36 | } else if (this.clickHandler) { 37 | this.clickHandler(); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /source/web-app/src/test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 17 | 18 | import 'zone.js/testing'; 19 | import { getTestBed } from '@angular/core/testing'; 20 | import { 21 | BrowserDynamicTestingModule, 22 | platformBrowserDynamicTesting 23 | } from '@angular/platform-browser-dynamic/testing'; 24 | 25 | declare const require: { 26 | context(path: string, deep?: boolean, filter?: RegExp): { 27 | (id: string): T; 28 | keys(): string[]; 29 | }; 30 | }; 31 | 32 | // First, initialize the Angular testing environment. 33 | getTestBed().initTestEnvironment( 34 | BrowserDynamicTestingModule, 35 | platformBrowserDynamicTesting(), 36 | ); 37 | 38 | // Then we find all the tests. 39 | const context = require.context('./', true, /\.spec\.ts$/); 40 | // And load the modules. 41 | context.keys().forEach(context); 42 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/dashboard/components/indicator/indicator.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Component, Input, OnChanges, SimpleChanges} from '@angular/core'; 17 | 18 | @Component({ 19 | selector: 'app-indicator', 20 | templateUrl: './indicator.component.html', 21 | styleUrls: ['./indicator.component.scss'] 22 | }) 23 | export class IndicatorComponent implements OnChanges { 24 | 25 | @Input() metric: any; 26 | @Input() mValue: any; 27 | 28 | ngOnChanges(changes: SimpleChanges): void { 29 | 30 | if (changes && changes['mValue'].currentValue !== changes['mValue'].previousValue) { 31 | const metricLine: any = document.querySelector(`.metric-bar-line.${this.metric}`); 32 | if (metricLine) { 33 | metricLine?.style.setProperty('--left', `${this.mValue}%`); 34 | metricLine?.style.setProperty('--animation', 'none'); 35 | } 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /source/web-app/src/app/interceptor/auth.interceptor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import { Injectable } from "@angular/core"; 17 | import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from "@angular/common/http"; 18 | import { Observable } from "rxjs"; 19 | import { AuthService } from "../services/auth.service"; 20 | 21 | @Injectable() 22 | export class AuthInterceptor implements HttpInterceptor { 23 | constructor(private authService: AuthService) {} 24 | 25 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 26 | if (request.url.includes("/api/") && !request.url.includes("/api/amplify-config")) { 27 | const cloneRequest = request.clone({ 28 | setHeaders: { 29 | Authorization: `Bearer ${this.authService.getToken()}`, 30 | }, 31 | }); 32 | return next.handle(cloneRequest); 33 | } else { 34 | return next.handle(request); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /source/web-app/src/app/guards/persona.guard.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Injectable} from '@angular/core'; 17 | import {CanActivate, Router, UrlTree} from '@angular/router'; 18 | import {Observable} from 'rxjs'; 19 | import {UserPreferenceService} from "../services/user-preference.service"; 20 | import {UserPersona} from "../models/user-persona"; 21 | 22 | @Injectable({ 23 | providedIn: 'root' 24 | }) 25 | export class PersonaGuard implements CanActivate { 26 | constructor(private userPrefService: UserPreferenceService, private router: Router) { 27 | } 28 | canActivate(): Observable | Promise | boolean | UrlTree { 29 | if (this.userPrefService.currentPersona === UserPersona.FLEET_OPERATOR) { 30 | if (!this.router.url.includes('/dashboard')) { 31 | this.router.navigate(['/dashboard']).then(); 32 | return false; 33 | } else { 34 | return true; 35 | } 36 | } else { 37 | return true; 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | import { NgModule } from "@angular/core"; 16 | import { CommonModule } from "@angular/common"; 17 | import { HeaderComponent } from "./components/header/header.component"; 18 | import { FooterComponent } from "./components/footer/footer.component"; 19 | import { FontAwesomeModule } from "@fortawesome/angular-fontawesome"; 20 | import { RouterLink, RouterLinkActive } from "@angular/router"; 21 | import { SpinnerComponent } from "./components/spinner/spinner.component"; 22 | import {HighchartsChartModule} from "highcharts-angular"; 23 | import { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.component'; 24 | 25 | @NgModule({ 26 | declarations: [HeaderComponent, FooterComponent, SpinnerComponent, BreadcrumbComponent], 27 | exports: [HeaderComponent, FooterComponent, FontAwesomeModule, SpinnerComponent, BreadcrumbComponent], 28 | imports: [CommonModule, FontAwesomeModule, HighchartsChartModule, RouterLink, RouterLinkActive], 29 | }) 30 | export class SharedModule {} 31 | -------------------------------------------------------------------------------- /source/web-app/README.md: -------------------------------------------------------------------------------- 1 | # Angular Prototype Starter 2 | 3 | --- 4 | 5 | ## Prerequisites 6 | 7 | * Node >= ^14.15.0 || ^16.10.0 8 | * npm >= 8.15.0 9 | 10 | 11 | ## How to use 12 | 13 | ### Configuration 14 | 1. Amplify config: `environment` contains the env variables that amplify **needs**. 15 | 1. Fill in the parameters needed for your project. 16 | * If using locally you only need `NG_APP_API`, for env lazy load. 17 | When tested: **Revert changes from `environment` file ** 18 | 2. Add `example.com` to your hosts file. 19 | On mac/linux, edit /etc/hosts and add the following line: 20 | ``` 21 | 127.0.0.1 example.com 22 | ``` 23 | Open **angular.json** and add the host and port at below shown path 24 | ``` 25 | "projects": { 26 | "ng-app": { 27 | "architect": { 28 | "serve:: { 29 | "options": { 30 | "host": "example.com", 31 | "port": 4200 32 | } 33 | } 34 | } 35 | } 36 | } 37 | ``` 38 | #### Note: DO NOT PUSH CHANGES TO `environment.ts`, `environment.prod.ts`, and `angular.json` 39 | 40 | ### Run 41 | ``` 42 | npm install 43 | npm start 44 | ``` 45 | 46 | --- 47 | 48 | ## Packages used 49 | 50 | Your project might need additional packages. 51 | The packages chosen are very commonly used and are necessary for most if not all prototypes. 52 | 53 | ### Fontawesome - [docs](https://fontawesome.com/v5/docs/web/use-with/angular) 54 | 55 | ### Amplify - [docs](https://docs.amplify.aws/start/q/integration/react) 56 | -------------------------------------------------------------------------------- /source/web-app/src/app/services/auth.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | import {Injectable} from "@angular/core"; 16 | import {Auth} from "aws-amplify"; 17 | import {Router} from "@angular/router"; 18 | 19 | @Injectable({ 20 | providedIn: "root", 21 | }) 22 | export class AuthService { 23 | isLoggedIn = false; 24 | currentSession: any; 25 | 26 | constructor(private router: Router) {} 27 | 28 | async getLoginStatus() { 29 | const currentSession = await Auth.currentSession(); 30 | return (this.isLoggedIn = currentSession.isValid()); 31 | } 32 | 33 | logout() { 34 | Auth.signOut().then(() => { 35 | this.router.navigate(["/login"]).then(); 36 | }); 37 | } 38 | 39 | getToken() { 40 | return this.currentSession?.idToken?.jwtToken; 41 | } 42 | 43 | setCurrentSession() { 44 | Auth.currentSession().then((session) => { 45 | this.isLoggedIn = true; 46 | this.currentSession = session; 47 | console.log('Successfully set the session'); 48 | }, () => console.log('User needs to log in')); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /source/deploy/src/constructs/ddb-table-construct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | import { RemovalPolicy } from "aws-cdk-lib"; 18 | import { Construct } from "constructs"; 19 | 20 | const ddb = cdk.aws_dynamodb; 21 | 22 | export interface DdbTableConstructProps extends cdk.StackProps {} 23 | 24 | const defaultProps: Partial = {}; 25 | 26 | export class DdbTableConstruct extends Construct { 27 | public table: cdk.aws_dynamodb.Table; 28 | 29 | constructor(parent: Construct, name: string, props: DdbTableConstructProps) { 30 | super(parent, name); 31 | 32 | props = { ...defaultProps, ...props }; 33 | 34 | // get the parent stack reference for the stackName and the aws region 35 | const stack = cdk.Stack.of(this); 36 | 37 | this.table = new ddb.Table(this, name, { 38 | partitionKey: { name: "Id", type: ddb.AttributeType.STRING }, 39 | tableClass: ddb.TableClass.STANDARD, 40 | encryption: ddb.TableEncryption.AWS_MANAGED, 41 | removalPolicy: RemovalPolicy.DESTROY, 42 | pointInTimeRecovery: true 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /source/web-app/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 4 | * 5 | * Licensed under the Amazon Software License (the "License"). 6 | * You may not use this file except in compliance with the License. 7 | * A copy of the License is located at 8 | * 9 | * http://aws.amazon.com/asl/ 10 | * 11 | * or in the "license" file accompanying this file. This file is distributed 12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | * express or implied. See the License for the specific language governing 14 | * permissions and limitations under the License. 15 | */ 16 | 17 | // This file can be replaced during build by using the `fileReplacements` array. 18 | // `ng build` replaces `environment.ts` with `environment.prod.ts`. 19 | // The list of file replacements can be found in `angular.json`. 20 | export const environment = { 21 | production: false, 22 | NG_APP_NAME: process.env['CUSTOMER_NAME'] ?? 'Smart Battery Management System', 23 | NG_APP_LOGO: process.env['CUSTOMER_LOGO'] ?? '/assets/logos/logo.png', 24 | NG_APP_API: "https://fdvfhgm098.execute-api.us-east-1.amazonaws.com", 25 | NG_APP_REGION: "", 26 | NG_APP_USER_POOL_ID: "", 27 | NG_APP_IDENTITY_POOL_ID: "", 28 | NG_APP_APP_CLIENT_ID: "", 29 | }; 30 | 31 | /* 32 | * For easier debugging in development mode, you can import the following file 33 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 34 | * 35 | * This import should be commented out in production mode because it will have a negative impact 36 | * on performance if an error is thrown. 37 | */ 38 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 39 | -------------------------------------------------------------------------------- /source/api/build.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | """ 15 | 16 | import os 17 | import shutil 18 | import sys 19 | import subprocess 20 | 21 | # Prepares the all the lambdas for deployment 22 | # 23 | # Walks each directory looking for a build script and executes it if found 24 | 25 | def exit_on_failure(exit_code, msg): 26 | if exit_code != 0: 27 | print(msg) 28 | exit(exit_code) 29 | 30 | build_file_name = "build.py" 31 | 32 | npm_cmd = shutil.which("npm") 33 | cmd = [npm_cmd, "install"] 34 | proc = subprocess.run(cmd, stderr=subprocess.STDOUT) 35 | exit_on_failure(proc.returncode, "Web app npm install failed") 36 | 37 | dir_path = os.path.dirname(os.path.realpath(__file__)) 38 | build_file_name = os.path.basename(__file__) 39 | exit_code = 0 40 | 41 | for file in os.listdir(dir_path): 42 | if os.path.isdir(file): 43 | folder_path = os.path.join(dir_path, file) 44 | build_file_path = os.path.join(folder_path, build_file_name) 45 | if os.path.exists(build_file_path): 46 | cmd = [sys.executable, build_file_path] 47 | proc = subprocess.run(cmd, stderr=subprocess.STDOUT) 48 | exit_code = exit_code + proc.returncode 49 | 50 | 51 | exit(exit_code) 52 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/dashboard/components/indicator/indicator.component.scss: -------------------------------------------------------------------------------- 1 | .metric-container { 2 | .metric-info { 3 | font-size: 16px; 4 | color: #fff; 5 | //margin: 2em 0 0 0; 6 | display: flex; 7 | justify-content: space-between; 8 | align-items: center; 9 | 10 | span { 11 | font-size: 12px; 12 | } 13 | 14 | span:last-child { 15 | font-size: 24px; 16 | } 17 | } 18 | 19 | .metric-bar { 20 | width: 100%; 21 | height: 24px; 22 | margin: 4px 0; 23 | background: linear-gradient(270deg, #1E8102 0%, #FF9900 43.38%, #CA3112 100%); 24 | position: relative; 25 | --animation: infinite; 26 | --left: 0; 27 | 28 | .metric-bar-line { 29 | animation: move-bar-line linear var(--animation) 3s; 30 | position: absolute; 31 | left: var(--left); 32 | top: 0; 33 | height: 24px; 34 | 35 | .metric-position-line { 36 | width: 2px; 37 | position: absolute; 38 | left: 0; 39 | height: 24px; 40 | background: #fff; 41 | } 42 | 43 | .metric-position-icon { 44 | position: relative; 45 | left: -4px; 46 | bottom: -20px; 47 | width: 0; 48 | height: 0; 49 | border-left: 5px solid transparent; 50 | border-right: 5px solid transparent; 51 | border-bottom: 8px solid #fff; 52 | } 53 | } 54 | } 55 | 56 | .percent-markers { 57 | width: 100%; 58 | display: flex; 59 | justify-content: space-between; 60 | 61 | span { 62 | font-size: 10px; 63 | color: #fff; 64 | opacity: 0.6; 65 | } 66 | } 67 | } 68 | 69 | @keyframes move-bar-line { 70 | 0% { 71 | left: 0; 72 | } 73 | 50% { 74 | left: 100% 75 | } 76 | 100% { 77 | left: 0; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /source/deploy/src/cf-waf-stack.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | import * as cdk from "aws-cdk-lib"; 16 | import { Construct } from "constructs"; 17 | 18 | import { Wafv2BasicConstruct, WafV2Scope } from "./constructs/wafv2-basic-construct"; 19 | 20 | export class CfWafStack extends cdk.Stack { 21 | /** 22 | * Name of the WafArn SSM parameter 23 | */ 24 | public ssmWafArnParameterName: string; 25 | 26 | constructor(scope: Construct, id: string, props: cdk.StackProps) { 27 | super(scope, id, props); 28 | 29 | // ssm parameter name must be unique in a region 30 | this.ssmWafArnParameterName = "waf_acl_arn_" + this.stackName; 31 | 32 | // requires us-east-1 for deployment to work due to limitations with the service. 33 | // for deployments outside of us-east-1 deploy waf separately 34 | const wafv2CF = new Wafv2BasicConstruct(this, "Wafv2CF", { 35 | wafScope: WafV2Scope.CLOUDFRONT, 36 | }); 37 | 38 | new cdk.aws_ssm.StringParameter(this, "waf_acl_arn", { 39 | parameterName: this.ssmWafArnParameterName, 40 | description: "WAF ACL ARN", 41 | stringValue: wafv2CF.webacl.attrArn, 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /source/deploy/src/constructs/wafv2-attachments.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | // import * as wafv2 from "@aws-cdk/aws-wafv2"; 17 | // import * as apigwv2 from "@aws-cdk/aws-apigatewayv2"; 18 | // import * as elbv2 from "@aws-cdk/aws-elasticloadbalancingv2"; 19 | 20 | import * as cdk from "aws-cdk-lib"; 21 | import { Construct } from "constructs"; 22 | 23 | /** 24 | * Helper function to attach the waf to an apigatewayv2 http api 25 | * @param parent 26 | * @param name 27 | * @param webAcl 28 | * @param apigwv2 29 | * @returns 30 | */ 31 | export function attachWafV2ToLoadBalancer( 32 | /** 33 | * Parent construct to assign the association to. 34 | */ 35 | parent: Construct, 36 | 37 | /** 38 | * Name of the construct 39 | */ 40 | name: string, 41 | 42 | /** 43 | * WafV2 WebAcl 44 | */ 45 | webAcl: cdk.aws_wafv2.CfnWebACL, 46 | 47 | /** 48 | * load balancer to attach the web acl to 49 | */ 50 | loadBalancer: cdk.aws_elasticloadbalancingv2.ApplicationLoadBalancer 51 | ) { 52 | return new cdk.aws_wafv2.CfnWebACLAssociation(parent, name, { 53 | webAclArn: webAcl.attrArn, 54 | resourceArn: loadBalancer.loadBalancerArn, 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /source/deploy/src/constructs/lambda-function-construct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | import { Construct } from "constructs"; 18 | 19 | const lambda = cdk.aws_lambda_nodejs; 20 | 21 | export interface LambdaFunctionConstructProps extends cdk.StackProps { 22 | readonly lambdaAsset: string; 23 | readonly lambdaEnvConfig?: { [key: string]: string }; 24 | } 25 | 26 | const defaultProps: Partial = {}; 27 | 28 | export class LambdaFunctionConstruct extends Construct { 29 | public function: cdk.aws_lambda_nodejs.NodejsFunction; 30 | 31 | constructor( 32 | parent: Construct, 33 | name: string, 34 | props: LambdaFunctionConstructProps 35 | ) { 36 | super(parent, name); 37 | 38 | props = { ...defaultProps, ...props }; 39 | 40 | this.function = new lambda.NodejsFunction(this, name, { 41 | entry: props.lambdaAsset, 42 | handler: "handler", 43 | depsLockFilePath: "../api/package-lock.json", 44 | runtime: cdk.aws_lambda.Runtime.NODEJS_18_X, 45 | architecture: cdk.aws_lambda.Architecture.X86_64, 46 | environment: props.lambdaEnvConfig, 47 | bundling: { 48 | externalModules: ["@aws-sdk/*"], 49 | }, 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /source/web-app/src/app/guards/auth.guard.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Injectable} from "@angular/core"; 17 | import {CanActivate, Router, UrlTree,} from "@angular/router"; 18 | import {Observable} from "rxjs"; 19 | import {AuthService} from "../services/auth.service"; 20 | import {Auth} from "aws-amplify"; 21 | import {CognitoUserSession} from "amazon-cognito-identity-js"; 22 | 23 | @Injectable({ 24 | providedIn: "root", 25 | }) 26 | export class AuthGuard implements CanActivate { 27 | constructor(private authService: AuthService, private router: Router) {} 28 | canActivate(): Observable | Promise | boolean | UrlTree { 29 | return new Promise((resolve) => { 30 | Auth.currentSession() 31 | .then((session: CognitoUserSession) => { 32 | if (session.isValid()) { 33 | this.authService.isLoggedIn = true; 34 | resolve(true); 35 | } else { 36 | this.router.navigate(["/login"]); 37 | resolve(false); 38 | } 39 | }) 40 | .catch(() => { 41 | this.router.navigate(["/login"]); 42 | }); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/header/header.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 |
8 | {{ userName }} 9 |
10 | 15 |
16 |
17 |
    18 |
  • 19 | 20 | Connected Vehicles 21 |
  • 22 |
  • 23 | 24 | Pipelines 25 |
  • 26 |
  • 27 | 28 |
    29 |

    {{userPrefService.currentPersona === 1 ? 'Fleet Operator' : 'Developer' }}

    30 | Switch Profile 31 |
    32 |
  • 33 |
  • Logout

  • 34 |
35 |
36 |
37 |
38 |
39 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/dashboard/components/battery-charge/battery-charge.component.scss: -------------------------------------------------------------------------------- 1 | .metric-container { 2 | .metric-info { 3 | font-size: 16px; 4 | color: #fff; 5 | display: flex; 6 | justify-content: space-between; 7 | align-items: center; 8 | 9 | span:first-child { 10 | font-size: 12px; 11 | } 12 | 13 | span:last-child { 14 | font-size: 24px; 15 | } 16 | } 17 | 18 | .battery { 19 | position: relative; 20 | width: 269px; 21 | background: #2D343D; 22 | height: 36px; 23 | margin: 4px 0; 24 | --animation: infinite; 25 | --width: 0; 26 | --background: #1D8102; 27 | border-radius: 2px; 28 | 29 | &:before { 30 | content: ""; 31 | position: absolute; 32 | top: 3px; 33 | left: 4px; 34 | width: var(--width); 35 | height: 30px; 36 | transition: all 2s ease-in; 37 | background-color: var(--background); 38 | animation: full 2s linear var(--animation); 39 | } 40 | 41 | &:after { 42 | content: ""; 43 | position: absolute; 44 | right: -6px; 45 | top: 7px; 46 | width: 4px; 47 | height: 24px; 48 | background-color: #2D343D; 49 | border-bottom-right-radius: 90px; 50 | border-top-right-radius: 90px; 51 | } 52 | } 53 | 54 | .percent-markers { 55 | width: 100%; 56 | display: flex; 57 | justify-content: space-between; 58 | 59 | span { 60 | font-size: 10px; 61 | color: #fff; 62 | opacity: 0.6; 63 | } 64 | } 65 | 66 | } 67 | 68 | .soc-predictions { 69 | .battery { 70 | background: #434A54; 71 | width: 100%; 72 | 73 | &:after { 74 | background: #434A54; 75 | } 76 | } 77 | } 78 | 79 | @keyframes full { 80 | 0% { 81 | width: 0%; 82 | } 83 | 25% { 84 | width: 24%; 85 | } 86 | 50% { 87 | width: 48%; 88 | } 89 | 75% { 90 | width: 72%; 91 | } 92 | 100% { 93 | width: 97%; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /source/api/lib/locationDataFn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | const csv = require("csv-parser"); 16 | 17 | import {GetObjectCommand, S3Client} from "@aws-sdk/client-s3"; 18 | 19 | const s3 = new S3Client({ region: process.env.AWS_REGION }); 20 | 21 | /** 22 | * Reads battery data from csv file and returns as array 23 | * @param event 24 | */ 25 | function getBatteryData(event: any) { 26 | const locationData: any[] = []; 27 | return new Promise(async (resolve) => { 28 | try { 29 | let Key = process.env.KEY; 30 | const getObjectParams = { 31 | Bucket: process.env.BUCKET, 32 | Key, 33 | }; 34 | const getObjectCommand = new GetObjectCommand(getObjectParams) 35 | const resp = await s3.send(getObjectCommand); 36 | const file_stream: any = resp.Body; 37 | 38 | file_stream.pipe(csv(["BatteryId", "Lng", "Lat", "Country", "City", "VIN"])) 39 | .on("data", (data: any) => { 40 | locationData.push(data); 41 | }) 42 | .on("end", () => { 43 | resolve(locationData); 44 | }); 45 | } catch (err) { 46 | console.log("Cannot read file"); 47 | } 48 | }); 49 | } 50 | 51 | /** 52 | * Handler of the lambda function 53 | * @param event 54 | * @param context 55 | */ 56 | export async function handler(event?: any, context?: any) { 57 | let data; 58 | try { 59 | data = await getBatteryData(event); 60 | return data; 61 | } catch (err) { 62 | console.error(err); 63 | return []; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /source/web-app/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import { Component } from '@angular/core'; 17 | import { Title } from "@angular/platform-browser"; 18 | import { environment } from "../environments/environment"; 19 | import { Hub } from 'aws-amplify'; 20 | import {AuthService} from "./services/auth.service"; 21 | import {Router} from "@angular/router"; 22 | 23 | @Component({ 24 | selector: 'app-root', 25 | templateUrl: './app.component.html', 26 | styleUrls: ['./app.component.scss'] 27 | }) 28 | export class AppComponent { 29 | constructor(private titleService: Title, private authService: AuthService, private router: Router) { 30 | this.titleService.setTitle(environment.NG_APP_NAME as any); 31 | Hub.listen('auth', (data) => { 32 | switch (data.payload.event) { 33 | case 'signIn': 34 | console.log('user signed in'); 35 | this.authService.setCurrentSession(); 36 | this.router.navigate(['/dashboard']).then(); 37 | break; 38 | case 'signUp': 39 | console.log('user signed up'); 40 | break; 41 | case 'signOut': 42 | console.log('user signed out'); 43 | break; 44 | case 'signIn_failure': 45 | console.log('user sign in failed'); 46 | break; 47 | case 'configured': 48 | console.log('the Auth module is configured'); 49 | } 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /source/web-app/karma.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | // Karma configuration file, see link for more information 17 | // https://karma-runner.github.io/1.0/config/configuration-file.html 18 | 19 | module.exports = function (config) { 20 | config.set({ 21 | basePath: '', 22 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 23 | plugins: [ 24 | require('karma-jasmine'), 25 | require('karma-chrome-launcher'), 26 | require('karma-jasmine-html-reporter'), 27 | require('karma-coverage'), 28 | require('@angular-devkit/build-angular/plugins/karma') 29 | ], 30 | client: { 31 | jasmine: { 32 | // you can add configuration options for Jasmine here 33 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 34 | // for example, you can disable the random execution with `random: false` 35 | // or set a specific seed with `seed: 4321` 36 | }, 37 | clearContext: false // leave Jasmine Spec Runner output visible in browser 38 | }, 39 | jasmineHtmlReporter: { 40 | suppressAll: true // removes the duplicated traces 41 | }, 42 | coverageReporter: { 43 | dir: require('path').join(__dirname, './coverage/ng_app'), 44 | subdir: '.', 45 | reporters: [ 46 | { type: 'html' }, 47 | { type: 'text-summary' } 48 | ] 49 | }, 50 | reporters: ['progress', 'kjhtml'], 51 | port: 9876, 52 | colors: true, 53 | logLevel: config.LOG_INFO, 54 | autoWatch: true, 55 | browsers: ['Chrome'], 56 | singleRun: false, 57 | restartOnFileChange: true 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /source/deploy/src/config/git-context.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | import * as path from "path"; 16 | import gitBranch from "git-branch"; 17 | import * as fs from "fs"; 18 | 19 | /** 20 | * Gets branch name and repository name from git configuration. Returns branch name, repo name and app stack name 21 | */ 22 | export const getGitContext = () => { 23 | const defaultNameIfGitIsMissing = "dev"; 24 | 25 | const findDirUp = (directoryName: string): string => { 26 | let cwd = process.cwd(); 27 | // while not the file system root (linux & windows) 28 | while (!(cwd === "/" || cwd === "C:\\")) { 29 | const directories = fs.readdirSync(cwd); 30 | if (directories.filter((dir) => dir === directoryName).length > 0) { 31 | return cwd; 32 | } else { 33 | cwd = path.join(cwd, "../"); 34 | } 35 | } 36 | console.log("GetContext: No .git parent directory found."); 37 | return ""; 38 | }; 39 | 40 | const gitDirectory = findDirUp(".git"); 41 | // if no git directory is found then default to something sensible 42 | const currentGitBranch = gitDirectory 43 | ? gitBranch.sync(gitDirectory) 44 | : defaultNameIfGitIsMissing; 45 | // replaces special characters with - and truncates to 122 characters. 128 is the max for CloudFormation. 46 | // This name is used below for the CICD stack name which appends an additional 5 characters. 47 | const appStackName = currentGitBranch.replace(/[^\w\s]/gi, "-").substring(0, 122); 48 | 49 | return { 50 | currentGitBranch, 51 | appStackName, 52 | }; 53 | }; 54 | -------------------------------------------------------------------------------- /source/web-app/src/graphql/queries.graphql: -------------------------------------------------------------------------------- 1 | # this is an auto generated file. This will be overwritten 2 | query GetPipelinesByUser($UserId: String!) { 3 | getPipelinesByUser(UserId: $UserId) { 4 | Id 5 | PipelineStatus 6 | StatusUpdatedAt 7 | UserId 8 | DataUploadedAt 9 | PluginUploadedAt 10 | ProcFinishedAt 11 | DataImportedAt 12 | TrainingFinishedAt 13 | ForecastGeneratedAt 14 | PredictionsExportedAt 15 | DataGroupArn 16 | PreProcessingId 17 | RawDataSize 18 | RawDataUri 19 | PluginScriptUri 20 | TrainingDataUri 21 | PredictorArn 22 | ForecastArn 23 | ModelDrift 24 | ExportJobArn 25 | PostProcessingId 26 | OriginalDatasetName 27 | OriginalPluginName 28 | } 29 | } 30 | query GetPipelineById($Id: String!) { 31 | getPipelineById(Id: $Id) { 32 | Id 33 | PipelineStatus 34 | StatusUpdatedAt 35 | UserId 36 | DataUploadedAt 37 | PluginUploadedAt 38 | ProcFinishedAt 39 | DataImportedAt 40 | TrainingFinishedAt 41 | ForecastGeneratedAt 42 | PredictionsExportedAt 43 | DataGroupArn 44 | PreProcessingId 45 | RawDataSize 46 | RawDataUri 47 | PluginScriptUri 48 | TrainingDataUri 49 | PredictorArn 50 | ForecastArn 51 | ModelDrift 52 | ExportJobArn 53 | PostProcessingId 54 | OriginalDatasetName 55 | OriginalPluginName 56 | } 57 | } 58 | query GetPipelineByProcessingId($PostProcessingId: String!) { 59 | getPipelineByProcessingId(PostProcessingId: $PostProcessingId) { 60 | Id 61 | PipelineStatus 62 | StatusUpdatedAt 63 | UserId 64 | DataUploadedAt 65 | PluginUploadedAt 66 | ProcFinishedAt 67 | DataImportedAt 68 | TrainingFinishedAt 69 | ForecastGeneratedAt 70 | PredictionsExportedAt 71 | DataGroupArn 72 | PreProcessingId 73 | RawDataSize 74 | RawDataUri 75 | PluginScriptUri 76 | TrainingDataUri 77 | PredictorArn 78 | ForecastArn 79 | ModelDrift 80 | ExportJobArn 81 | PostProcessingId 82 | OriginalDatasetName 83 | OriginalPluginName 84 | } 85 | } 86 | query GetLocationData { 87 | getLocationData { 88 | BatteryId 89 | Lng 90 | Lat 91 | Country 92 | City 93 | VIN 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/pipeline/components/dataset-selection/dataset-selection.component.html: -------------------------------------------------------------------------------- 1 |
2 | 4 |
5 | Upload Image 6 |
7 | Upload Battery Dataset 8 | Drag and drop 9 | 10 | or click 11 | 12 | 13 |
14 |
15 | 16 | File has successfully uploaded. 17 |
18 |
19 | 20 | Please upload valid file types. 21 |
22 |
23 |
24 | Provide the S3 URI for the Battery Dataset 25 | 26 | S3 URI 27 | 29 | 32 | 33 |
34 |
35 | -------------------------------------------------------------------------------- /source/web-app/src/app/services/data.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | import {Injectable} from '@angular/core'; 16 | import {HttpClient, HttpHeaders} from "@angular/common/http"; 17 | import {environment} from "../../environments/environment"; 18 | import {Observable} from "rxjs"; 19 | 20 | @Injectable({ 21 | providedIn: 'root' 22 | }) 23 | export class DataService { 24 | 25 | constructor(private http: HttpClient) { 26 | } 27 | 28 | getSignedUrl(key: string): Observable { 29 | return this.http.get(`${environment.NG_APP_API}/api/link?fileName=${key}&action=GSU`); 30 | } 31 | 32 | getMetadata(key: string, uuid: string): Observable { 33 | return this.http.get(`${environment.NG_APP_API}/api/metadata?key=${key}&uuid=${uuid}&action=GM`) 34 | } 35 | 36 | getBatteryData(key: string, uuid: string, battery: string, type: string): Observable { 37 | return this.http.get(`${environment.NG_APP_API}/api/metadata?key=${key}&uuid=${uuid}&battery=${battery}&type=${type}&action=GBD`) 38 | } 39 | 40 | uploadFile(signedURL: string, file: File, contentType: string): Observable { 41 | return this.http.put(signedURL, file, { 42 | headers: new HttpHeaders({"Content-Type": contentType}), 43 | }); 44 | } 45 | 46 | simulate(id: string, fileName: string): Observable { 47 | return this.http.get(`${environment.NG_APP_API}/api/simulation?key=${id}&file=${fileName}&action=SS`); 48 | } 49 | 50 | copyFile(uri: string | undefined, key: string | undefined, plugin = 'N') { 51 | return this.http.get(`${environment.NG_APP_API}/api/metadata?uri=${uri}&key=${key}&plugin=${plugin}&action=CD`); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/web-app/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {NgModule} from "@angular/core"; 17 | import {RouterModule, Routes} from "@angular/router"; 18 | import {AuthGuard} from "./guards/auth.guard"; 19 | import {PipelineComponent} from "./components/pipeline/pipeline.component"; 20 | import {DashboardComponent} from "./components/dashboard/dashboard.component"; 21 | import {LoginComponent} from "./components/login/login.component"; 22 | import {TrackerComponent} from "./components/tracker/tracker.component"; 23 | import {HistoryComponent} from "./components/history/history.component"; 24 | import {PersonaGuard} from "./guards/persona.guard"; 25 | 26 | const routes: Routes = [ 27 | { 28 | path: "", 29 | redirectTo: "dashboard", 30 | pathMatch: "full", 31 | }, 32 | { 33 | path: "login", 34 | component: LoginComponent, 35 | pathMatch: "full", 36 | }, 37 | { 38 | path: "pipeline", 39 | component: PipelineComponent, 40 | pathMatch: "full", 41 | canActivate: [AuthGuard, PersonaGuard], 42 | }, 43 | { 44 | path: "dashboard", 45 | component: DashboardComponent, 46 | pathMatch: "full", 47 | canActivate: [AuthGuard], 48 | }, 49 | { 50 | path: "tracker/:id", 51 | component: TrackerComponent, 52 | pathMatch: "full", 53 | canActivate: [AuthGuard, PersonaGuard], 54 | }, 55 | { 56 | path: "history", 57 | component: HistoryComponent, 58 | pathMatch: "full", 59 | canActivate: [AuthGuard, PersonaGuard], 60 | }, 61 | ]; 62 | 63 | @NgModule({ 64 | imports: [RouterModule.forRoot(routes)], 65 | exports: [RouterModule], 66 | }) 67 | export class AppRoutingModule {} 68 | -------------------------------------------------------------------------------- /source/deploy/src/cdk-nag-suppressions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | import { NagSuppressions } from "cdk-nag"; 18 | 19 | /** 20 | * General cdk nag suppressions to allow infrastructure that is acceptable for a prototype 21 | */ 22 | export const suppressCdkNagRules = (stack: cdk.Stack) => { 23 | // General 24 | NagSuppressions.addStackSuppressions( 25 | stack, 26 | [ 27 | { 28 | id: "AwsSolutions-APIG1", 29 | reason: "API Gateway access logging not required for prototype", 30 | }, 31 | { 32 | id: "AwsSolutions-CFR1", 33 | reason: "CloudFront geo restrictions not required for prototype", 34 | }, 35 | { 36 | id: "AwsSolutions-CFR3", 37 | reason: 38 | "CloudFront logs bucket. Not required for prototype", 39 | }, 40 | { 41 | id: "AwsSolutions-CFR4", 42 | reason: 43 | "Custom certificate required for enabling this rule. Not required for prototype", 44 | }, 45 | { 46 | id: "AwsSolutions-COG2", 47 | reason: "Cognito MFA not required for prototype", 48 | }, 49 | { 50 | id: "AwsSolutions-COG3", 51 | reason: "Cognito advanced security mode not required for prototype", 52 | }, 53 | { 54 | id: "AwsSolutions-IAM4", 55 | reason: "AWS managed policies allowed for prototype", 56 | }, 57 | { id: "AwsSolutions-IAM5", reason: "IAM wildcard allowed for prototype" }, 58 | { 59 | id: "AwsSolutions-L1", 60 | reason: "Latest runtime not required for prototype", 61 | }, 62 | { 63 | id: "AwsSolutions-S1", 64 | reason: "S3 server access logs not required for prototype", 65 | }, 66 | 67 | ], 68 | true 69 | ); 70 | }; 71 | -------------------------------------------------------------------------------- /source/deploy/src/constructs/apigatewayv2-lambda-construct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as apigw from "@aws-cdk/aws-apigatewayv2-alpha"; 17 | import * as apigwIntegrations from "@aws-cdk/aws-apigatewayv2-integrations-alpha"; 18 | import * as cdk from "aws-cdk-lib"; 19 | import { Construct } from "constructs"; 20 | 21 | export interface ApiGatewayV2LambdaConstructProps extends cdk.StackProps { 22 | /** 23 | * The lambda function 24 | */ 25 | readonly lambdaFn: cdk.aws_lambda.Function; 26 | /** 27 | * The apigatewayv2 route path 28 | */ 29 | readonly routePath: string; 30 | /** 31 | * Api methods supported by this API 32 | */ 33 | readonly methods: Array; 34 | /** 35 | * The ApiGatewayV2 HttpApi to attach the lambda 36 | */ 37 | readonly api: apigw.HttpApi; 38 | } 39 | 40 | const defaultProps: Partial = {}; 41 | 42 | /** 43 | * Deploys a lambda and attaches it to a route on the apigatewayv2 44 | */ 45 | export class ApiGatewayV2LambdaConstruct extends Construct { 46 | constructor( 47 | parent: Construct, 48 | name: string, 49 | props: ApiGatewayV2LambdaConstructProps 50 | ) { 51 | super(parent, name); 52 | 53 | props = { ...defaultProps, ...props }; 54 | 55 | // add lambda policies 56 | props.lambdaFn.grantInvoke( 57 | new cdk.aws_iam.ServicePrincipal("apigateway.amazonaws.com") 58 | ); 59 | 60 | // add lambda integration 61 | const lambdaFnIntegration = new apigwIntegrations.HttpLambdaIntegration( 62 | "apiInt", 63 | props.lambdaFn, 64 | {} 65 | ); 66 | 67 | // add route to the api gateway 68 | props.api.addRoutes({ 69 | path: props.routePath, 70 | methods: props.methods, 71 | integration: lambdaFnIntegration, 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/tracker/tracker.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 | {{(pipelineData?.DataUploadedAt ?? pipelineData?.StatusUpdatedAt)| date: 'medium' : 'GMT'}} 6 | {{pipelineData?.OriginalDatasetName}} | {{pipelineData?.OriginalPluginName}} 7 |
8 |
9 |
    10 |
  • 11 |
    12 | 13 | 14 |
    15 |
    16 |
    17 | 18 | 20 | 21 | 22 |
    23 |
    24 |
    25 |
    STEP {{step.id}}
    26 |

    {{step.title}}

    27 | {{calculateTime(step.id)}} 28 |
    29 |
  • 30 |
31 |
32 | 33 |

Something happened. Unable to access pipeline.

34 |
Please click on Pipeline Detail Page to check if the pipeline was created.
35 |
36 |
37 | 39 |
40 |
41 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/map/map.component.scss: -------------------------------------------------------------------------------- 1 | #map { 2 | height: calc(100%); 3 | width: 100%; 4 | } 5 | 6 | .show-vehicle-info { 7 | height: 100px !important; 8 | opacity: 1 !important; 9 | } 10 | 11 | .map-info-container { 12 | display: flex; 13 | align-items: center; 14 | justify-content: space-between; 15 | height: 0; 16 | opacity: 0; 17 | position: fixed; 18 | bottom: 40px; 19 | width: 100%; 20 | background: #2D343D; 21 | transition: opacity 0.5s, height 0.75s ease; 22 | box-shadow: 0 -5px 5px -3px rgb(0 0 0 / 50%), 0px -8px 10px 1px rgb(0 0 0 / 30%), 0px -3px 14px 2px rgb(0 0 0 / 10%); 23 | 24 | 25 | .map-info { 26 | width: 100%; 27 | display: flex; 28 | flex-direction: column; 29 | padding: 0 2em; 30 | flex: 1; 31 | 32 | span { 33 | font-style: italic; 34 | font-weight: 300; 35 | font-size: 12px; 36 | } 37 | } 38 | 39 | .map-legends { 40 | display: flex; 41 | align-items: center; 42 | flex: 1; 43 | 44 | .location-pin { 45 | display: flex; 46 | align-items: center; 47 | justify-content: center; 48 | width: 200px; 49 | 50 | .charging, .need-charging, .not-charging { 51 | width: 40px; 52 | height: 40px; 53 | border-radius: 50% 50% 50% 0; 54 | margin: -20px 0 0 -20px; 55 | transform: rotate(-45deg); 56 | } 57 | 58 | .hint { 59 | display: flex; 60 | flex-direction: column; 61 | justify-content: space-between; 62 | align-items: center; 63 | padding: 0.5em; 64 | 65 | span:first-child { 66 | font-size: 10px; 67 | } 68 | 69 | span:last-child { 70 | font-size: 32px; 71 | } 72 | } 73 | 74 | .charging { 75 | background: #1D8102; 76 | } 77 | 78 | .need-charging { 79 | background: #FFAA00; 80 | } 81 | 82 | .not-charging { 83 | background: #545B64; 84 | } 85 | } 86 | } 87 | 88 | .map-summary { 89 | flex: 1; 90 | display: flex; 91 | justify-content: flex-end; 92 | 93 | 94 | .vehicle-count { 95 | display: flex; 96 | flex-direction: column; 97 | justify-content: center; 98 | align-items: center; 99 | height: 100px; 100 | background: #1B1E20; 101 | width: 200px; 102 | 103 | span:last-child { 104 | font-size: 32px; 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /source/deploy/src/constructs/schema.graphql: -------------------------------------------------------------------------------- 1 | type Mutation { 2 | pipeline(input: PipelineRequestInput!): PipelineData! @aws_iam @aws_cognito_user_pools 3 | } 4 | 5 | type PipelineData @aws_iam @aws_cognito_user_pools { 6 | Id: String! 7 | PipelineStatus: String! 8 | StatusUpdatedAt: String! 9 | UserId: String 10 | DataUploadedAt: String 11 | PluginUploadedAt: String 12 | ProcFinishedAt: String 13 | DataImportedAt: String 14 | TrainingFinishedAt: String 15 | ForecastGeneratedAt: String 16 | PredictionsExportedAt: String 17 | DataGroupArn: String 18 | PreProcessingId: String 19 | RawDataSize: Int 20 | RawDataUri: String 21 | PluginScriptUri: String 22 | TrainingDataUri: String 23 | PredictorArn: String 24 | ForecastArn: String 25 | ModelDrift: Float 26 | ExportJobArn: String 27 | PostProcessingId: String 28 | OriginalDatasetName: String 29 | OriginalPluginName: String 30 | } 31 | 32 | input PipelineRequestInput { 33 | Id: String! 34 | PipelineStatus: String! 35 | StatusUpdatedAt: String! 36 | UserId: String 37 | DataUploadedAt: String 38 | PluginUploadedAt: String 39 | ProcFinishedAt: String 40 | DataImportedAt: String 41 | TrainingFinishedAt: String 42 | ForecastGeneratedAt: String 43 | PredictionsExportedAt: String 44 | DataGroupArn: String 45 | PreProcessingId: String 46 | RawDataSize: Int 47 | RawDataUri: String 48 | PluginScriptUri: String 49 | TrainingDataUri: String 50 | PredictorArn: String 51 | ForecastArn: String 52 | ModelDrift: Float 53 | ExportJobArn: String 54 | PostProcessingId: String 55 | OriginalDatasetName: String 56 | OriginalPluginName: String 57 | } 58 | 59 | type LocationData @aws_cognito_user_pools { 60 | BatteryId: String! 61 | Lng: String! 62 | Lat: String! 63 | Country: String! 64 | City: String!, 65 | VIN: String! 66 | } 67 | 68 | input LocationDataRequestInput { 69 | BatteryId: String! 70 | Lng: String! 71 | Lat: String! 72 | Country: String! 73 | City: String!, 74 | VIN: String! 75 | } 76 | 77 | type Query { 78 | getPipelinesByUser(UserId: String!): [PipelineData!] 79 | @aws_iam 80 | @aws_cognito_user_pools 81 | getPipelineById(Id: String!): PipelineData! @aws_iam @aws_cognito_user_pools 82 | getPipelineByProcessingId(PostProcessingId: String!): [PipelineData!] @aws_iam @aws_cognito_user_pools 83 | getLocationData: [LocationData] @aws_iam @aws_cognito_user_pools 84 | } 85 | 86 | type Subscription { 87 | pipelineSub: PipelineData @aws_subscribe(mutations: ["pipeline"]) 88 | } 89 | 90 | schema { 91 | query: Query 92 | mutation: Mutation 93 | subscription: Subscription 94 | } 95 | -------------------------------------------------------------------------------- /source/api/lib/initPipelineFn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | import { fromEnv } from "@aws-sdk/credential-providers"; 17 | import { AppSyncClient } from "./appsync"; 18 | 19 | const appSync = new AppSyncClient({ 20 | graphQlUrl: process.env.APPSYNC_GRAPHQL_URL!, 21 | credentials: fromEnv(), 22 | }); 23 | 24 | /** 25 | * Builds response before sending back to UI 26 | * @param http_code 27 | * @param body 28 | */ 29 | function build_response(http_code: number, body: any) { 30 | return { 31 | headers: { 32 | "Cache-Control": "no-cache, no-store", // tell cloudfront and api gateway not to cache the response 33 | "Content-Type": "application/json", 34 | }, 35 | statusCode: http_code, 36 | body: JSON.stringify(body), 37 | }; 38 | } 39 | 40 | /** 41 | * Add dataset upload event as new entry in ddb table 42 | * @param event 43 | * @param context 44 | */ 45 | export async function handler(event?: any, context?: any) { 46 | let data: String[] = []; 47 | 48 | try { 49 | const bucket = event["detail"]["bucket"]["name"]; 50 | const file = event["detail"]["object"]; 51 | const userId = file["key"].split("/")[0]; 52 | const pipeId = file["key"].split("/")[1]; 53 | 54 | await appSync.post({ 55 | query: `mutation MyMutation($input: PipelineRequestInput!) { 56 | pipeline(input: $input) { Id PipelineStatus StatusUpdatedAt } 57 | }`, 58 | variables: { 59 | input: { 60 | Id: pipeId, 61 | UserId: userId, 62 | PluginUploadedAt: event["time"], 63 | PluginScriptUri: `s3://${bucket}/${file["key"]}`, 64 | PipelineStatus: process.env.PIPELINE_STATUS, 65 | StatusUpdatedAt: new Date().toISOString(), 66 | }, 67 | }, 68 | }); 69 | data.push( 70 | `Pipeline created in ${process.env.PIPELINE_TABLE} with ID ${pipeId}` 71 | ); 72 | 73 | return build_response(200, data); 74 | } catch (err) { 75 | console.error(err); 76 | data.push("Server Error"); 77 | 78 | return build_response(500, data); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /source/web-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-app", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test", 10 | "lint": "eslint '**/*.{ts,tsx}'" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular-eslint/schematics": "^16.2.0", 15 | "@angular/animations": "*", 16 | "@angular/cdk": "*", 17 | "@angular/common": "*", 18 | "@angular/compiler": "16.2.11", 19 | "@angular/core": "*", 20 | "@angular/forms": "^16.2.11", 21 | "@angular/material": "^16.2.11", 22 | "@angular/platform-browser": "*", 23 | "@angular/platform-browser-dynamic": "^16.2.11", 24 | "@angular/router": "^16.2.11", 25 | "@aws-amplify/api": "^5.4.5", 26 | "@aws-amplify/core": "^5.8.5", 27 | "@aws-amplify/ui-angular": "^4.0.9", 28 | "@fortawesome/angular-fontawesome": "^0.13.0", 29 | "@fortawesome/fontawesome-svg-core": "^6.4.2", 30 | "@fortawesome/free-brands-svg-icons": "^6.4.2", 31 | "@fortawesome/free-regular-svg-icons": "^6.4.2", 32 | "@fortawesome/free-solid-svg-icons": "^6.4.2", 33 | "aws-amplify": "^5.3.11", 34 | "highcharts": "^11.2.0", 35 | "highcharts-angular": "^3.1.2", 36 | "highcharts-annotations": "^1.3.3", 37 | "maplibre-gl": "^2.4.0", 38 | "maplibre-gl-js-amplify": "^3.1.0", 39 | "rxjs": "^7.8.1", 40 | "tslib": "^2.6.2", 41 | "uuid": "^9.0.1" 42 | }, 43 | "devDependencies": { 44 | "@angular-builders/custom-webpack": "^16.0.1", 45 | "@angular-devkit/build-angular": "^16.2.9", 46 | "@angular-eslint/eslint-plugin": "*", 47 | "@angular/cli": "^16.2.9", 48 | "@angular/compiler-cli": "^16.2.11", 49 | "@types/jasmine": "^5.1.1", 50 | "@types/lodash": "^4.14.200", 51 | "@types/mapbox__mapbox-gl-draw": "^1.4.5", 52 | "@types/node": "^20.8.10", 53 | "@typescript-eslint/eslint-plugin": "^6.9.1", 54 | "@typescript-eslint/parser": "^6.9.1", 55 | "dotenv-webpack": "^8.0.1", 56 | "eslint": "^8.52.0", 57 | "eslint-config-prettier": "^8.10.0", 58 | "eslint-plugin-prettier": "^5.0.1", 59 | "jasmine-core": "^5.1.1", 60 | "karma": "^6.4.2", 61 | "karma-chrome-launcher": "^3.2.0", 62 | "karma-coverage": "^2.2.1", 63 | "karma-jasmine": "^5.1.0", 64 | "prettier": "^3.0.3", 65 | "typescript": "^4.9.5" 66 | }, 67 | "browserslist": { 68 | "production": [ 69 | "defaults", 70 | "not ie 11" 71 | ], 72 | "development": [ 73 | "last 1 chrome version", 74 | "last 1 firefox version", 75 | "last 1 safari version" 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /source/build.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | """ 15 | 16 | import os 17 | import subprocess 18 | import sys 19 | import argparse 20 | 21 | 22 | def exit_on_failure(exit_code, msg): 23 | if exit_code != 0: 24 | print(msg) 25 | exit(exit_code) 26 | 27 | 28 | def change_dir_with_return(dir): 29 | current_dir = os.getcwd() 30 | os.chdir(dir) 31 | return lambda: os.chdir(current_dir) 32 | 33 | 34 | def build_api(): 35 | 36 | return_dir = change_dir_with_return("./api") 37 | 38 | cmd = [sys.executable, "build.py"] 39 | proc = subprocess.run(cmd, stderr=subprocess.STDOUT) 40 | exit_on_failure(proc.returncode, "Api build failed") 41 | 42 | return_dir() 43 | 44 | 45 | def build_web_app(): 46 | 47 | return_dir = change_dir_with_return("./web-app") 48 | cmd = [sys.executable, "build.py"] 49 | proc = subprocess.run(cmd, stderr=subprocess.STDOUT) 50 | exit_on_failure(proc.returncode, "Web app build failed") 51 | 52 | return_dir() 53 | 54 | 55 | def build_deploy(): 56 | return_dir = change_dir_with_return("./deploy") 57 | cmd = [sys.executable, "build.py"] 58 | proc = subprocess.run(cmd, stderr=subprocess.STDOUT) 59 | exit_on_failure(proc.returncode, "Deploy build failed") 60 | 61 | return_dir() 62 | 63 | 64 | def main(): 65 | parser = argparse.ArgumentParser( 66 | description="Builds parts or all of the solution. If no arguments are passed then all builds are run" 67 | ) 68 | parser.add_argument("--web", action="store_true", help="builds web app") 69 | parser.add_argument("--api", action="store_true", help="builds api") 70 | parser.add_argument("--deploy", action="store_true", help="builds deploy") 71 | args = parser.parse_args() 72 | 73 | if len(sys.argv) == 1: 74 | build_web_app() 75 | build_api() 76 | # needs to be last to ensure the dependencies are built before the CDK deployment can build/run 77 | build_deploy() 78 | else: 79 | if args.web: 80 | build_web_app() 81 | if args.api: 82 | build_api() 83 | if args.deploy: 84 | build_deploy() 85 | 86 | 87 | if __name__ == "__main__": 88 | main() 89 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/pipeline/components/plugin-selection/plugin-selection.component.html: -------------------------------------------------------------------------------- 1 |
2 | 4 |
5 | Upload Image 6 |
7 | Upload Processing Plugin 8 | Drag and drop 9 | 10 | or click 11 | 12 | 13 |
14 |
15 | 16 | File has successfully uploaded. 17 |
18 |
19 | 20 | Please upload valid file types. 21 |
22 |
23 |
24 | Provide the S3 URI for the Processing Plugin 25 | 26 | S3 URI 27 | 29 | 32 | 33 |
34 |
35 | Select Processing Plugin 36 | 37 | Select a plugin 38 | 39 | default_processing-plugin.py 40 | 41 | 42 |
43 |
44 | -------------------------------------------------------------------------------- /source/api/lib/closePipelineFn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | import { GlueClient, GetJobRunCommand } from "@aws-sdk/client-glue"; 17 | import { fromEnv } from "@aws-sdk/credential-providers"; 18 | import { AppSyncClient } from "./appsync"; 19 | 20 | const glue = new GlueClient({ region: process.env.AWS_REGION }); 21 | const appSync = new AppSyncClient({ 22 | graphQlUrl: process.env.APPSYNC_GRAPHQL_URL!, 23 | credentials: fromEnv(), 24 | }); 25 | 26 | /** 27 | * Builds response before sending back to UI 28 | * @param http_code 29 | * @param body 30 | */ 31 | function build_response(http_code: number, body: any) { 32 | return { 33 | headers: { 34 | "Cache-Control": "no-cache, no-store", // tell cloudfront and api gateway not to cache the response 35 | "Content-Type": "application/json", 36 | }, 37 | statusCode: http_code, 38 | body: JSON.stringify(body), 39 | }; 40 | } 41 | 42 | /** 43 | * Import training dataset into Forecast 44 | * @param event 45 | * @param context 46 | */ 47 | export async function handler(event?: any, context?: any) { 48 | let data: String[] = []; 49 | 50 | try { 51 | const postProcId = event["detail"]["jobRunId"]; 52 | 53 | const run = await glue.send( 54 | new GetJobRunCommand({ 55 | JobName: process.env.POST_PROCESSOR_NAME, 56 | RunId: postProcId, 57 | }) 58 | ); 59 | 60 | const outputPath = run.JobRun?.Arguments?.["--output_path"]!; 61 | const pipeId = outputPath.split("/")[1]; 62 | 63 | // Update pipeline info in ddb table 64 | await appSync.post({ 65 | query: `mutation MyMutation($input: PipelineRequestInput!) { 66 | pipeline(input: $input) { Id PipelineStatus StatusUpdatedAt } 67 | }`, 68 | variables: { 69 | input: { 70 | Id: pipeId, 71 | PipelineStatus: process.env.PIPELINE_STATUS, 72 | StatusUpdatedAt: new Date().toISOString(), 73 | }, 74 | }, 75 | }); 76 | data.push(`Pipeline status updated to ${process.env.PIPELINE_STATUS}`); 77 | 78 | return build_response(200, data); 79 | } catch (err) { 80 | console.error(err); 81 | data.push("Server Error"); 82 | 83 | return build_response(500, data); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/dashboard/components/battery-charge/battery-charge.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Component, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core'; 17 | import * as Highcharts from 'highcharts'; 18 | 19 | @Component({ 20 | selector: 'app-battery-charge', 21 | templateUrl: './battery-charge.component.html', 22 | styleUrls: ['./battery-charge.component.scss'] 23 | }) 24 | export class BatteryChargeComponent implements OnChanges, OnInit { 25 | 26 | highcharts = Highcharts; 27 | chartOptions: any; 28 | 29 | @Input() metric: any; 30 | @Input() mValue: any; 31 | @Input() overrideCss: any; 32 | 33 | ngOnChanges(changes: SimpleChanges): void { 34 | if (changes && changes['mValue'].currentValue && changes['mValue'].currentValue !== changes['mValue'].previousValue) { 35 | const battery: any = document.querySelector(`.battery.${this.metric}`); 36 | if (battery) { 37 | battery?.style.setProperty('--width', `${this.mValue}%`); 38 | battery?.style.setProperty('--animation', 'none'); 39 | this.setBatteryCellHealth(battery, this.mValue); 40 | } 41 | } 42 | } 43 | 44 | ngOnInit(): void { 45 | const battery: any = document.querySelector(`.battery.${this.metric}`); 46 | if (battery) { 47 | battery?.style.setProperty('--width', `${this.mValue}%`); 48 | battery?.style.setProperty('--animation', 'none'); 49 | this.setBatteryCellHealth(battery, this.mValue); 50 | } 51 | } 52 | setBatteryCellHealth(battery: any, value: number) { 53 | if (value <= 80 && value >= 60) { 54 | battery?.style.setProperty('--background', '#fcd303'); 55 | } else if (value <= 60 && value >= 40) { 56 | battery?.style.setProperty('--background', '#fcb503'); 57 | } else if (value <= 40 && value >= 20) { 58 | battery?.style.setProperty('--background', '#ff9606'); 59 | } else if (value < 20) { 60 | battery?.style.setProperty('--background', 'red'); 61 | } 62 | } 63 | 64 | 65 | getOverrideClass() { 66 | return this.overrideCss; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/history/history.component.scss: -------------------------------------------------------------------------------- 1 | .history-container { 2 | width: 100%; 3 | padding: 5em; 4 | 5 | .history-container--header { 6 | display: flex; 7 | justify-content: space-between; 8 | align-items: center; 9 | } 10 | .history-container--records { 11 | width: 100%; 12 | 13 | table { 14 | border: none; 15 | border-collapse: collapse; 16 | width: 100%; 17 | 18 | tr { 19 | background: #434A54; 20 | height: 44px; 21 | border: 2px solid #3A4552; 22 | 23 | th, td { 24 | color: #fff; 25 | font-size: 14px; 26 | padding: 0 8px; 27 | text-align: left; 28 | font-weight: 400; 29 | width: 25%; 30 | 31 | .pipeline-steps { 32 | display: flex; 33 | 34 | .pipeline-step { 35 | margin: 0 3px; 36 | } 37 | 38 | img { 39 | height: 18px; 40 | width: 18px; 41 | } 42 | 43 | .animate-progress { 44 | animation: spin 1s linear infinite; 45 | } 46 | } 47 | 48 | .pagination { 49 | display: flex; 50 | justify-content: space-evenly; 51 | 52 | fa-icon { 53 | cursor: pointer; 54 | 55 | &:hover { 56 | transform: scale(1.3); 57 | } 58 | } 59 | } 60 | 61 | span { 62 | display: flex; 63 | align-items: center; 64 | 65 | img { 66 | padding: 0 8px; 67 | } 68 | } 69 | } 70 | 71 | th { 72 | font-size: 12px; 73 | font-weight: bold; 74 | } 75 | 76 | td:first-child { 77 | color: #00A1C9; 78 | } 79 | } 80 | } 81 | } 82 | } 83 | 84 | .mat-select { 85 | width: 60px; 86 | border: 1px solid rgba(255, 255, 255, 0.6); 87 | border-radius: 4px; 88 | height: 20px; 89 | display: flex; 90 | align-items: center; 91 | padding: 0 4px; 92 | } 93 | 94 | .mat-select-arrow { 95 | width: 0; 96 | height: 0; 97 | border-left: 5px solid transparent; 98 | border-right: 5px solid transparent; 99 | border-top: 5px solid !important; 100 | margin: 0 4px; 101 | color: rgba(255, 255, 255, 0.6); 102 | 103 | &:active { 104 | color: #FF9900; 105 | } 106 | } 107 | 108 | .disable-step { 109 | color: grey; 110 | pointer-events: none; 111 | } 112 | 113 | .highlight:hover { 114 | cursor: pointer; 115 | 116 | td { 117 | background: #2D343D; 118 | } 119 | } 120 | 121 | @keyframes spin { 122 | 0% { 123 | transform: rotate(0deg) 124 | } 125 | 100% { 126 | transform: rotate(360deg) 127 | } 128 | } -------------------------------------------------------------------------------- /source/deploy/src/constructs/ssm-parameter-reader-construct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | import { Construct } from "constructs"; 18 | 19 | export interface SsmParameterReaderConstructProps extends cdk.StackProps { 20 | readonly ssmParameterName: string; 21 | readonly ssmParameterRegion: string; 22 | /** 23 | * Sets the physical resource id to current date to force a pull of the parameter on subsequent 24 | * deploys 25 | * 26 | * @default false 27 | */ 28 | readonly pullEveryTime?: boolean; 29 | } 30 | 31 | const defaultProps: Partial = { 32 | pullEveryTime: false, 33 | }; 34 | 35 | /** 36 | * Deploys the SsmParameterReaderConstruct construct 37 | * 38 | * Reads a inter / intra region parameter by name. 39 | * 40 | */ 41 | export class SsmParameterReaderConstruct extends Construct { 42 | public ssmParameter: cdk.custom_resources.AwsCustomResource; 43 | 44 | constructor(parent: Construct, name: string, props: SsmParameterReaderConstructProps) { 45 | super(parent, name); 46 | 47 | props = { ...defaultProps, ...props }; 48 | 49 | const stack = cdk.Stack.of(this); 50 | 51 | const physicalResourceId = props.pullEveryTime 52 | ? Date.UTC.toString() 53 | : `${props.ssmParameterName}-${props.ssmParameterRegion}`; 54 | 55 | this.ssmParameter = new cdk.custom_resources.AwsCustomResource(this, "Param", { 56 | onUpdate: { 57 | service: "SSM", 58 | action: "getParameter", 59 | parameters: { Name: `${props.ssmParameterName}` }, 60 | region: props.ssmParameterRegion, 61 | physicalResourceId: cdk.custom_resources.PhysicalResourceId.of(physicalResourceId), 62 | }, 63 | policy: cdk.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ 64 | resources: [ 65 | `arn:aws:ssm:${props.ssmParameterRegion}:${stack.account}:parameter/${props.ssmParameterName}`, 66 | ], 67 | }), 68 | }); 69 | } 70 | 71 | /** 72 | * @returns string value of the parameter 73 | */ 74 | public getValue() { 75 | return this.ssmParameter.getResponseField("Parameter.Value"); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/header/header.component.scss: -------------------------------------------------------------------------------- 1 | header { 2 | transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; 3 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); 4 | display: flex; 5 | flex-direction: column; 6 | width: 100%; 7 | box-sizing: border-box; 8 | flex-shrink: 0; 9 | position: fixed; 10 | background: #1B1E20; 11 | top: 0; 12 | z-index: 10; 13 | 14 | .header-container { 15 | height: 60px; 16 | display: flex; 17 | justify-content: space-between; 18 | align-items: center; 19 | margin: 0 3em; 20 | color: #fff; 21 | 22 | .header-container--menu { 23 | width: 60px; 24 | text-align: center; 25 | 26 | fa-icon { 27 | color: #1B1E20; 28 | } 29 | } 30 | 31 | .header-container--logo { 32 | display: flex; 33 | align-items: center; 34 | justify-content: flex-end; 35 | cursor: pointer; 36 | 37 | img { 38 | height: 24px; 39 | width: 25px; 40 | } 41 | } 42 | 43 | .header-container--user { 44 | display: flex; 45 | align-items: center; 46 | justify-content: flex-end; 47 | cursor: pointer; 48 | color: #fff; 49 | 50 | span { 51 | text-align: right; 52 | padding-right: 1em; 53 | font-weight: 300; 54 | } 55 | 56 | .logout-menu { 57 | position: relative; 58 | top: 20px; 59 | right: 260px; 60 | height: 0; 61 | width: 0; 62 | 63 | ul { 64 | list-style: none; 65 | margin: 0; 66 | padding: 0; 67 | position: absolute; 68 | outline: 0; 69 | display: flex; 70 | flex-direction: column; 71 | background: #2D343D; 72 | border-radius: 4px; 73 | box-shadow: 0 5px 5px -3px rgb(0 0 0 / 20%), 0px 8px 10px 1px rgb(0 0 0 / 14%), 0px 3px 14px 2px rgb(0 0 0 / 12%); 74 | width: 250px; 75 | 76 | 77 | li { 78 | color: #fff; 79 | font-size: 14px; 80 | font-weight: 300; 81 | display: flex; 82 | justify-content: flex-start; 83 | align-items: center; 84 | height: 40px; 85 | padding: 4px 1em; 86 | border-bottom: 1px solid #687078; 87 | 88 | img { 89 | margin-right: 1em; 90 | } 91 | 92 | span { 93 | width: 100%; 94 | text-align: left; 95 | } 96 | 97 | h4 { 98 | margin: 0; 99 | } 100 | 101 | &:hover { 102 | background: #1B1E20; 103 | } 104 | 105 | &:last-child { 106 | border: none; 107 | } 108 | } 109 | } 110 | } 111 | 112 | .show-logout { 113 | opacity: 1; 114 | } 115 | } 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /source/web-app/src/app/components/pipeline/components/plugin-selection/plugin-selection.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Component, ElementRef, ViewChild} from '@angular/core'; 17 | import {faBug, faCircleCheck} from "@fortawesome/free-solid-svg-icons"; 18 | import {faPython} from "@fortawesome/free-brands-svg-icons"; 19 | 20 | @Component({ 21 | selector: 'app-plugin-selection', 22 | templateUrl: './plugin-selection.component.html', 23 | styleUrls: ['../../pipeline.component.scss'] 24 | }) 25 | export class PluginSelectionComponent { 26 | pluginSelectionTab = 1; 27 | faCheck = faCircleCheck; 28 | faBug = faBug; 29 | faPython = faPython; 30 | showUploadError = false; 31 | plugin: any; 32 | s3Uri: string | undefined; 33 | existingPlugin: any; 34 | 35 | @ViewChild("pluginInput") pluginInput: ElementRef | undefined; 36 | 37 | dragEnterHandler(event: DragEvent) { 38 | event.preventDefault(); 39 | const container = document.querySelector(".plugin-upload"); 40 | if (container) { 41 | container.classList.add("drop-enter"); 42 | } 43 | } 44 | 45 | dragLeaveHandler(event: DragEvent) { 46 | event.preventDefault(); 47 | const container = document.querySelector(".plugin-upload"); 48 | if (container) { 49 | container.classList.remove("drop-enter"); 50 | } 51 | } 52 | 53 | dragOverHandler(event: DragEvent) { 54 | event.preventDefault(); 55 | } 56 | 57 | dropHandler(event: any) { 58 | event.preventDefault(); 59 | event.stopPropagation(); 60 | this.showUploadError = false; 61 | const files: any = Array.from(event.dataTransfer.files) || []; 62 | if (this.validateUpload(files[0])) { 63 | this.plugin = files[0]; 64 | } else { 65 | this.showUploadError = true; 66 | } 67 | const container = document.querySelector(".plugin-upload"); 68 | if (container) { 69 | container.classList.remove("drop-enter"); 70 | } 71 | } 72 | 73 | triggerPluginInput() { 74 | this.pluginInput?.nativeElement.click(); 75 | } 76 | 77 | onPluginUpload(event: any) { 78 | event.stopPropagation(); 79 | event.preventDefault(); 80 | this.plugin = event.target.files[0]; 81 | } 82 | 83 | validateUpload(file: File) { 84 | return file.name.includes('.py'); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/pipeline/components/dataset-selection/dataset-selection.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Component, ElementRef, ViewChild} from '@angular/core'; 17 | import {faBug, faCircleCheck, faFileCsv,} from "@fortawesome/free-solid-svg-icons"; 18 | import {faPython} from "@fortawesome/free-brands-svg-icons"; 19 | 20 | @Component({ 21 | selector: 'app-dataset-selection', 22 | templateUrl: './dataset-selection.component.html', 23 | styleUrls: ['../../pipeline.component.scss'] 24 | }) 25 | export class DatasetSelectionComponent { 26 | datasetSelectionTab = 1; 27 | faCheck = faCircleCheck; 28 | faBug = faBug; 29 | faCsv = faFileCsv; 30 | faPython = faPython; 31 | showUploadError = false; 32 | file: any; 33 | s3Uri: string | undefined; 34 | @ViewChild("datasetInput") datasetInput: ElementRef | undefined; 35 | 36 | dragEnterHandler(event: DragEvent) { 37 | event.preventDefault(); 38 | const container = document.querySelector(".dataset-upload"); 39 | if (container) { 40 | container.classList.add("drop-enter"); 41 | } 42 | } 43 | 44 | dragLeaveHandler(event: DragEvent) { 45 | event.preventDefault(); 46 | const container = document.querySelector(".dataset-upload"); 47 | if (container) { 48 | container.classList.remove("drop-enter"); 49 | } 50 | } 51 | 52 | dragOverHandler(event: DragEvent) { 53 | event.preventDefault(); 54 | } 55 | 56 | dropHandler(event: any) { 57 | event.preventDefault(); 58 | event.stopPropagation(); 59 | this.showUploadError = false; 60 | const files: any = Array.from(event.dataTransfer.files) || []; 61 | if (this.validateUpload(files[0])) { 62 | this.file = files[0]; 63 | } else { 64 | this.showUploadError = true; 65 | } 66 | const container = document.querySelector(".dataset-upload"); 67 | if (container) { 68 | container.classList.remove("drop-enter"); 69 | } 70 | } 71 | 72 | triggerInput() { 73 | this.datasetInput?.nativeElement.click(); 74 | } 75 | 76 | onDatasetUpload(event: any) { 77 | event.stopPropagation(); 78 | event.preventDefault(); 79 | this.file = event.target.files[0]; 80 | } 81 | 82 | validateUpload(file: File) { 83 | return file.name.includes(".csv"); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /source/api/lib/cleanExportsFn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | import { GlueClient, StartJobRunCommand } from "@aws-sdk/client-glue"; 17 | import { fromEnv } from "@aws-sdk/credential-providers"; 18 | import { AppSyncClient } from "./appsync"; 19 | 20 | const glue = new GlueClient({ region: process.env.AWS_REGION }); 21 | const appSync = new AppSyncClient({ 22 | graphQlUrl: process.env.APPSYNC_GRAPHQL_URL!, 23 | credentials: fromEnv(), 24 | }); 25 | 26 | /** 27 | * Builds response before sending back to UI 28 | * @param http_code 29 | * @param body 30 | */ 31 | function build_response(http_code: number, body: any) { 32 | return { 33 | headers: { 34 | "Cache-Control": "no-cache, no-store", // tell cloudfront and api gateway not to cache the response 35 | "Content-Type": "application/json", 36 | }, 37 | statusCode: http_code, 38 | body: JSON.stringify(body), 39 | }; 40 | } 41 | 42 | /** 43 | * Create and run Glue job on processor script upload 44 | * @param event 45 | * @param context 46 | */ 47 | export async function handler(event?: any, context?: any) { 48 | let data: String[] = []; 49 | 50 | try { 51 | const fcastArn = event["resources"][0]; 52 | const exportId = fcastArn.split("/")[2].split("_"); 53 | const userId = exportId[0]; 54 | const pipeId = exportId[1]; 55 | 56 | // Kick off post processing job 57 | const run = await glue.send( 58 | new StartJobRunCommand({ 59 | JobName: process.env.GLUE_JOB_NAME, 60 | Arguments: { 61 | "--s3_bucket": process.env.DATA_BUCKET!, 62 | "--output_path": `${userId}/${pipeId}/${process.env.FORECAST_OUTPUT_PREFIX}`, 63 | }, 64 | }) 65 | ); 66 | data.push(`Glue job run initiated: ${run["JobRunId"]}`); 67 | 68 | // Update pipeline info in ddb table 69 | await appSync.post({ 70 | query: `mutation MyMutation($input: PipelineRequestInput!) { 71 | pipeline(input: $input) { Id PipelineStatus StatusUpdatedAt } 72 | }`, 73 | variables: { 74 | input: { 75 | Id: pipeId, 76 | PostProcessingId: run["JobRunId"], 77 | PipelineStatus: process.env.PIPELINE_STATUS, 78 | StatusUpdatedAt: new Date().toISOString(), 79 | }, 80 | }, 81 | }); 82 | data.push(`Pipeline status updated to ${process.env.PIPELINE_STATUS}`); 83 | 84 | return build_response(200, data); 85 | } catch (err) { 86 | console.error(err); 87 | data.push("Server Error"); 88 | 89 | return build_response(500, data); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/tracker/tracker.component.scss: -------------------------------------------------------------------------------- 1 | .tracker-container { 2 | width: 100%; 3 | height: 100vh; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: space-evenly; 8 | 9 | .pipeline-info { 10 | width: 90%; 11 | 12 | .created-date { 13 | display: flex; 14 | flex-direction: column; 15 | align-items: flex-start; 16 | 17 | span { 18 | font-size: 14px; 19 | } 20 | span:first-child { 21 | font-size: 2em; 22 | } 23 | } 24 | } 25 | 26 | ul { 27 | list-style: none; 28 | display: flex; 29 | margin: 0; 30 | padding: 0; 31 | width: 100%; 32 | justify-content: center; 33 | align-items: center; 34 | 35 | li { 36 | display: flex; 37 | flex-direction: column; 38 | justify-content: center; 39 | align-items: center; 40 | position: relative; 41 | 42 | .progress-step-img { 43 | width: 80px; 44 | height: 80px; 45 | margin: 1em 0; 46 | } 47 | 48 | .progress-step-info { 49 | display: flex; 50 | flex-direction: column; 51 | justify-content: flex-start; 52 | align-items: center; 53 | width: 100px; 54 | min-height: 150px; 55 | 56 | h6 { 57 | margin: 2em 0 1em 0; 58 | } 59 | 60 | h4 { 61 | text-align: center; 62 | margin: 0 0 0.5em; 63 | } 64 | 65 | .step-timer { 66 | font-size: 12px; 67 | width: 100px; 68 | text-align: center; 69 | } 70 | } 71 | 72 | .progress-bar { 73 | display: flex; 74 | justify-content: center; 75 | align-items: center; 76 | 77 | img { 78 | transform: scale(1.06); 79 | height: 30px; 80 | width: 30px; 81 | } 82 | 83 | hr { 84 | height: 8px; 85 | background: #FF9900; 86 | width: 100px; 87 | border: 0; 88 | } 89 | 90 | hr.current-step { 91 | background: #979797; 92 | } 93 | 94 | .hide-bar { 95 | background: transparent !important; 96 | } 97 | } 98 | } 99 | 100 | .disable-step { 101 | 102 | hr { 103 | background: #979797 !important; 104 | } 105 | 106 | .progress-step-info h6, h4, span { 107 | color: #545B64; 108 | } 109 | } 110 | } 111 | 112 | .error { 113 | display: flex; 114 | flex-direction: column; 115 | justify-content: center; 116 | align-items: center; 117 | 118 | h5 { 119 | font-weight: normal; 120 | } 121 | } 122 | 123 | .tracker-action { 124 | } 125 | } 126 | 127 | .animate-inprogress { 128 | animation: highlight 1s linear infinite; 129 | } 130 | 131 | @keyframes highlight { 132 | 0% { 133 | transform: scale(1); 134 | } 135 | 50% { 136 | transform: scale(1.5); 137 | } 138 | 100% { 139 | transform: scale(1); 140 | } 141 | 142 | } 143 | 144 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/pipeline/pipeline.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 |
6 |
7 |
    8 |
  • 9 |
    10 | 1 11 |
    12 | Battery Dataset 13 |
  • 14 |
    15 |
  • 16 |
    20 | 2 21 |
    22 | Processing Plugin 23 |
  • 24 |
    25 |
  • 26 |
    27 | 3 28 |
    29 | Review 30 |
  • 31 |
32 |
33 |
34 | 35 | 36 |
37 |
38 | Review your uploaded files 39 |
    40 |
  • 41 | 42 | {{originalDatasetFileName}} 43 |
  • 44 |
  • 45 | 46 | {{originalPluginFileName}} 47 |
  • 48 |
49 |
50 | You are ready, launch your pipeline 51 | Launch Pipeline 52 |
53 |
54 |
56 | 62 |
63 | 69 | 76 |
77 | 83 |
84 |
85 | 86 | 87 | -------------------------------------------------------------------------------- /source/deploy/src/app.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | 18 | import { CfWafStack } from "./cf-waf-stack"; 19 | import { AppStack } from "./app-stack"; 20 | import { MLOpsStack } from "./ml-ops-stack"; 21 | import { AwsSolutionsChecks } from "cdk-nag"; 22 | import { suppressCdkNagRules } from "./cdk-nag-suppressions"; 23 | 24 | // Custom config providers. 25 | import { getConfigProvider } from "./config/index"; 26 | const gitContext = getConfigProvider("git")(); 27 | 28 | process.env.CDK_DEBUG = "true"; 29 | const app = new cdk.App(); 30 | 31 | const stackName = 32 | app.node.tryGetContext("stack_name") || gitContext.appStackName; 33 | const account = 34 | app.node.tryGetContext("account") || 35 | process.env.CDK_DEPLOY_ACCOUNT || 36 | process.env.CDK_DEFAULT_ACCOUNT; 37 | const region = 38 | app.node.tryGetContext("region") || 39 | process.env.CDK_DEPLOY_REGION || 40 | process.env.CDK_DEFAULT_REGION; 41 | 42 | const lambdaFnPath = "../api/lib"; 43 | 44 | // Deploy Waf for CloudFront in us-east-1 45 | const cfWafStackName = stackName + "-waf"; 46 | 47 | const cfWafStack = new CfWafStack(app, cfWafStackName, { 48 | env: { 49 | account: account, 50 | region: "us-east-1", 51 | }, 52 | description: "Guidance for Electric Vehicle (EV) Battery Health Prediction (SO9184)", 53 | stackName: cfWafStackName, 54 | }); 55 | 56 | // Deploy App Stack 57 | const appStack = new AppStack(app, stackName, { 58 | env: { 59 | account: account, 60 | region: region, 61 | }, 62 | description: "Guidance for Electric Vehicle (EV) Battery Health Prediction (SO9184)", 63 | stackName: stackName, 64 | ssmWafArnParameterName: cfWafStack.ssmWafArnParameterName, 65 | ssmWafArnParameterRegion: cfWafStack.region, 66 | lambdaFnPath: lambdaFnPath, 67 | }); 68 | 69 | appStack.addDependency(cfWafStack); 70 | 71 | // Deploy ML pipeline stack 72 | const mlStack = new MLOpsStack(app, stackName + "-ml", { 73 | env: { 74 | account: account, 75 | region: region, 76 | }, 77 | description: "Guidance for Electric Vehicle (EV) Battery Health Prediction (SO9184)", 78 | libraryBucket: appStack.bucket, 79 | lambdaFnPath: lambdaFnPath, 80 | pipelineTable: appStack.pipelineTable, 81 | appSyncApi: appStack.appSyncApi, 82 | }); 83 | 84 | mlStack.addDependency(appStack); 85 | 86 | // Add Aws Solutions Checks and suppress rules 87 | cdk.Aspects.of(app).add(new AwsSolutionsChecks({ logIgnores: true })); 88 | suppressCdkNagRules(cfWafStack); 89 | suppressCdkNagRules(mlStack); 90 | suppressCdkNagRules(appStack); 91 | 92 | app.synth(); 93 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/header/header.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {Component, OnInit} from "@angular/core"; 17 | import {AuthService} from "../../../../services/auth.service"; 18 | import {Auth} from "aws-amplify"; 19 | import {faUser} from "@fortawesome/free-solid-svg-icons"; 20 | import {UserPreferenceService} from "../../../../services/user-preference.service"; 21 | import {Router} from "@angular/router"; 22 | import {environment} from "../../../../../environments/environment"; 23 | 24 | @Component({ 25 | selector: "app-header", 26 | templateUrl: "./header.component.html", 27 | styleUrls: ["./header.component.scss"], 28 | }) 29 | export class HeaderComponent implements OnInit { 30 | isLoggedIn = false; 31 | userName = ""; 32 | faUser = faUser; 33 | showLogout = false; 34 | showSidebar = false; 35 | showSidebarContainer = false; 36 | showSpinner = false; 37 | logoSrc = environment.NG_APP_LOGO; 38 | customerName = environment.NG_APP_NAME; 39 | 40 | toggleMenu = () => { 41 | this.showLogout = false; 42 | this.removeClickEvent() 43 | } 44 | toggleMenuBind = this.toggleMenu.bind(this); 45 | 46 | constructor(private authService: AuthService, public userPrefService: UserPreferenceService, private router: Router) {} 47 | 48 | ngOnInit(): void { 49 | this.authService.getLoginStatus().then(async (status) => { 50 | this.isLoggedIn = status; 51 | if (this.isLoggedIn) { 52 | const userInfo = await Auth.currentUserInfo(); 53 | this.userName = userInfo.username; 54 | } 55 | }); 56 | } 57 | 58 | signOut() { 59 | this.showSpinner = true; 60 | this.authService.logout(); 61 | } 62 | 63 | switchProfile() { 64 | if (this.userPrefService.currentPersona === 1) 65 | this.userPrefService.currentPersona = 2; 66 | else 67 | this.userPrefService.currentPersona = 1; 68 | this.showLogout = false; 69 | if (!this.router.url.includes('/dashboard')) { 70 | this.router.navigate(['/dashboard']); 71 | } 72 | } 73 | 74 | navHandler() { 75 | this.showLogout = !this.showLogout; 76 | if(this.showLogout) { 77 | setTimeout(() => { 78 | window.addEventListener('click', this.toggleMenuBind); 79 | }); 80 | } 81 | } 82 | 83 | private removeClickEvent() { 84 | window.removeEventListener('click', this.toggleMenuBind); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /source/web-app/src/app/modules/shared/components/spinner/spinner.component.scss: -------------------------------------------------------------------------------- 1 | .spinner-container { 2 | width: 100%; 3 | height: 100%; 4 | background: rgba(0, 0, 0, 0.3);; 5 | backdrop-filter: blur(15px); 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: center; 9 | align-items: center; 10 | top: 0; 11 | left: 0; 12 | font-size: 14px; 13 | color: #fff; 14 | z-index: 10; 15 | 16 | .spinner-content { 17 | position: absolute; 18 | width: 250px; 19 | height: 350px; 20 | display: flex; 21 | flex-direction: column; 22 | justify-content: center; 23 | align-items: center; 24 | } 25 | 26 | .status-text { 27 | position: absolute; 28 | top: 250px; 29 | 30 | h4 { 31 | padding-left: 5px; 32 | text-align: left; 33 | font-weight: 400; 34 | letter-spacing: 5px; 35 | } 36 | } 37 | 38 | .spinner { 39 | display: flex; 40 | flex-direction: column; 41 | justify-content: center; 42 | align-items: center; 43 | position: relative; 44 | left: -20%; 45 | } 46 | 47 | .spinner div { 48 | animation: spinner 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; 49 | transform-origin: 40px 40px; 50 | } 51 | .spinner div:after { 52 | content: " "; 53 | display: block; 54 | position: absolute; 55 | width: 7px; 56 | height: 7px; 57 | border-radius: 50%; 58 | background: #fff; 59 | margin: -4px 0 0 -4px; 60 | } 61 | .spinner div:nth-child(1) { 62 | animation-delay: -0.036s; 63 | } 64 | .spinner div:nth-child(1):after { 65 | top: 63px; 66 | left: 63px; 67 | } 68 | .spinner div:nth-child(2) { 69 | animation-delay: -0.072s; 70 | } 71 | .spinner div:nth-child(2):after { 72 | top: 68px; 73 | left: 56px; 74 | } 75 | .spinner div:nth-child(3) { 76 | animation-delay: -0.108s; 77 | } 78 | .spinner div:nth-child(3):after { 79 | top: 71px; 80 | left: 48px; 81 | } 82 | .spinner div:nth-child(4) { 83 | animation-delay: -0.144s; 84 | } 85 | .spinner div:nth-child(4):after { 86 | top: 72px; 87 | left: 40px; 88 | } 89 | .spinner div:nth-child(5) { 90 | animation-delay: -0.18s; 91 | } 92 | .spinner div:nth-child(5):after { 93 | top: 71px; 94 | left: 32px; 95 | } 96 | .spinner div:nth-child(6) { 97 | animation-delay: -0.216s; 98 | } 99 | .spinner div:nth-child(6):after { 100 | top: 68px; 101 | left: 24px; 102 | } 103 | .spinner div:nth-child(7) { 104 | animation-delay: -0.252s; 105 | } 106 | .spinner div:nth-child(7):after { 107 | top: 63px; 108 | left: 17px; 109 | } 110 | .spinner div:nth-child(8) { 111 | animation-delay: -0.288s; 112 | } 113 | .spinner div:nth-child(8):after { 114 | top: 56px; 115 | left: 12px; 116 | } 117 | @keyframes spinner { 118 | 0% { 119 | transform: rotate(0deg); 120 | } 121 | 100% { 122 | transform: rotate(360deg); 123 | } 124 | } 125 | } 126 | 127 | .inline-position { 128 | position: absolute; 129 | } 130 | 131 | .fixed-position { 132 | position: fixed; 133 | } -------------------------------------------------------------------------------- /source/deploy/src/constructs/wafv2-basic-construct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | import { Construct } from "constructs"; 18 | 19 | /** 20 | * defines the scope for the WebACL 21 | * Cloudfront - for cloud front for cloud front distributions 22 | * Regional - for load balancers and api gateway's 23 | */ 24 | export enum WafV2Scope { 25 | /** 26 | * for cloudfront distributions 27 | */ 28 | CLOUDFRONT = "CLOUDFRONT", 29 | /** 30 | * for api gateways, loadbalancers and other supported resources 31 | */ 32 | REGIONAL = "REGIONAL", 33 | } 34 | 35 | export interface Wafv2BasicConstructProps extends cdk.StackProps { 36 | /** 37 | * The ACL scope. 38 | */ 39 | readonly wafScope: WafV2Scope; 40 | 41 | /** 42 | * Optional rules for the firewall 43 | */ 44 | readonly rules?: 45 | | Array 46 | | cdk.IResolvable; 47 | 48 | /** 49 | * The region where the WAF will be deployed 50 | */ 51 | readonly region?: string; 52 | } 53 | 54 | /** 55 | * Default input properties 56 | */ 57 | const defaultProps: Partial = { 58 | region: "us-east-1", 59 | }; 60 | 61 | /** 62 | * Deploys a basic WAFv2 ACL that is open by default 63 | */ 64 | export class Wafv2BasicConstruct extends Construct { 65 | public webacl: cdk.aws_wafv2.CfnWebACL; 66 | 67 | constructor(parent: Construct, name: string, props: Wafv2BasicConstructProps) { 68 | super(parent, name); 69 | 70 | props = { ...defaultProps, ...props }; 71 | 72 | const wafScopeString = props.wafScope.toString(); 73 | 74 | if (props.wafScope === WafV2Scope.CLOUDFRONT && props.region !== "us-east-1") { 75 | throw new Error( 76 | "Only supported region for WAFv2 scope when set to CLOUDFRONT is us-east-1. " + 77 | "see - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-wafv2-webacl.html" 78 | ); 79 | } 80 | 81 | const webacl = new cdk.aws_wafv2.CfnWebACL(this, "webacl", { 82 | description: "Basic waf", 83 | defaultAction: { 84 | allow: {}, // allow everything by default 85 | }, 86 | rules: props.rules, 87 | scope: wafScopeString, 88 | visibilityConfig: { 89 | cloudWatchMetricsEnabled: true, 90 | metricName: "WAFACLGlobal", 91 | sampledRequestsEnabled: true, 92 | }, 93 | }); 94 | 95 | this.webacl = webacl; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /source/deploy/assets/connected-batteries.json: -------------------------------------------------------------------------------- 1 | { 2 | "b1": { 3 | "batteryName": "b1", 4 | "batteryModel": "29.1 x 19.6 x 15.7", 5 | "batteryClass": "ABC", 6 | "batteryMaterial": "Lithium", 7 | "numberOfModules": 12, 8 | "modulesUnderWarning": [3, 8], 9 | "modulesNeedAttention": [], 10 | "temperature": 98, 11 | "current": 3.9, 12 | "voltage": 85, 13 | "polarization": 123, 14 | "capacity": 123, 15 | "impedance": 2, 16 | "chargingDetails": { 17 | "currentCharge": 53, 18 | "lastChargingTime": "00:02:32", 19 | "chargingRate": "123 A", 20 | "chargingVoltage": "12 V", 21 | "fastChargingDuration": "00:34:34", 22 | "numberOfFastCharges": 12, 23 | "totalFastCharges": 20, 24 | "numberOfL1": 24, 25 | "totalNumberOfL1": 50, 26 | "numberOfL2": 14, 27 | "totalNumberOfL2": 50, 28 | "chargeCycles": 234 29 | }, 30 | "drivingDetails": { 31 | "drivingScore": 86, 32 | "distanceToTravel": 2934 33 | }, 34 | "faultThresholds": { 35 | "temperatureMin": 0, 36 | "temperatureMax": 200, 37 | "voltageMin": 0, 38 | "voltageMax": 200, 39 | "currentMin": 0, 40 | "currentMax": 10, 41 | "temperatureThresholdMin": 50, 42 | "temperatureThresholdMax": 150, 43 | "voltageThresholdMin": 50, 44 | "voltageThresholdMax": 150, 45 | "currentThresholdMin": 2.5, 46 | "currentThresholdMax": 7.5 47 | }, 48 | "stateOfCharge": 87, 49 | "expectedEOL": 1862, 50 | "lithiumPlatingDegree": 3, 51 | "availablePower": 123 52 | }, 53 | "b3": { 54 | "batteryName": "b3", 55 | "batteryModel": "29.1 x 19.6 x 15.7", 56 | "batteryClass": "DEF", 57 | "batteryMaterial": "Lithium", 58 | "numberOfModules": 24, 59 | "modulesUnderWarning": [3, 8, 17], 60 | "modulesNeedAttention": [], 61 | "temperature": 96, 62 | "current": 3.8, 63 | "voltage": 94, 64 | "polarization": 112, 65 | "capacity": 112, 66 | "impedance": 2, 67 | "chargingDetails": { 68 | "currentCharge": 75, 69 | "lastChargingTime": "00:35:32", 70 | "chargingRate": "112 A", 71 | "chargingVoltage": "12 V", 72 | "fastChargingDuration": "00:35:32", 73 | "numberOfFastCharges": 21, 74 | "totalFastCharges": 40, 75 | "numberOfL1": 29, 76 | "totalNumberOfL1": 50, 77 | "numberOfL2": 26, 78 | "totalNumberOfL2": 50, 79 | "chargeCycles": 412 80 | }, 81 | "drivingDetails": { 82 | "drivingScore": 91, 83 | "distanceToTravel": 1734 84 | }, 85 | "faultThresholds": { 86 | "temperatureMin": 0, 87 | "temperatureMax": 200, 88 | "voltageMin": 0, 89 | "voltageMax": 200, 90 | "currentMin": 0, 91 | "currentMax": 10, 92 | "temperatureThresholdMin": 50, 93 | "temperatureThresholdMax": 150, 94 | "voltageThresholdMin": 50, 95 | "voltageThresholdMax": 150, 96 | "currentThresholdMin": 2.5, 97 | "currentThresholdMax": 7.5 98 | }, 99 | "stateOfCharge": 91, 100 | "expectedEOL": 1862, 101 | "lithiumPlatingDegree": 3, 102 | "availablePower": 123 103 | } 104 | } -------------------------------------------------------------------------------- /source/api/lib/trainModelFn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | import { fromEnv } from "@aws-sdk/credential-providers"; 17 | import { AppSyncClient } from "./appsync"; 18 | import { 19 | ForecastClient, 20 | CreateAutoPredictorCommand, 21 | } from "@aws-sdk/client-forecast"; 22 | 23 | const fcast = new ForecastClient({ region: process.env.AWS_REGION }); 24 | const appSync = new AppSyncClient({ 25 | graphQlUrl: process.env.APPSYNC_GRAPHQL_URL!, 26 | credentials: fromEnv(), 27 | }); 28 | 29 | /** 30 | * Builds response before sending back to UI 31 | * @param http_code 32 | * @param body 33 | */ 34 | function build_response(http_code: number, body: any) { 35 | return { 36 | headers: { 37 | "Cache-Control": "no-cache, no-store", // tell cloudfront and api gateway not to cache the response 38 | "Content-Type": "application/json", 39 | }, 40 | statusCode: http_code, 41 | body: JSON.stringify(body), 42 | }; 43 | } 44 | 45 | /** 46 | * Import training dataset into Forecast 47 | * @param event 48 | * @param context 49 | */ 50 | export async function handler(event?: any, context?: any) { 51 | let data: String[] = []; 52 | 53 | try { 54 | const importArn = event["resources"][0]; 55 | const fcastId = importArn.split("/")[1]; 56 | const pipeId = fcastId.split("_")[1]; 57 | 58 | const arnBase = importArn.substr(0, importArn.lastIndexOf(":")); 59 | const dsgArn = `${arnBase}:dataset-group/${fcastId}`; 60 | 61 | // Kick off AutoML model training 62 | const predictor = await fcast.send( 63 | new CreateAutoPredictorCommand({ 64 | PredictorName: `${fcastId}_${Date.now()}`, 65 | ForecastHorizon: parseInt(`${process.env.FORECAST_HORIZON}`), 66 | ForecastFrequency: process.env.DATASET_FREQUENCY, 67 | DataConfig: { DatasetGroupArn: dsgArn }, 68 | }) 69 | ); 70 | data.push(`Initiated predictor training: ${predictor.PredictorArn}`); 71 | 72 | // Update pipeline info in ddb table 73 | await appSync.post({ 74 | query: `mutation MyMutation($input: PipelineRequestInput!) { 75 | pipeline(input: $input) { Id PipelineStatus StatusUpdatedAt } 76 | }`, 77 | variables: { 78 | input: { 79 | Id: pipeId, 80 | DataImportedAt: event["time"], 81 | PredictorArn: predictor.PredictorArn, 82 | PipelineStatus: process.env.PIPELINE_STATUS, 83 | StatusUpdatedAt: new Date().toISOString(), 84 | }, 85 | }, 86 | }); 87 | data.push(`Pipeline status updated to ${process.env.PIPELINE_STATUS}`); 88 | 89 | return build_response(200, data); 90 | } catch (err) { 91 | console.error(err); 92 | data.push("Server Error"); 93 | 94 | return build_response(500, data); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /source/web-app/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | /** 17 | * This file includes polyfills needed by Angular and is loaded before the app. 18 | * You can add your own extra polyfills to this file. 19 | * 20 | * This file is divided into 2 sections: 21 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 22 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 23 | * file. 24 | * 25 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 26 | * automatically update themselves. This includes recent versions of Safari, Chrome (including 27 | * Opera), Edge on the desktop, and iOS and Chrome on mobile. 28 | * 29 | * Learn more in https://angular.io/guide/browser-support 30 | */ 31 | 32 | /*************************************************************************************************** 33 | * BROWSER POLYFILLS 34 | */ 35 | 36 | /** 37 | * By default, zone.js will patch all possible macroTask and DomEvents 38 | * user can disable parts of macroTask/DomEvents patch by setting following flags 39 | * because those flags need to be set before `zone.js` being loaded, and webpack 40 | * will put import in the top of bundle, so user need to create a separate file 41 | * in this directory (for example: zone-flags.ts), and put the following flags 42 | * into that file, and then add the following code before importing zone.js. 43 | * import './zone-flags'; 44 | * 45 | * The flags allowed in zone-flags.ts are listed here. 46 | * 47 | * The following flags will work for all browsers. 48 | * 49 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 50 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 51 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 52 | * 53 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 54 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 55 | * 56 | * (window as any).__Zone_enable_cross_context_check = true; 57 | * 58 | */ 59 | 60 | /*************************************************************************************************** 61 | * Zone JS is required by default for Angular itself. 62 | */ 63 | import 'zone.js'; // Included with Angular CLI. 64 | import 'zone.js/dist/zone'; // Included with Angular CLI. 65 | 66 | 67 | /*************************************************************************************************** 68 | * APPLICATION IMPORTS 69 | */ 70 | 71 | (window as any).global = window; 72 | (window as any).process = { 73 | env: { DEBUG: undefined }, 74 | }; 75 | 76 | (window as any).Zone['__zone_symbol__ignoreConsoleErrorUncaughtError'] = true; -------------------------------------------------------------------------------- /source/deploy/src/constructs/eventbridge-lambda-contruct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | import { Construct } from "constructs"; 18 | import { LambdaFunctionConstruct } from "./lambda-function-construct"; 19 | 20 | const events = cdk.aws_events; 21 | const targets = cdk.aws_events_targets; 22 | 23 | export interface EventBridgeLambdaProps extends cdk.StackProps { 24 | readonly pipelineStatus: string; 25 | readonly libraryBucket: cdk.aws_s3.Bucket; 26 | readonly appSyncApi: cdk.aws_appsync.GraphqlApi; 27 | readonly triggerType: string; 28 | readonly triggerFilter?: string; 29 | readonly serviceRole?: cdk.aws_iam.Role; 30 | readonly lambdaFnPath: string; 31 | readonly lambdaAsset: string; 32 | readonly lambdaPolicy?: cdk.aws_iam.PolicyStatement; 33 | readonly lambdaEnvConfig?: { [key: string]: string }; 34 | } 35 | 36 | const defaultProps: Partial = {}; 37 | 38 | export class EventBridgeLambdaConstruct extends Construct { 39 | constructor(parent: Construct, name: string, props: EventBridgeLambdaProps) { 40 | super(parent, name); 41 | 42 | props = { ...defaultProps, ...props }; 43 | 44 | const lambdaFn = new LambdaFunctionConstruct(this, "Fn", { 45 | lambdaAsset: `${props.lambdaFnPath}/${props.lambdaAsset}`, 46 | lambdaEnvConfig: { 47 | ...props.lambdaEnvConfig, 48 | PIPELINE_STATUS: props.pipelineStatus, 49 | APPSYNC_GRAPHQL_URL: props.appSyncApi.graphqlUrl, 50 | SERVICE_ROLE_ARN: props.serviceRole?.roleArn!, 51 | }, 52 | }).function; 53 | 54 | if (props.lambdaPolicy) lambdaFn.addToRolePolicy(props.lambdaPolicy); 55 | 56 | // Fill out trigger information based on trigger info 57 | let triggerSrc, 58 | triggerFilter = null; 59 | if (props.triggerType.match(/glue/i)) { 60 | triggerSrc = "glue"; 61 | triggerFilter = { 62 | state: ["SUCCEEDED"], 63 | jobName: [props.triggerFilter], 64 | }; 65 | } else if (props.triggerType.match(/forecast/i)) { 66 | triggerSrc = "forecast"; 67 | triggerFilter = { status: ["ACTIVE"] }; 68 | } else { 69 | triggerSrc = "s3"; 70 | triggerFilter = { 71 | bucket: { name: [props.libraryBucket.bucketName] }, 72 | object: { key: [{ suffix: props.triggerFilter }] }, 73 | }; 74 | } 75 | 76 | const eventTrigger = new events.Rule(this, "Trigger", { 77 | eventPattern: { 78 | source: [`aws.${triggerSrc}`], 79 | detailType: [props.triggerType], 80 | detail: triggerFilter, 81 | }, 82 | }); 83 | 84 | // Allow lambda to pass role to Forecast import 85 | props.serviceRole?.grantPassRole(lambdaFn.grantPrincipal); 86 | 87 | props.appSyncApi.grantQuery(lambdaFn); 88 | props.appSyncApi.grantMutation(lambdaFn); 89 | 90 | eventTrigger.addTarget(new targets.LambdaFunction(lambdaFn)); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /source/api/lib/processDataFn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | import { 17 | GlueClient, 18 | CreateJobCommand, 19 | StartJobRunCommand, 20 | } from "@aws-sdk/client-glue"; 21 | import { fromEnv } from "@aws-sdk/credential-providers"; 22 | import { AppSyncClient } from "./appsync"; 23 | 24 | const glue = new GlueClient({ region: process.env.AWS_REGION }); 25 | const appSync = new AppSyncClient({ 26 | graphQlUrl: process.env.APPSYNC_GRAPHQL_URL!, 27 | credentials: fromEnv(), 28 | }); 29 | 30 | /** 31 | * Builds response before sending back to UI 32 | * @param http_code 33 | * @param body 34 | */ 35 | function build_response(http_code: number, body: any) { 36 | return { 37 | headers: { 38 | "Cache-Control": "no-cache, no-store", // tell cloudfront and api gateway not to cache the response 39 | "Content-Type": "application/json", 40 | }, 41 | statusCode: http_code, 42 | body: JSON.stringify(body), 43 | }; 44 | } 45 | 46 | /** 47 | * Create and run Glue job on processor script upload 48 | * @param event 49 | * @param context 50 | */ 51 | export async function handler(event?: any, context?: any) { 52 | let data: String[] = []; 53 | 54 | try { 55 | const bucket = event["detail"]["bucket"]["name"]; 56 | const file = event["detail"]["object"]; 57 | const pipeId = file["key"].split("/")[1]; 58 | const pipePrefix = file["key"].substring(0, file["key"].lastIndexOf("/")); 59 | 60 | // Create Glue job from processing script 61 | const job = await glue.send( 62 | new CreateJobCommand({ 63 | Name: pipeId, 64 | Role: process.env.SERVICE_ROLE_ARN, 65 | Command: { 66 | Name: "glueetl", 67 | PythonVersion: "3", 68 | ScriptLocation: `s3://${bucket}/${pipePrefix}/${process.env.PLUGIN_SCRIPT_KEY}`, 69 | }, 70 | DefaultArguments: { 71 | "--s3_bucket": bucket, 72 | "--raw_dataset_key": file["key"], 73 | }, 74 | GlueVersion: "3.0", 75 | }) 76 | ); 77 | 78 | data.push(`Glue job created: ${job["Name"]}`); 79 | 80 | // Kick off glue job 81 | const run = await glue.send( 82 | new StartJobRunCommand({ JobName: job["Name"] }) 83 | ); 84 | 85 | data.push(`Glue job run initiated: ${run["JobRunId"]}`); 86 | 87 | // Update pipeline info in ddb table 88 | await appSync.post({ 89 | query: `mutation MyMutation($input: PipelineRequestInput!) { 90 | pipeline(input: $input) { Id PipelineStatus StatusUpdatedAt } 91 | }`, 92 | variables: { 93 | input: { 94 | Id: pipeId, 95 | DataUploadedAt: event["time"], 96 | RawDataUri: `s3://${bucket}/${file["key"]}`, 97 | RawDataSize: file["size"], 98 | PreProcessingId: run["JobRunId"], 99 | PipelineStatus: process.env.PIPELINE_STATUS, 100 | StatusUpdatedAt: new Date().toISOString(), 101 | }, 102 | }, 103 | }); 104 | data.push(`Pipeline status updated to ${process.env.PIPELINE_STATUS}`); 105 | 106 | return build_response(200, data); 107 | } catch (err) { 108 | console.error(err); 109 | data.push("Server Error"); 110 | 111 | return build_response(500, data); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/history/history.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

Pipelines

5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 64 | 65 | 66 |
Date CreatedDataset File NamePlugin NameStatus
No pipeline available
{{pipeline.DataUploadedAt | date: 'medium' : 'GMT'}}{{pipeline.OriginalDatasetName}}{{pipeline.OriginalPluginName}} 26 |
27 | 28 |
29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |
46 | 63 |
67 |
68 |
69 | 70 | -------------------------------------------------------------------------------- /source/api/lib/exportPredsFn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | 4 | Licensed under the Amazon Software License (the "License"). 5 | You may not use this file except in compliance with the License. 6 | A copy of the License is located at 7 | 8 | http://aws.amazon.com/asl/ 9 | 10 | or in the "license" file accompanying this file. This file is distributed 11 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | express or implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | */ 15 | 16 | import { fromEnv } from "@aws-sdk/credential-providers"; 17 | import { AppSyncClient } from "./appsync"; 18 | import { 19 | ForecastClient, 20 | CreateForecastExportJobCommand, 21 | } from "@aws-sdk/client-forecast"; 22 | 23 | const fcast = new ForecastClient({ region: process.env.AWS_REGION }); 24 | const appSync = new AppSyncClient({ 25 | graphQlUrl: process.env.APPSYNC_GRAPHQL_URL!, 26 | credentials: fromEnv(), 27 | }); 28 | 29 | /** 30 | * Builds response before sending back to UI 31 | * @param http_code 32 | * @param body 33 | */ 34 | function build_response(http_code: number, body: any) { 35 | return { 36 | headers: { 37 | "Cache-Control": "no-cache, no-store", // tell cloudfront and api gateway not to cache the response 38 | "Content-Type": "application/json", 39 | }, 40 | statusCode: http_code, 41 | body: JSON.stringify(body), 42 | }; 43 | } 44 | 45 | /** 46 | * Import training dataset into Forecast 47 | * @param event 48 | * @param context 49 | */ 50 | export async function handler(event?: any, context?: any) { 51 | let data: String[] = []; 52 | 53 | try { 54 | //arn:aws:forecast:us-east-1:157670018337:forecast/adi_uuid_1677324841398 55 | const fcastArn = event["resources"][0]; 56 | const arnId = fcastArn.split("/")[1]; 57 | const fcastId = arnId.substring(0, arnId.lastIndexOf("_")); 58 | const pipeId = fcastId.split("_")[1]; 59 | 60 | // Get S3 URI for pipe data from stored ddb entry 61 | const pipeData = await appSync.post({ 62 | query: `query ($Id: String!) { 63 | getPipelineById(Id: $Id) { 64 | RawDataUri 65 | } 66 | }`, 67 | variables: { Id: pipeId }, 68 | }); 69 | 70 | const rawDataUri = pipeData.data.getPipelineById.RawDataUri; 71 | const pipeDataUri = rawDataUri.substring(0, rawDataUri.lastIndexOf("/")); 72 | const fcastDataUri = `${pipeDataUri}/${process.env.FORECAST_OUTPUT_PREFIX}`; 73 | const timestamp = Date.now(); 74 | 75 | const exportJob = await fcast.send( 76 | new CreateForecastExportJobCommand({ 77 | ForecastExportJobName: fcastId, 78 | ForecastArn: fcastArn, 79 | Format: "CSV", 80 | Destination: { 81 | S3Config: { 82 | Path: `${fcastDataUri}/`, 83 | RoleArn: process.env.SERVICE_ROLE_ARN, 84 | }, 85 | }, 86 | }) 87 | ); 88 | data.push(`Exporting forecast data to ${fcastDataUri}`); 89 | 90 | /// Update pipeline info in ddb table 91 | await appSync.post({ 92 | query: `mutation MyMutation($input: PipelineRequestInput!) { 93 | pipeline(input: $input) { Id PipelineStatus StatusUpdatedAt } 94 | }`, 95 | variables: { 96 | input: { 97 | Id: pipeId, 98 | ForecastGeneratedAt: event["time"], 99 | ExportJobArn: exportJob.ForecastExportJobArn, 100 | PipelineStatus: process.env.PIPELINE_STATUS, 101 | StatusUpdatedAt: new Date().toISOString(), 102 | }, 103 | }, 104 | }); 105 | data.push(`Pipeline status updated to ${process.env.PIPELINE_STATUS}`); 106 | 107 | return build_response(200, data); 108 | } catch (err) { 109 | console.error(err); 110 | data.push("Server Error"); 111 | 112 | return build_response(500, data); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /source/web-app/src/app/components/map/map.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import {AfterViewInit, Component, EventEmitter, Input, Output} from '@angular/core'; 17 | import {createMap} from "maplibre-gl-js-amplify"; 18 | import * as maplibregl from "maplibre-gl"; 19 | import {APIService, GetLocationDataQuery} from "../../services/api.service"; 20 | 21 | @Component({ 22 | selector: 'app-map', 23 | templateUrl: './map.component.html', 24 | styleUrls: ['./map.component.scss'] 25 | }) 26 | export class MapComponent implements AfterViewInit { 27 | 28 | map: any; 29 | locations: GetLocationDataQuery[] = []; 30 | batteryInfo: any; 31 | showVehicleInfo = false; 32 | showSpinner = false; 33 | 34 | @Input() batteries: any; 35 | @Output() batterySelectionEmitter = new EventEmitter(); 36 | @Output() mapCreationEmitter = new EventEmitter(); 37 | constructor(private apiService: APIService) { 38 | } 39 | 40 | public ngAfterViewInit(): void { 41 | this.apiService.GetLocationData().then(locations => { 42 | this.locations = locations.filter(location => this.batteries.includes(location.BatteryId)); 43 | this.loadMap(); 44 | }); 45 | } 46 | 47 | private loadMap(): void { 48 | createMap({ 49 | container: 'map', 50 | center: [0, 0], 51 | zoom: 5, 52 | attributionControl: false 53 | }) 54 | .then((map: any) => { 55 | this.map = map; 56 | this.map.addControl(new maplibregl.NavigationControl({}), 'top-left'); 57 | this.map.addControl(new maplibregl.AttributionControl({customAttribution: ['Battery Locations']})); 58 | this.locations.forEach((l: any) => { 59 | const loc = document.createElement('div'); 60 | const pin = document.createElement('div'); 61 | const pulse = document.createElement('div'); 62 | loc.classList.add('loc'); 63 | pin.classList.add("pin"); 64 | if (l.BatteryId === 'b1') 65 | pin.classList.add('charging') 66 | else if (l.BatteryId === 'b2') 67 | pin.classList.add('not-charging') 68 | else 69 | pin.classList.add('need-charging') 70 | pulse.classList.add("pulse"); 71 | loc.append(pin, pulse); 72 | const marker = new maplibregl.Marker(loc, { 73 | anchor: 'bottom', 74 | }); 75 | marker.setLngLat([l.Lng, l.Lat]); 76 | marker.setPopup(new maplibregl.Popup({ 77 | closeButton: false, 78 | closeOnClick: false 79 | }).setHTML(` 80 |
81 |

${l.City}, ${l.Country}

82 |
VIN: ${l.VIN}
83 |
Battery: ${l.BatteryId}
84 |
`)); 85 | marker.addTo(this.map); 86 | loc.addEventListener('click', (event: any) => { 87 | event.stopImmediatePropagation(); 88 | event.preventDefault(); 89 | marker.togglePopup(); 90 | this.batteryInfo = l; 91 | this.batterySelectionEmitter.emit(this.batteryInfo); 92 | }); 93 | loc.addEventListener('mouseenter', () => marker.togglePopup()); 94 | loc.addEventListener('mouseleave', () => marker.togglePopup()); 95 | }); 96 | this.map.flyTo({center: [this.locations[0].Lng, this.locations[0].Lat], zoom: 5}); 97 | this.mapCreationEmitter.emit(true); 98 | setTimeout(() => this.showVehicleInfo = true, 1000); 99 | }); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /source/deploy/src/constructs/s3-library-construct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Amazon Software License (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/asl/ 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | import * as cdk from "aws-cdk-lib"; 17 | import { Construct } from "constructs"; 18 | 19 | const s3 = cdk.aws_s3; 20 | 21 | export interface S3LibraryConstructProps extends cdk.StackProps { 22 | /** 23 | * The Arn of the WafV2 WebAcl. 24 | */ 25 | readonly webAclArn?: string; 26 | } 27 | 28 | const defaultProps: Partial = {}; 29 | 30 | /** 31 | * Deploys a CloudFront Distribution pointing to an S3 bucket containing the deployed web application {webSiteBuildPath}. 32 | * Creates: 33 | * - S3 bucket 34 | * - CloudFrontDistribution 35 | * - OriginAccessIdentity 36 | * 37 | * On redeployment, will automatically invalidate the CloudFront distribution cache 38 | */ 39 | export class S3LibraryConstruct extends Construct { 40 | /** 41 | * The origin access identity used to access the S3 website 42 | */ 43 | public originAccessIdentity: cdk.aws_cloudfront.OriginAccessIdentity; 44 | 45 | /** 46 | * The cloud front distribution to attach additional behaviors like `/api` 47 | */ 48 | public s3bucket: cdk.aws_s3.Bucket; 49 | 50 | constructor(parent: Construct, name: string, props: S3LibraryConstructProps) { 51 | super(parent, name); 52 | 53 | props = { ...defaultProps, ...props }; 54 | 55 | // get the parent stack reference for the stackName and the aws region 56 | const stack = cdk.Stack.of(this); 57 | 58 | // When using Distribution, do not set the s3 bucket website documents 59 | // if these are set then the distribution origin is configured for HTTP communication with the 60 | // s3 bucket and won't configure the cloudformation correctly. 61 | const libraryBucket = new s3.Bucket(this, "Library", { 62 | encryption: s3.BucketEncryption.S3_MANAGED, 63 | autoDeleteObjects: true, 64 | blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, 65 | removalPolicy: cdk.RemovalPolicy.DESTROY, 66 | bucketName: cdk.PhysicalName.GENERATE_IF_NEEDED, 67 | eventBridgeEnabled: true, 68 | enforceSSL: true, 69 | serverAccessLogsPrefix: "accesslog/", 70 | objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_PREFERRED, 71 | intelligentTieringConfigurations: [{ 72 | name: 'libraryBucketConfig', 73 | archiveAccessTierTime: cdk.Duration.days(90), 74 | deepArchiveAccessTierTime: cdk.Duration.days(180), 75 | }], 76 | }); 77 | 78 | libraryBucket.addToResourcePolicy( 79 | new cdk.aws_iam.PolicyStatement({ 80 | sid: "EnforceTLS", 81 | effect: cdk.aws_iam.Effect.DENY, 82 | principals: [new cdk.aws_iam.AnyPrincipal()], 83 | actions: ["s3:*"], 84 | resources: [libraryBucket.bucketArn, libraryBucket.bucketArn + "/*"], 85 | conditions: { Bool: { "aws:SecureTransport": "false" } }, 86 | }) 87 | ); 88 | 89 | const originAccessIdentity = new cdk.aws_cloudfront.OriginAccessIdentity( 90 | this, 91 | "OriginAccessIdentity" 92 | ); 93 | libraryBucket.grantRead(originAccessIdentity); 94 | 95 | libraryBucket.addCorsRule({ 96 | allowedMethods: [ 97 | s3.HttpMethods.GET, 98 | s3.HttpMethods.PUT, 99 | s3.HttpMethods.POST, 100 | s3.HttpMethods.DELETE, 101 | ], 102 | allowedOrigins: ["*"], 103 | allowedHeaders: ["*"], 104 | }); 105 | 106 | // export any cf outputs 107 | new cdk.CfnOutput(this, "LibraryBucket", { 108 | value: libraryBucket.bucketName, 109 | }); 110 | 111 | // assign public properties 112 | this.originAccessIdentity = originAccessIdentity; 113 | this.s3bucket = libraryBucket; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /source/web-app/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ng_app": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-builders/custom-webpack:browser", 19 | "options": { 20 | "customWebpackConfig": { 21 | "path": "./custom-webpack.config.ts" 22 | }, 23 | "outputPath": "build", 24 | "index": "src/index.html", 25 | "main": "src/main.ts", 26 | "polyfills": "src/polyfills.ts", 27 | "tsConfig": "tsconfig.app.json", 28 | "inlineStyleLanguage": "scss", 29 | "assets": [ 30 | "src/favicon.ico", 31 | "src/assets" 32 | ], 33 | "styles": [ 34 | "node_modules/maplibre-gl/dist/maplibre-gl.css", 35 | "src/styles.scss" 36 | ], 37 | "scripts": [], 38 | "allowedCommonJsDependencies": [ 39 | "uuid", 40 | "lodash", 41 | "url", 42 | "buffer", 43 | "ulid", 44 | "cookie", 45 | "js-cookie", 46 | "isomorphic-unfetch", 47 | "crypto-js", 48 | "@aws-crypto/sha256-browser", 49 | "@aws-crypto/sha256-js", 50 | "@aws-crypto/crc32", 51 | "camelcase-keys", 52 | "paho-mqtt", 53 | "qrcode", 54 | "xstate", 55 | "style-dictionary", 56 | "fast-xml-parser", 57 | "@aws-amplify/ui", 58 | "events", 59 | "classnames", 60 | "highcharts", 61 | "maplibre-gl", 62 | "@maplibre/maplibre-gl-geocoder" 63 | ] 64 | }, 65 | "configurations": { 66 | "production": { 67 | "budgets": [ 68 | { 69 | "type": "initial", 70 | "maximumWarning": "3.5mb", 71 | "maximumError": "5mb" 72 | }, 73 | { 74 | "type": "anyComponentStyle", 75 | "maximumWarning": "15kb", 76 | "maximumError": "20kb" 77 | } 78 | ], 79 | "fileReplacements": [ 80 | { 81 | "replace": "src/environments/environment.ts", 82 | "with": "src/environments/environment.prod.ts" 83 | } 84 | ], 85 | "outputHashing": "all" 86 | }, 87 | "development": { 88 | "buildOptimizer": false, 89 | "optimization": false, 90 | "vendorChunk": true, 91 | "extractLicenses": false, 92 | "sourceMap": true, 93 | "namedChunks": true 94 | } 95 | }, 96 | "defaultConfiguration": "production" 97 | }, 98 | "serve": { 99 | "builder": "@angular-builders/custom-webpack:dev-server", 100 | "configurations": { 101 | "production": { 102 | "browserTarget": "ng_app:build:production" 103 | }, 104 | "development": { 105 | "browserTarget": "ng_app:build:development" 106 | } 107 | }, 108 | "defaultConfiguration": "development" 109 | }, 110 | "extract-i18n": { 111 | "builder": "@angular-builders/custom-webpack:extract-i18n", 112 | "options": { 113 | "browserTarget": "ng_app:build" 114 | } 115 | }, 116 | "test": { 117 | "builder": "@angular-builders/custom-webpack:karma", 118 | "options": { 119 | "main": "src/test.ts", 120 | "polyfills": "src/polyfills.ts", 121 | "tsConfig": "tsconfig.spec.json", 122 | "karmaConfig": "karma.conf.js", 123 | "inlineStyleLanguage": "scss", 124 | "assets": [ 125 | "src/favicon.ico", 126 | "src/assets" 127 | ], 128 | "styles": [ 129 | "src/styles.scss" 130 | ], 131 | "scripts": [] 132 | } 133 | } 134 | } 135 | } 136 | }, 137 | "cli": { 138 | "analytics": "da254ab0-c621-4c9f-9df9-eb5554447b6c" 139 | } 140 | } 141 | --------------------------------------------------------------------------------