From b7db1c53fc8736e170d3caa28c1be18e5f7513af Mon Sep 17 00:00:00 2001 From: ChristopherDiesch Date: Fri, 14 Mar 2025 13:37:08 -0600 Subject: [PATCH] Initial commit --- .dockerignore | 5 + CHANGELOG.md | 11 + Dockerfile | 10 + LICENSE.md | 32 + README.md | 229 + apps/MRE/__init__.py | 117 + apps/MRE/graph.png | Bin 0 -> 77395 bytes apps/__init__.py | 44 + apps/authentication/__init__.py | 12 + apps/authentication/forms.py | 31 + apps/authentication/models.py | 49 + apps/authentication/routes.py | 122 + apps/authentication/util.py | 34 + apps/config.py | 52 + apps/home/__init__.py | 12 + apps/home/forms.py | 14 + apps/home/routes.py | 79 + apps/static/assets/.gitkeep | 0 apps/static/assets/css/material-kit.css | 17413 ++++++++++++++++ apps/static/assets/css/material-kit.css.map | 1 + apps/static/assets/css/material-kit.min.css | 22 + apps/static/assets/css/nucleo-icons.css | 597 + apps/static/assets/css/nucleo-svg.css | 135 + apps/static/assets/fonts/nucleo-icons.eot | Bin 0 -> 18516 bytes apps/static/assets/fonts/nucleo-icons.svg | 312 + apps/static/assets/fonts/nucleo-icons.ttf | Bin 0 -> 18292 bytes apps/static/assets/fonts/nucleo-icons.woff | Bin 0 -> 10220 bytes apps/static/assets/fonts/nucleo-icons.woff2 | Bin 0 -> 8580 bytes apps/static/assets/fonts/nucleo.eot | Bin 0 -> 26524 bytes apps/static/assets/fonts/nucleo.ttf | Bin 0 -> 26364 bytes apps/static/assets/fonts/nucleo.woff | Bin 0 -> 15168 bytes apps/static/assets/fonts/nucleo.woff2 | Bin 0 -> 12616 bytes apps/static/assets/gulpfile.js | 59 + apps/static/assets/img/apple-icon.png | Bin 0 -> 809 bytes apps/static/assets/img/bg.jpg | Bin 0 -> 124945 bytes apps/static/assets/img/bg0.jpg | Bin 0 -> 563130 bytes apps/static/assets/img/bg10.jpg | Bin 0 -> 547482 bytes apps/static/assets/img/bg2.jpg | Bin 0 -> 279482 bytes apps/static/assets/img/bg3.jpg | Bin 0 -> 272948 bytes apps/static/assets/img/bg5.jpg | Bin 0 -> 962073 bytes apps/static/assets/img/bg9.jpg | Bin 0 -> 174691 bytes apps/static/assets/img/bruce-mars.jpg | Bin 0 -> 26785 bytes apps/static/assets/img/city-profile.jpg | Bin 0 -> 464890 bytes apps/static/assets/img/down-arrow-dark.svg | 11 + apps/static/assets/img/down-arrow-white.svg | 1 + apps/static/assets/img/down-arrow.svg | 11 + apps/static/assets/img/examples/blog-9-4.jpg | Bin 0 -> 356531 bytes apps/static/assets/img/examples/blog2.jpg | Bin 0 -> 280668 bytes .../assets/img/examples/testimonial-6-2.jpg | Bin 0 -> 113709 bytes .../assets/img/examples/testimonial-6-3.jpg | Bin 0 -> 104820 bytes apps/static/assets/img/favicon.png | Bin 0 -> 809 bytes .../img/illustrations/illustration-signin.jpg | Bin 0 -> 531130 bytes apps/static/assets/img/ivana-square.jpg | Bin 0 -> 21720 bytes apps/static/assets/img/ivana-squares.jpg | Bin 0 -> 21257 bytes apps/static/assets/img/logo-ct-dark.png | Bin 0 -> 6956 bytes .../img/logos/gray-logos/logo-apple.svg | 10 + .../img/logos/gray-logos/logo-coinbase.svg | 16 + .../logos/gray-logos/logo-digitalocean.svg | 9 + .../img/logos/gray-logos/logo-facebook.svg | 11 + .../assets/img/logos/gray-logos/logo-nasa.svg | 9 + .../img/logos/gray-logos/logo-netflix.svg | 9 + .../img/logos/gray-logos/logo-pinterest.svg | 9 + .../img/logos/gray-logos/logo-spotify.svg | 9 + .../img/logos/gray-logos/logo-vodafone.svg | 9 + apps/static/assets/img/macbook.png | Bin 0 -> 887604 bytes apps/static/assets/img/shapes/waves-white.svg | 324 + apps/static/assets/img/team-1.jpg | Bin 0 -> 27795 bytes apps/static/assets/img/team-2.jpg | Bin 0 -> 23880 bytes apps/static/assets/img/team-3.jpg | Bin 0 -> 21970 bytes apps/static/assets/img/team-4.jpg | Bin 0 -> 14489 bytes apps/static/assets/img/team-5.jpg | Bin 0 -> 24747 bytes .../assets/js/core/bootstrap.bundle.min.js | 6 + apps/static/assets/js/core/bootstrap.min.js | 6 + apps/static/assets/js/core/popper.min.js | 5 + apps/static/assets/js/material-kit.js | 264 + apps/static/assets/js/material-kit.js.map | 1 + apps/static/assets/js/material-kit.min.js | 2 + apps/static/assets/js/plugins/choices.min.js | 11 + apps/static/assets/js/plugins/countup.min.js | 1 + .../static/assets/js/plugins/flatpickr.min.js | 2 + .../static/assets/js/plugins/highlight.min.js | 1358 ++ apps/static/assets/js/plugins/moment.min.js | 7 + apps/static/assets/js/plugins/parallax.min.js | 54 + .../js/plugins/perfect-scrollbar.min.js | 19 + apps/static/assets/js/plugins/prism.min.js | 7 + apps/static/assets/js/plugins/rellax.min.js | 24 + apps/static/assets/js/plugins/tilt.min.js | 508 + apps/static/assets/js/plugins/typedjs.js | 381 + apps/static/assets/package.json | 51 + apps/static/assets/scss/material-kit.scss | 30 + .../assets/scss/material-kit/_accordion.scss | 28 + .../assets/scss/material-kit/_alert.scss | 17 + .../assets/scss/material-kit/_avatars.scss | 123 + .../scss/material-kit/_backgrounds.scss | 19 + .../assets/scss/material-kit/_badge.scss | 8 + .../scss/material-kit/_breadcrumbs.scss | 48 + .../assets/scss/material-kit/_buttons.scss | 190 + .../scss/material-kit/_cards-extend.scss | 4 + .../assets/scss/material-kit/_cards.scss | 60 + .../assets/scss/material-kit/_components.scss | 5 + .../scss/material-kit/_dark-version.scss | 216 + .../scss/material-kit/_dropdown-extend.scss | 31 + .../assets/scss/material-kit/_dropdown.scss | 292 + .../assets/scss/material-kit/_dropup.scss | 41 + .../scss/material-kit/_fixed-plugin.scss | 60 + .../scss/material-kit/_floating-elements.scss | 73 + .../assets/scss/material-kit/_footer.scss | 17 + .../assets/scss/material-kit/_forms.scss | 49 + .../assets/scss/material-kit/_gradients.scss | 18 + .../assets/scss/material-kit/_header.scss | 25 + .../assets/scss/material-kit/_icons.scss | 32 + .../assets/scss/material-kit/_info-areas.scss | 178 + .../assets/scss/material-kit/_list-check.scss | 25 + .../scss/material-kit/_misc-extend.scss | 244 + .../assets/scss/material-kit/_misc.scss | 365 + .../static/assets/scss/material-kit/_nav.scss | 123 + .../scss/material-kit/_navbar-vertical.scss | 693 + .../assets/scss/material-kit/_navbar.scss | 220 + .../assets/scss/material-kit/_pagination.scss | 58 + .../assets/scss/material-kit/_popovers.scss | 10 + .../assets/scss/material-kit/_progress.scss | 15 + .../assets/scss/material-kit/_ripple.scss | 15 + .../assets/scss/material-kit/_rtl-extend.scss | 63 + .../static/assets/scss/material-kit/_rtl.scss | 108 + .../scss/material-kit/_social-buttons.scss | 42 + .../assets/scss/material-kit/_tables.scss | 49 + .../assets/scss/material-kit/_tilt.scss | 11 + .../assets/scss/material-kit/_timeline.scss | 137 + .../assets/scss/material-kit/_tooltips.scss | 9 + .../assets/scss/material-kit/_typography.scss | 301 + .../scss/material-kit/_utilities-extend.scss | 11 + .../assets/scss/material-kit/_utilities.scss | 832 + .../assets/scss/material-kit/_variables.scss | 1672 ++ .../material-kit/badges/_badge-circle.scss | 29 + .../scss/material-kit/badges/_badge-dot.scss | 42 + .../material-kit/badges/_badge-floating.scss | 17 + .../scss/material-kit/badges/_badge.scss | 93 + .../material-kit/bootstrap/_accordion.scss | 118 + .../scss/material-kit/bootstrap/_alert.scss | 57 + .../scss/material-kit/bootstrap/_badge.scss | 29 + .../material-kit/bootstrap/_breadcrumb.scss | 28 + .../material-kit/bootstrap/_button-group.scss | 139 + .../scss/material-kit/bootstrap/_buttons.scss | 111 + .../scss/material-kit/bootstrap/_card.scss | 216 + .../material-kit/bootstrap/_carousel.scss | 229 + .../scss/material-kit/bootstrap/_close.scss | 40 + .../material-kit/bootstrap/_containers.scss | 41 + .../material-kit/bootstrap/_dropdown.scss | 240 + .../scss/material-kit/bootstrap/_forms.scss | 9 + .../material-kit/bootstrap/_functions.scss | 302 + .../scss/material-kit/bootstrap/_grid.scss | 33 + .../scss/material-kit/bootstrap/_helpers.scss | 9 + .../scss/material-kit/bootstrap/_images.scss | 42 + .../material-kit/bootstrap/_list-group.scss | 174 + .../scss/material-kit/bootstrap/_mixins.scss | 43 + .../scss/material-kit/bootstrap/_modal.scss | 209 + .../scss/material-kit/bootstrap/_nav.scss | 139 + .../scss/material-kit/bootstrap/_navbar.scss | 335 + .../material-kit/bootstrap/_offcanvas.scss | 83 + .../material-kit/bootstrap/_pagination.scss | 64 + .../material-kit/bootstrap/_placeholders.scss | 51 + .../scss/material-kit/bootstrap/_popover.scss | 158 + .../material-kit/bootstrap/_progress.scss | 48 + .../scss/material-kit/bootstrap/_reboot.scss | 625 + .../scss/material-kit/bootstrap/_root.scss | 54 + .../material-kit/bootstrap/_spinners.scss | 69 + .../scss/material-kit/bootstrap/_tables.scss | 151 + .../scss/material-kit/bootstrap/_toasts.scss | 51 + .../scss/material-kit/bootstrap/_tooltip.scss | 115 + .../material-kit/bootstrap/_transitions.scss | 27 + .../scss/material-kit/bootstrap/_type.scss | 104 + .../material-kit/bootstrap/_utilities.scss | 630 + .../material-kit/bootstrap/_variables.scss | 1639 ++ .../bootstrap/bootstrap-grid.scss | 67 + .../bootstrap/bootstrap-reboot.scss | 13 + .../bootstrap/bootstrap-utilities.scss | 18 + .../material-kit/bootstrap/bootstrap.scss | 53 + .../bootstrap/forms/_floating-labels.scss | 63 + .../bootstrap/forms/_form-check.scss | 152 + .../bootstrap/forms/_form-control.scss | 219 + .../bootstrap/forms/_form-range.scss | 91 + .../bootstrap/forms/_form-select.scss | 70 + .../bootstrap/forms/_form-text.scss | 11 + .../bootstrap/forms/_input-group.scss | 121 + .../material-kit/bootstrap/forms/_labels.scss | 36 + .../bootstrap/forms/_validation.scss | 12 + .../bootstrap/helpers/_clearfix.scss | 3 + .../bootstrap/helpers/_colored-links.scss | 12 + .../bootstrap/helpers/_position.scss | 30 + .../bootstrap/helpers/_ratio.scss | 26 + .../bootstrap/helpers/_stacks.scss | 15 + .../bootstrap/helpers/_stretched-link.scss | 15 + .../bootstrap/helpers/_text-truncation.scss | 7 + .../bootstrap/helpers/_visually-hidden.scss | 8 + .../material-kit/bootstrap/helpers/_vr.scss | 8 + .../material-kit/bootstrap/mixins/_alert.scss | 11 + .../bootstrap/mixins/_backdrop.scss | 14 + .../bootstrap/mixins/_border-radius.scss | 78 + .../bootstrap/mixins/_box-shadow.scss | 18 + .../bootstrap/mixins/_breakpoints.scss | 127 + .../bootstrap/mixins/_buttons.scss | 133 + .../material-kit/bootstrap/mixins/_caret.scss | 64 + .../bootstrap/mixins/_clearfix.scss | 9 + .../bootstrap/mixins/_color-scheme.scss | 7 + .../bootstrap/mixins/_container.scss | 9 + .../bootstrap/mixins/_deprecate.scss | 10 + .../material-kit/bootstrap/mixins/_forms.scss | 144 + .../bootstrap/mixins/_gradients.scss | 47 + .../material-kit/bootstrap/mixins/_grid.scss | 150 + .../material-kit/bootstrap/mixins/_image.scss | 16 + .../bootstrap/mixins/_list-group.scss | 24 + .../material-kit/bootstrap/mixins/_lists.scss | 7 + .../bootstrap/mixins/_pagination.scss | 31 + .../bootstrap/mixins/_reset-text.scss | 17 + .../bootstrap/mixins/_resize.scss | 6 + .../bootstrap/mixins/_table-variants.scss | 21 + .../bootstrap/mixins/_text-truncate.scss | 8 + .../bootstrap/mixins/_transition.scss | 26 + .../bootstrap/mixins/_utilities.scss | 89 + .../bootstrap/mixins/_visually-hidden.scss | 29 + .../bootstrap/utilities/_api.scss | 47 + .../material-kit/bootstrap/vendor/_rfs.scss | 354 + .../material-kit/cards/card-background.scss | 81 + .../scss/material-kit/cards/card-blog.scss | 17 + .../material-kit/cards/card-horizontal.scss | 24 + .../scss/material-kit/cards/card-pricing.scss | 48 + .../scss/material-kit/cards/card-profile.scss | 59 + .../scss/material-kit/cards/card-rotate.scss | 148 + .../scss/material-kit/custom/_styles.scss | 0 .../scss/material-kit/custom/_variables.scss | 0 .../scss/material-kit/forms/_form-check.scss | 91 + .../scss/material-kit/forms/_form-select.scss | 3 + .../scss/material-kit/forms/_form-switch.scss | 43 + .../scss/material-kit/forms/_forms.scss | 6 + .../scss/material-kit/forms/_input-group.scss | 312 + .../scss/material-kit/forms/_inputs.scss | 47 + .../scss/material-kit/forms/_labels.scss | 21 + .../scss/material-kit/mixins/_badge.scss | 12 + .../scss/material-kit/mixins/_buttons.scss | 12 + .../material-kit/mixins/_colored-shadows.scss | 5 + .../scss/material-kit/mixins/_hover.scss | 27 + .../material-kit/mixins/_social-buttons.scss | 46 + .../scss/material-kit/mixins/_vendor.scss | 51 + .../scss/material-kit/mixins/mixins.scss | 6 + .../material-kit/plugins/free/_flatpickr.scss | 878 + .../plugins/free/_nouislider.scss | 298 + .../plugins/free/_perfect-scrollbar.scss | 116 + .../material-kit/plugins/free/_prism.scss | 142 + .../material-kit/plugins/free/plugins.scss | 4 + .../plugins/pro/_carousel-slick.scss | 102 + .../material-kit/plugins/pro/_choices.scss | 455 + .../plugins/pro/_datatable-extend.scss | 126 + .../material-kit/plugins/pro/_datatable.scss | 179 + .../material-kit/plugins/pro/_dragula.scss | 22 + .../material-kit/plugins/pro/_dropzone.scss | 396 + .../plugins/pro/_fullcalendar-extend.scss | 96 + .../plugins/pro/_fullcalendar.scss | 1434 ++ .../material-kit/plugins/pro/_glidejs.scss | 179 + .../material-kit/plugins/pro/_highlight.scss | 83 + .../material-kit/plugins/pro/_kanban.scss | 158 + .../material-kit/plugins/pro/_leaflet.scss | 656 + .../material-kit/plugins/pro/_list-check.scss | 17 + .../material-kit/plugins/pro/_photoswipe.scss | 182 + .../scss/material-kit/plugins/pro/_quill.scss | 949 + .../plugins/pro/_rating-widget.scss | 36 + .../plugins/pro/_sweetalert2-extend.scss | 70 + .../plugins/pro/_sweetalert2.scss | 1322 ++ .../material-kit/plugins/pro/_vector-map.scss | 131 + .../material-kit/plugins/pro/multi-step.scss | 188 + .../plugins/pro/plugins-extend.scss | 22 + .../assets/scss/material-kit/theme-pro.scss | 37 + .../assets/scss/material-kit/theme.scss | 63 + .../material-kit/variables/_animations.scss | 45 + .../scss/material-kit/variables/_avatars.scss | 28 + .../scss/material-kit/variables/_badge.scss | 40 + .../material-kit/variables/_breadcrumb.scss | 5 + .../material-kit/variables/_cards-extend.scss | 28 + .../scss/material-kit/variables/_cards.scss | 65 + .../scss/material-kit/variables/_choices.scss | 8 + .../material-kit/variables/_dark-version.scss | 9 + .../material-kit/variables/_dropdowns.scss | 53 + .../material-kit/variables/_fixed-plugin.scss | 7 + .../material-kit/variables/_form-switch.scss | 3 + .../variables/_full-calendar.scss | 6 + .../scss/material-kit/variables/_header.scss | 24 + .../material-kit/variables/_info-areas.scss | 32 + .../material-kit/variables/_misc-extend.scss | 48 + .../scss/material-kit/variables/_misc.scss | 59 + .../variables/_navbar-vertical.scss | 65 + .../scss/material-kit/variables/_navbar.scss | 17 + .../material-kit/variables/_pagination.scss | 19 + .../scss/material-kit/variables/_ripple.scss | 32 + .../scss/material-kit/variables/_rtl.scss | 1 + .../variables/_social-buttons.scss | 28 + .../scss/material-kit/variables/_table.scss | 19 + .../material-kit/variables/_timeline.scss | 20 + .../variables/_utilities-extend.scss | 32 + .../material-kit/variables/_utilities.scss | 191 + .../variables/_virtual-reality.scss | 5 + apps/static/favicon.ico | Bin 0 -> 32038 bytes apps/static/sitemap.xml | 16 + apps/templates/.gitkeep | 0 apps/templates/accounts/login.html | 73 + apps/templates/accounts/register.html | 91 + apps/templates/home/about-us.html | 359 + apps/templates/home/author.html | 257 + apps/templates/home/contact-us.html | 73 + apps/templates/home/index.html | 1344 ++ apps/templates/home/mre.html | 104 + apps/templates/home/page-403.html | 52 + apps/templates/home/page-404.html | 52 + apps/templates/home/page-500.html | 52 + apps/templates/home/sign-in.html | 77 + apps/templates/home/sign-up.html | 82 + apps/templates/home/template.html | 38 + apps/templates/home/ui-catchers-alerts.html | 222 + apps/templates/home/ui-catchers-modals.html | 249 + .../home/ui-catchers-tooltips-popovers.html | 255 + apps/templates/home/ui-elements-avatars.html | 252 + apps/templates/home/ui-elements-badges.html | 296 + .../home/ui-elements-breadcrumbs.html | 156 + apps/templates/home/ui-elements-buttons.html | 636 + .../templates/home/ui-elements-dropdowns.html | 212 + .../home/ui-elements-progress-bars.html | 226 + apps/templates/home/ui-elements-toggles.html | 197 + .../home/ui-elements-typography.html | 668 + apps/templates/home/ui-input-forms.html | 248 + apps/templates/home/ui-input-inputs.html | 542 + .../home/ui-navigation-nav-tabs.html | 212 + .../templates/home/ui-navigation-navbars.html | 281 + .../home/ui-navigation-pagination.html | 220 + apps/templates/home/ui-sections-features.html | 452 + .../home/ui-sections-hero-sections.html | 416 + apps/templates/includes/footer-auth.html | 26 + .../templates/includes/footer-components.html | 53 + apps/templates/includes/footer.html | 152 + apps/templates/includes/navigation-light.html | 366 + .../includes/navigation-transparent.html | 373 + apps/templates/includes/navigation.html | 367 + apps/templates/includes/scripts.html | 23 + apps/templates/layouts/base-fullscreen.html | 53 + apps/templates/layouts/base.html | 55 + docker-compose.yml | 28 + env.sample | 15 + graph.png | Bin 0 -> 74258 bytes gunicorn-cfg.py | 11 + log.json | 22 + nginx/appseed-app.conf | 15 + package.json | 20 + requirements.txt | 17 + run.py | 43 + scripts/install-multipoly-fit.sh | 5 + 352 files changed, 58586 insertions(+) create mode 100644 .dockerignore create mode 100644 CHANGELOG.md create mode 100644 Dockerfile create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 apps/MRE/__init__.py create mode 100644 apps/MRE/graph.png create mode 100644 apps/__init__.py create mode 100644 apps/authentication/__init__.py create mode 100644 apps/authentication/forms.py create mode 100644 apps/authentication/models.py create mode 100644 apps/authentication/routes.py create mode 100644 apps/authentication/util.py create mode 100644 apps/config.py create mode 100644 apps/home/__init__.py create mode 100644 apps/home/forms.py create mode 100644 apps/home/routes.py create mode 100644 apps/static/assets/.gitkeep create mode 100644 apps/static/assets/css/material-kit.css create mode 100644 apps/static/assets/css/material-kit.css.map create mode 100644 apps/static/assets/css/material-kit.min.css create mode 100644 apps/static/assets/css/nucleo-icons.css create mode 100644 apps/static/assets/css/nucleo-svg.css create mode 100644 apps/static/assets/fonts/nucleo-icons.eot create mode 100644 apps/static/assets/fonts/nucleo-icons.svg create mode 100644 apps/static/assets/fonts/nucleo-icons.ttf create mode 100644 apps/static/assets/fonts/nucleo-icons.woff create mode 100644 apps/static/assets/fonts/nucleo-icons.woff2 create mode 100644 apps/static/assets/fonts/nucleo.eot create mode 100644 apps/static/assets/fonts/nucleo.ttf create mode 100644 apps/static/assets/fonts/nucleo.woff create mode 100644 apps/static/assets/fonts/nucleo.woff2 create mode 100644 apps/static/assets/gulpfile.js create mode 100644 apps/static/assets/img/apple-icon.png create mode 100644 apps/static/assets/img/bg.jpg create mode 100644 apps/static/assets/img/bg0.jpg create mode 100644 apps/static/assets/img/bg10.jpg create mode 100644 apps/static/assets/img/bg2.jpg create mode 100644 apps/static/assets/img/bg3.jpg create mode 100644 apps/static/assets/img/bg5.jpg create mode 100644 apps/static/assets/img/bg9.jpg create mode 100644 apps/static/assets/img/bruce-mars.jpg create mode 100644 apps/static/assets/img/city-profile.jpg create mode 100644 apps/static/assets/img/down-arrow-dark.svg create mode 100644 apps/static/assets/img/down-arrow-white.svg create mode 100644 apps/static/assets/img/down-arrow.svg create mode 100644 apps/static/assets/img/examples/blog-9-4.jpg create mode 100644 apps/static/assets/img/examples/blog2.jpg create mode 100644 apps/static/assets/img/examples/testimonial-6-2.jpg create mode 100644 apps/static/assets/img/examples/testimonial-6-3.jpg create mode 100644 apps/static/assets/img/favicon.png create mode 100644 apps/static/assets/img/illustrations/illustration-signin.jpg create mode 100644 apps/static/assets/img/ivana-square.jpg create mode 100644 apps/static/assets/img/ivana-squares.jpg create mode 100644 apps/static/assets/img/logo-ct-dark.png create mode 100644 apps/static/assets/img/logos/gray-logos/logo-apple.svg create mode 100644 apps/static/assets/img/logos/gray-logos/logo-coinbase.svg create mode 100644 apps/static/assets/img/logos/gray-logos/logo-digitalocean.svg create mode 100644 apps/static/assets/img/logos/gray-logos/logo-facebook.svg create mode 100644 apps/static/assets/img/logos/gray-logos/logo-nasa.svg create mode 100644 apps/static/assets/img/logos/gray-logos/logo-netflix.svg create mode 100644 apps/static/assets/img/logos/gray-logos/logo-pinterest.svg create mode 100644 apps/static/assets/img/logos/gray-logos/logo-spotify.svg create mode 100644 apps/static/assets/img/logos/gray-logos/logo-vodafone.svg create mode 100644 apps/static/assets/img/macbook.png create mode 100644 apps/static/assets/img/shapes/waves-white.svg create mode 100644 apps/static/assets/img/team-1.jpg create mode 100644 apps/static/assets/img/team-2.jpg create mode 100644 apps/static/assets/img/team-3.jpg create mode 100644 apps/static/assets/img/team-4.jpg create mode 100644 apps/static/assets/img/team-5.jpg create mode 100644 apps/static/assets/js/core/bootstrap.bundle.min.js create mode 100644 apps/static/assets/js/core/bootstrap.min.js create mode 100644 apps/static/assets/js/core/popper.min.js create mode 100644 apps/static/assets/js/material-kit.js create mode 100644 apps/static/assets/js/material-kit.js.map create mode 100644 apps/static/assets/js/material-kit.min.js create mode 100644 apps/static/assets/js/plugins/choices.min.js create mode 100644 apps/static/assets/js/plugins/countup.min.js create mode 100644 apps/static/assets/js/plugins/flatpickr.min.js create mode 100644 apps/static/assets/js/plugins/highlight.min.js create mode 100644 apps/static/assets/js/plugins/moment.min.js create mode 100644 apps/static/assets/js/plugins/parallax.min.js create mode 100644 apps/static/assets/js/plugins/perfect-scrollbar.min.js create mode 100644 apps/static/assets/js/plugins/prism.min.js create mode 100644 apps/static/assets/js/plugins/rellax.min.js create mode 100644 apps/static/assets/js/plugins/tilt.min.js create mode 100644 apps/static/assets/js/plugins/typedjs.js create mode 100644 apps/static/assets/package.json create mode 100644 apps/static/assets/scss/material-kit.scss create mode 100644 apps/static/assets/scss/material-kit/_accordion.scss create mode 100644 apps/static/assets/scss/material-kit/_alert.scss create mode 100644 apps/static/assets/scss/material-kit/_avatars.scss create mode 100644 apps/static/assets/scss/material-kit/_backgrounds.scss create mode 100644 apps/static/assets/scss/material-kit/_badge.scss create mode 100644 apps/static/assets/scss/material-kit/_breadcrumbs.scss create mode 100644 apps/static/assets/scss/material-kit/_buttons.scss create mode 100644 apps/static/assets/scss/material-kit/_cards-extend.scss create mode 100644 apps/static/assets/scss/material-kit/_cards.scss create mode 100644 apps/static/assets/scss/material-kit/_components.scss create mode 100644 apps/static/assets/scss/material-kit/_dark-version.scss create mode 100644 apps/static/assets/scss/material-kit/_dropdown-extend.scss create mode 100644 apps/static/assets/scss/material-kit/_dropdown.scss create mode 100644 apps/static/assets/scss/material-kit/_dropup.scss create mode 100644 apps/static/assets/scss/material-kit/_fixed-plugin.scss create mode 100644 apps/static/assets/scss/material-kit/_floating-elements.scss create mode 100644 apps/static/assets/scss/material-kit/_footer.scss create mode 100644 apps/static/assets/scss/material-kit/_forms.scss create mode 100644 apps/static/assets/scss/material-kit/_gradients.scss create mode 100644 apps/static/assets/scss/material-kit/_header.scss create mode 100644 apps/static/assets/scss/material-kit/_icons.scss create mode 100644 apps/static/assets/scss/material-kit/_info-areas.scss create mode 100644 apps/static/assets/scss/material-kit/_list-check.scss create mode 100644 apps/static/assets/scss/material-kit/_misc-extend.scss create mode 100644 apps/static/assets/scss/material-kit/_misc.scss create mode 100644 apps/static/assets/scss/material-kit/_nav.scss create mode 100644 apps/static/assets/scss/material-kit/_navbar-vertical.scss create mode 100644 apps/static/assets/scss/material-kit/_navbar.scss create mode 100644 apps/static/assets/scss/material-kit/_pagination.scss create mode 100644 apps/static/assets/scss/material-kit/_popovers.scss create mode 100644 apps/static/assets/scss/material-kit/_progress.scss create mode 100644 apps/static/assets/scss/material-kit/_ripple.scss create mode 100644 apps/static/assets/scss/material-kit/_rtl-extend.scss create mode 100644 apps/static/assets/scss/material-kit/_rtl.scss create mode 100644 apps/static/assets/scss/material-kit/_social-buttons.scss create mode 100644 apps/static/assets/scss/material-kit/_tables.scss create mode 100644 apps/static/assets/scss/material-kit/_tilt.scss create mode 100644 apps/static/assets/scss/material-kit/_timeline.scss create mode 100644 apps/static/assets/scss/material-kit/_tooltips.scss create mode 100644 apps/static/assets/scss/material-kit/_typography.scss create mode 100644 apps/static/assets/scss/material-kit/_utilities-extend.scss create mode 100644 apps/static/assets/scss/material-kit/_utilities.scss create mode 100644 apps/static/assets/scss/material-kit/_variables.scss create mode 100644 apps/static/assets/scss/material-kit/badges/_badge-circle.scss create mode 100644 apps/static/assets/scss/material-kit/badges/_badge-dot.scss create mode 100644 apps/static/assets/scss/material-kit/badges/_badge-floating.scss create mode 100644 apps/static/assets/scss/material-kit/badges/_badge.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_accordion.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_alert.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_badge.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_breadcrumb.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_button-group.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_buttons.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_card.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_carousel.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_close.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_containers.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_dropdown.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_forms.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_functions.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_grid.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_helpers.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_images.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_list-group.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_mixins.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_modal.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_nav.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_navbar.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_offcanvas.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_pagination.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_placeholders.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_popover.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_progress.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_reboot.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_root.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_spinners.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_tables.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_toasts.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_tooltip.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_transitions.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_type.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_utilities.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/_variables.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/bootstrap-grid.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/bootstrap-reboot.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/bootstrap-utilities.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/bootstrap.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/forms/_floating-labels.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/forms/_form-check.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/forms/_form-control.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/forms/_form-range.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/forms/_form-select.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/forms/_form-text.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/forms/_input-group.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/forms/_labels.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/forms/_validation.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/helpers/_clearfix.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/helpers/_colored-links.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/helpers/_position.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/helpers/_ratio.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/helpers/_stacks.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/helpers/_stretched-link.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/helpers/_text-truncation.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/helpers/_visually-hidden.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/helpers/_vr.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_alert.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_backdrop.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_border-radius.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_box-shadow.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_breakpoints.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_buttons.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_caret.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_clearfix.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_color-scheme.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_container.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_deprecate.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_forms.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_gradients.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_grid.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_image.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_list-group.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_lists.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_pagination.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_reset-text.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_resize.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_table-variants.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_text-truncate.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_transition.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_utilities.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/mixins/_visually-hidden.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/utilities/_api.scss create mode 100644 apps/static/assets/scss/material-kit/bootstrap/vendor/_rfs.scss create mode 100644 apps/static/assets/scss/material-kit/cards/card-background.scss create mode 100644 apps/static/assets/scss/material-kit/cards/card-blog.scss create mode 100644 apps/static/assets/scss/material-kit/cards/card-horizontal.scss create mode 100644 apps/static/assets/scss/material-kit/cards/card-pricing.scss create mode 100644 apps/static/assets/scss/material-kit/cards/card-profile.scss create mode 100644 apps/static/assets/scss/material-kit/cards/card-rotate.scss create mode 100644 apps/static/assets/scss/material-kit/custom/_styles.scss create mode 100644 apps/static/assets/scss/material-kit/custom/_variables.scss create mode 100644 apps/static/assets/scss/material-kit/forms/_form-check.scss create mode 100644 apps/static/assets/scss/material-kit/forms/_form-select.scss create mode 100644 apps/static/assets/scss/material-kit/forms/_form-switch.scss create mode 100644 apps/static/assets/scss/material-kit/forms/_forms.scss create mode 100644 apps/static/assets/scss/material-kit/forms/_input-group.scss create mode 100644 apps/static/assets/scss/material-kit/forms/_inputs.scss create mode 100644 apps/static/assets/scss/material-kit/forms/_labels.scss create mode 100644 apps/static/assets/scss/material-kit/mixins/_badge.scss create mode 100644 apps/static/assets/scss/material-kit/mixins/_buttons.scss create mode 100644 apps/static/assets/scss/material-kit/mixins/_colored-shadows.scss create mode 100644 apps/static/assets/scss/material-kit/mixins/_hover.scss create mode 100644 apps/static/assets/scss/material-kit/mixins/_social-buttons.scss create mode 100644 apps/static/assets/scss/material-kit/mixins/_vendor.scss create mode 100644 apps/static/assets/scss/material-kit/mixins/mixins.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/free/_flatpickr.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/free/_nouislider.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/free/_perfect-scrollbar.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/free/_prism.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/free/plugins.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_carousel-slick.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_choices.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_datatable-extend.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_datatable.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_dragula.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_dropzone.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_fullcalendar-extend.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_fullcalendar.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_glidejs.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_highlight.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_kanban.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_leaflet.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_list-check.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_photoswipe.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_quill.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_rating-widget.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_sweetalert2-extend.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_sweetalert2.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/_vector-map.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/multi-step.scss create mode 100644 apps/static/assets/scss/material-kit/plugins/pro/plugins-extend.scss create mode 100644 apps/static/assets/scss/material-kit/theme-pro.scss create mode 100644 apps/static/assets/scss/material-kit/theme.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_animations.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_avatars.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_badge.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_breadcrumb.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_cards-extend.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_cards.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_choices.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_dark-version.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_dropdowns.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_fixed-plugin.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_form-switch.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_full-calendar.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_header.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_info-areas.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_misc-extend.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_misc.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_navbar-vertical.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_navbar.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_pagination.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_ripple.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_rtl.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_social-buttons.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_table.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_timeline.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_utilities-extend.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_utilities.scss create mode 100644 apps/static/assets/scss/material-kit/variables/_virtual-reality.scss create mode 100644 apps/static/favicon.ico create mode 100644 apps/static/sitemap.xml create mode 100644 apps/templates/.gitkeep create mode 100644 apps/templates/accounts/login.html create mode 100644 apps/templates/accounts/register.html create mode 100644 apps/templates/home/about-us.html create mode 100644 apps/templates/home/author.html create mode 100644 apps/templates/home/contact-us.html create mode 100644 apps/templates/home/index.html create mode 100644 apps/templates/home/mre.html create mode 100644 apps/templates/home/page-403.html create mode 100644 apps/templates/home/page-404.html create mode 100644 apps/templates/home/page-500.html create mode 100644 apps/templates/home/sign-in.html create mode 100644 apps/templates/home/sign-up.html create mode 100644 apps/templates/home/template.html create mode 100644 apps/templates/home/ui-catchers-alerts.html create mode 100644 apps/templates/home/ui-catchers-modals.html create mode 100644 apps/templates/home/ui-catchers-tooltips-popovers.html create mode 100644 apps/templates/home/ui-elements-avatars.html create mode 100644 apps/templates/home/ui-elements-badges.html create mode 100644 apps/templates/home/ui-elements-breadcrumbs.html create mode 100644 apps/templates/home/ui-elements-buttons.html create mode 100644 apps/templates/home/ui-elements-dropdowns.html create mode 100644 apps/templates/home/ui-elements-progress-bars.html create mode 100644 apps/templates/home/ui-elements-toggles.html create mode 100644 apps/templates/home/ui-elements-typography.html create mode 100644 apps/templates/home/ui-input-forms.html create mode 100644 apps/templates/home/ui-input-inputs.html create mode 100644 apps/templates/home/ui-navigation-nav-tabs.html create mode 100644 apps/templates/home/ui-navigation-navbars.html create mode 100644 apps/templates/home/ui-navigation-pagination.html create mode 100644 apps/templates/home/ui-sections-features.html create mode 100644 apps/templates/home/ui-sections-hero-sections.html create mode 100644 apps/templates/includes/footer-auth.html create mode 100644 apps/templates/includes/footer-components.html create mode 100644 apps/templates/includes/footer.html create mode 100644 apps/templates/includes/navigation-light.html create mode 100644 apps/templates/includes/navigation-transparent.html create mode 100644 apps/templates/includes/navigation.html create mode 100644 apps/templates/includes/scripts.html create mode 100644 apps/templates/layouts/base-fullscreen.html create mode 100644 apps/templates/layouts/base.html create mode 100644 docker-compose.yml create mode 100644 env.sample create mode 100644 graph.png create mode 100644 gunicorn-cfg.py create mode 100644 log.json create mode 100644 nginx/appseed-app.conf create mode 100644 package.json create mode 100644 requirements.txt create mode 100644 run.py create mode 100644 scripts/install-multipoly-fit.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..72e294c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.git +__pycache__ +*.pyc +*.pyo +*.pyd \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..20299ee --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Change Log + +## CodeBase version: 2stable.0.1 / 2022-01-15 +### Improvements + +- Dependencies update (all packages) + - Flask==2.0.2 (latest stable version) + - flask_wtf==1.0.0 + - jinja2==3.0.3 + - flask-restx==0.5.1 + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..33221e0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.9 + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +COPY . . + +# gunicorn +CMD [".", "venv/bin/activate", ";" , "gunicorn", "--config", "gunicorn-cfg.py", "run:app"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..5012dd9 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,32 @@ +# MIT License + +Copyright (c) 2019 - present [AppSeed](http://appseed.us/) + +
+ +## Licensing Information + +
+ +| Item | - | +| ---------------------------------- | --- | +| License Type | MIT | +| Use for print | **YES** | +| Create single personal website/app | **YES** | +| Create single website/app for client | **YES** | +| Create multiple website/apps for clients | **YES** | +| Create multiple SaaS applications | **YES** | +| End-product paying users | **YES** | +| Product sale | **YES** | +| Remove footer credits | **YES** | +| --- | --- | +| Remove copyright mentions from source code | NO | +| Production deployment assistance | NO | +| Create HTML/CSS template for sale | NO | +| Create Theme/Template for CMS for sale | NO | +| Separate sale of our UI Elements | NO | + +
+ +--- +For more information regarding licensing, please contact the AppSeed Service < *support@appseed.us* > diff --git a/README.md b/README.md new file mode 100644 index 0000000..9f00e35 --- /dev/null +++ b/README.md @@ -0,0 +1,229 @@ +# [Material Kit](https://appseed.us/generator/material-kit/) Flask + +`Open-Source` seed project generated by AppSeed in **Flask** Framework on top of **[Material Kit](https://appseed.us/generator/material-kit/)** design. Designed for those who like bold elements and beautiful websites, **Material Kit 2** is ready to help you create stunning websites and web apps. `Material Kit 2` is built with over 60 frontend individual elements, like buttons, inputs, navbars, nav tabs, cards, or alerts, giving you the freedom of choosing and combining. + +- 👉 [Flask Material Kit](https://appseed.us/product/material-kit/flask/) - product page +- 👉 [Complete documentation](https://docs.appseed.us/products/flask-apps/material-kit) - `Learn how to use and update the product` +- ✅ [PRO Version Available](#pro-version) - `enhanced UI` and more `features` + +
+ +> Built with [Material Kit Generator](https://appseed.us/generator/material-kit/) + +- Timestamp: `2022-06-23 20:44` +- Build ID: `90d2d084-8a7e-457c-b9ea-045f590accfd` +- **Free [Support](https://appseed.us/support/)** (registered users) via `Email` and `Discord` + +
+ +> Features + +- `Up-to-date dependencies` +- Database: `sqlite` +- `DB Tools`: SQLAlchemy ORM, Flask-Migrate (schema migrations) +- Session-Based authentication (via **flask_login**), Forms validation + +
+ +![Material Kit - Starter generated by AppSeed.](https://user-images.githubusercontent.com/51070104/167396765-c88b7a95-155f-4236-8691-7b80fa2d9cd9.png) + +
+ + +## ✨ Start the app in Docker + +> **Step 1** - Download the code from the GH repository (using `GIT`) + +```bash +$ # Get the code +$ git clone https://github.com/appseed-projects/.git +$ cd +``` + +
+ +> **Step 2** - Edit `.env` and set `DEBUG=True`. This will activate the `SQLite` persistance. + +```txt +DEBUG=True +``` + +
+ +> **Step 3** - Start the APP in `Docker` + +```bash +$ docker-compose up --build +``` + +Visit `http://localhost:5085` in your browser. The app should be up & running. + +
+ + + + +## ✨ How to use it + +> Download the code + +```bash +$ # Get the code +$ git clone https://github.com/appseed-projects/90d2d084-8a7e-457c-b9ea-045f590accfd.git +$ cd 90d2d084-8a7e-457c-b9ea-045f590accfd +``` + +
+ +### 👉 Set Up for `Unix`, `MacOS` + +> Install modules via `VENV` + +```bash +$ virtualenv env +$ source env/bin/activate +$ pip3 install -r requirements.txt +``` + +
+ +> Set Up Flask Environment + +```bash +$ export FLASK_APP=run.py +$ export FLASK_ENV=development +``` + +
+ +> Start the app + +```bash +$ flask run +``` + +At this point, the app runs at `http://127.0.0.1:5000/`. + +
+ +### 👉 Set Up for `Windows` + +> Install modules via `VENV` (windows) + +``` +$ virtualenv env +$ .\env\Scripts\activate +$ pip3 install -r requirements.txt +``` + +
+ +> Set Up Flask Environment + +```bash +$ # CMD +$ set FLASK_APP=run.py +$ set FLASK_ENV=development +$ +$ # Powershell +$ $env:FLASK_APP = ".\run.py" +$ $env:FLASK_ENV = "development" +``` + +
+ +> Start the app + +```bash +$ flask run +``` + +At this point, the app runs at `http://127.0.0.1:5000/`. + +
+ +### 👉 Create Users + +By default, the app redirects guest users to authenticate. In order to access the private pages, follow this set up: + +- Start the app via `flask run` +- Access the `registration` page and create a new user: + - `http://127.0.0.1:5000/register` +- Access the `sign in` page and authenticate + - `http://127.0.0.1:5000/login` + +
+ +## ✨ Code-base structure + +The project is coded using blueprints, app factory pattern, dual configuration profile (development and production) and an intuitive structure presented bellow: + +```bash +< PROJECT ROOT > + | + |-- apps/ + | | + | |-- home/ # A simple app that serve HTML files + | | |-- routes.py # Define app routes + | | + | |-- authentication/ # Handles auth routes (login and register) + | | |-- routes.py # Define authentication routes + | | |-- models.py # Defines models + | | |-- forms.py # Define auth forms (login and register) + | | + | |-- static/ + | | |-- # CSS files, Javascripts files + | | + | |-- templates/ # Templates used to render pages + | | |-- includes/ # HTML chunks and components + | | | |-- navigation.html # Top menu component + | | | |-- sidebar.html # Sidebar component + | | | |-- footer.html # App Footer + | | | |-- scripts.html # Scripts common to all pages + | | | + | | |-- layouts/ # Master pages + | | | |-- base-fullscreen.html # Used by Authentication pages + | | | |-- base.html # Used by common pages + | | | + | | |-- accounts/ # Authentication pages + | | | |-- login.html # Login page + | | | |-- register.html # Register page + | | | + | | |-- home/ # UI Kit Pages + | | |-- index.html # Index page + | | |-- 404-page.html # 404 page + | | |-- *.html # All other pages + | | + | config.py # Set up the app + | __init__.py # Initialize the app + | + |-- requirements.txt # App Dependencies + | + |-- .env # Inject Configuration via Environment + |-- run.py # Start the app - WSGI gateway + | + |-- ************************************************************************ +``` + +
+ + + +## PRO Version + +> For more components, pages and priority on support, feel free to take a look at this amazing starter: + +**Material Kit 2** is a premium design crafted by the `Creative-Tim` agency on top of Bootstrap 5 Framework. Designed for those who like bold elements and beautiful websites, Material Kit 2 is made of hundreds of elements, designed blocks, and fully coded pages built with an impressive level of quality. + +- 👉 [Flask Material Kit2 PRO](https://appseed.us/product/material-kit2-pro/flask/) - product page + - ✅ `Enhanced UI` - more pages and components + - ✅ `Priority` on support + +
+ +![Mk2 PRO - Premium Seed project by AppSeed.](https://user-images.githubusercontent.com/51070104/168224733-b054bb46-d454-4aea-bb94-2d01bf4760d2.png) + +
+ +--- +[Material Kit](https://appseed.us/generator/material-kit/) Flask - Open-source starter generated by **[AppSeed Generator](https://appseed.us/generator/)**. diff --git a/apps/MRE/__init__.py b/apps/MRE/__init__.py new file mode 100644 index 0000000..fa28339 --- /dev/null +++ b/apps/MRE/__init__.py @@ -0,0 +1,117 @@ +import numpy as np +import matplotlib.pyplot as plt +from multipolyfit import multipolyfit as mpf + + +class ConsumerNotEligibleError(Exception): + def __init__(self, reason, *args): + super().__init__(args) + self.reason = reason + + def __str__(self): + return f'Consumer is ineligible due to {self.reason}' + + +def fico_default_risk_modifier_by_lend_percent(credit_score, loan_percent): + # The initial data set is organized as [{credit_score}, {loan_percent}] + initial_data = [[620, 1], + [620, 70], + [620, 85], + [620, 90], + [620, 95], + [660, 1], + [660, 70], + [660, 85], + [660, 90], + [660, 95], + [700, 1], + [700, 70], + [700, 85], + [700, 90], + [700, 95], + [740, 1], + [740, 70], + [740, 85], + [740, 90], + [740, 95]] + # the default rates to use + def_rate_620 = 3.03 + def_rate_660 = 2.01 + def_rate_700 = 1.50 + def_rate_740 = 0.94 + # the risk factor multiples to use + risk_factor_620 = [0.20, 0.61, 1.22, 1.48, 1.80] + risk_factor_660 = [0.20, 0.62, 1.22, 1.48, 1.82] + risk_factor_700 = [0.20, 0.62, 1.22, 1.49, 1.83] + risk_factor_740 = [0.20, 0.63, 1.21, 1.47, 1.81] + + net_def_risks_620 = np.multiply(def_rate_620, risk_factor_620) + net_def_risks_660 = np.multiply(def_rate_660, risk_factor_660) + net_def_risks_700 = np.multiply(def_rate_700, risk_factor_700) + net_def_risks_740 = np.multiply(def_rate_740, risk_factor_740) + + initial_risk_mults = [r for r in net_def_risks_620] + [r for r in net_def_risks_660] + [r for r in net_def_risks_700] + [r for r in net_def_risks_740] + + x, y = zip(*initial_data) + ax = plt.axes(projection='3d') + ax.scatter(x, y, initial_risk_mults) + + coefficients = mpf(initial_data, initial_risk_mults, 1) + result = np.multiply(coefficients[1], x) + np.multiply(coefficients[2], y) + coefficients[0] + ax.plot3D(x, y, result) + ax.set_title('Credit Score/Loan % Line of Best Fit') + plt.savefig('graph.png') + + return coefficients[0] + coefficients[1] * credit_score + coefficients[2] * loan_percent + + +def get_loss_severity(): + return 0.19 + + +def get_recovery_rate(): + return 0.9 + + +def get_risk_pool_allocation_percent(): + return 0.01 + + +def get_gross_rent_yield(): + return 3.686 + + +def compute_mre(home_value, down_payment_percent, consumer_fico): + if not 0 <= down_payment_percent < 100: + raise ValueError('Down payment percent must be contained in [0, 100)') + + if consumer_fico < 620: + raise ConsumerNotEligibleError('Credit score too low (must be 620 or Greater)') + + monthly_payment = home_value * get_gross_rent_yield()/100/12 + + income_interruption_home_percentage = 100 * (monthly_payment * 4 / home_value) + risk_pool_percent = get_risk_pool_allocation_percent() + + loss_severity = get_loss_severity() + recovery_rate = get_recovery_rate() + default_rate = fico_default_risk_modifier_by_lend_percent(consumer_fico, 100 - down_payment_percent) + + at_risk_value = home_value * loss_severity + + def_rate_mult = 100/default_rate + risk_pool_allocation = risk_pool_percent * home_value + denom = recovery_rate + num = at_risk_value - (def_rate_mult * risk_pool_allocation) + result = num/denom + mre = result/home_value + income_interruption_home_percentage + mre += income_interruption_home_percentage + + return mre + + +if __name__ == '__main__': + print(compute_mre(742500, 2.5, 680)) + # print(fico_default_risk_modifier_by_lend_percent(620, 85)) + + diff --git a/apps/MRE/graph.png b/apps/MRE/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..4136888b520057a490602097128837c61dfef1f3 GIT binary patch literal 77395 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

tH1D;DYT3#XWo&h-XVhf~YW# zTusl+ObupoU=3NI3A4U|(g3=k`K#=fBiK|4&1eCX%>e6(96D0O2M78mHjU5%J>~2- z?%F%0V)=ZFwRFN5O$B&pHZ`|vVq!2?R#pJtrbkDCL<7oJIOB4RIU+#V;%Ib%OT zBp_H_3khg^Tn1cBBqb&3Uu*#5@-_wr9V@HDh_fkErzP=chabr8`E9pT-Q6Hf+pceN zR~5a9yFp9|m|o6I&ht$F@0HF*KOv^T_MT(6dr%%Js7(al%tru#d?>}5*&8Nm)A6sbwN+AXl&Z5F{QliDJ)O#^oC0+o zwAU-yKHp%?N9O0x4+UKpK7$+v!OFK2K-*l2puV|`Fo&)Mv=tW)7+(c`cy!tC0hkCw z+=2gTevABR-NuMzaw&NJ759a>rAi<=rl(K;mPSi?cs|)0FOZdg`Abav{L$6VCdQN6 z;5b|MwgsKeSN$?mbiuBoR%7qt>nWq-qoO+O`IQ_Pz!IU`v37NHQ)p#q%~nNI;FeKmO z$&>Y7ayRRqXyVHs3R0_k^-CMjiZ4eo=a@qaQfk`jKUX0qZ_AGe=6@o2>=ExpQZh)^ z^*tZjvhT(S#4}Y1xcZPe-H*O2>XsWLq8+4D^w4`U3sHkqe2c%z%F9!_cYh2|O13Iy z{LFAfmW7I}_I2I%!7=Ge|LS^2meuWTHWziLT0=kq3#Q(tnQ@HZtT{TnKem{nT;7a|y)2(4>s01Qj2=l6$tdu5{ zHGh|UC#8*egej3(=vM$GXoxm z?8ZnE;}exTV<>qrH5G800`gT@hdB}{Es_FvfH2)vH&Fr%bHRe8_YuMAwl%IUatdld zRr^b?2zs6DmXk*Tdt+sa&(BobniyQgg!uVifo?PkMnkFNOG{FgBg0EeW&}c>l`3z> z?1hczuvmn}SS_dGip>62C1kxY6*tilPs!$go3{V+C$o@H>cPQ*Vke40~=WT2-U=SwyfYx={I>A(-z zvIfu>relF-qhGQES#fih!jO8;>lE( z5BQ#i!jH0~Mqx`z_yP=^01g3j+>G){N zDzKR}pkcphhw14MV3f9j6zLO)qHoA)VPS#k^1;?av@h0`nsamUBj!M-yWv@3tLbSs zKTEB^P606$7###eMBPX0g9nUZhPJl`4E!MD zPaxeG<{pjDKJkmf6o)qjqb^prA!toFj2m8sW8Se8%q&pSg&|=(dQ5tH4P{SCpq3Wl zRZQZKi#!V4JH&$|LpitJjybP?TP#T9K-pwM^*sD=Xf>aez9S}@<7#dm0)RW~+SaEs zq#&{DqUjx&uy;&P`=}nLGp`nm?i7E)TK@Ug_JWR3G&@MQzHZOL>&eH;$^@wV5p8hU ztaP^OCJ{1AkZ)o_r5u7R$(g5E-TKuV>tIyPNo1BMa?DcBh9~id(}vz18$1I3E37P+ z@DY4u7OsHqo}NnI!&RO<)k`y**-8EBpb{QCgk3R>>qihlXrSI=HKX4^pu>S{IwJ{K9^;jlIdI4FX|HaoOo_`r7efK84F4CUE==LL$91;Bo zW!@oZpIoti{dq(9#<@ML>vx#3=rbguYdp33abf>itExcD0!q z;7xhEe5ES8OCq*47UZj%7;gOK>);YDHaXF?trd!LVdMz;BiJGjnGoB?@^n3h^$is&EjH_?15UC6(zAHH8=BN$SIvX{bFMNpKL#l6$ z9HfG~ML5FUSwDzi4p2x1Wi2n81F@-Sa*Ake0ulqXH{KUvPc0(;Dz|+b;`T|l#77lm zjCg$bVM`~kn`8s-vOWMTrRXy|L`KxxCW=mpABc%ne@ z==U|mL+wZMuS0z^V!m?aRl%LWi8!&sATzh%YK|a^0r{x)%6*jZaTA zN0q?Uh56P-VlcJKyLYwJYIjaO5;5=I-8xpf6c7?pUgf4SKf+Ce`IUZ|uH@-*n8;FT z5G-@F!EOI}8=|X-OS)=6ozLgz-|X|27UJvcaZPh_-XOtE7j3=U+AnG`uV#!|d8^e_ z4F(}>$KcbF&HveSyx#_4r*FV#*b55TyN+Vp^-WJVEoCIM3KomfthVi?)Iv$YA;0>?r z?1))XRo7X)Dwig7b!244CnTpDeJ-_Ekyqez>i^A0dLi;>)KwB$ob6EPP_azbwNdSt z+>Q<=ODih`I3kqeTJ93ySaaj@_`tpAfj?j6?p0>$6Y3eoxt7 zpZ{XAe9&^3@ebSZXH^s+4HOjOTke zsuHBl$|?-vz@Ud0Ch>sjs}kd#GIRxzsJE38oXcP!2w{}j_yn)gZeqHw%;6p>gV<{z z-a*7YI6RatnIyo+m$qE}#pa2mP_VZY!t>4bp0UZ<#MR3CdLRo~*t~w~Vgm@VhX)T7 zhzP{fYn;FIr#}3gDoBYtWEDcA0OJUi_ri%tCww zSx(F2pyA8fx;h}t_YNepL-&z!(sl>+nF5vH^Xtn^DGE`qha|zg)zJvNQv#@IM?K`j z)+k@57X1D;J$aXb`c3>>cjX)xuz2z=C1#o)1GD+jRfK<)RU~l*a1mHjC7dH#_DoKI zJ4VmO77chRG??(JVdGt%HrtpzKbRLd+`T{v)DT;^qIA0&HLnXK{B3LFqJkt;nk$^$ zNdmVAvbJu?6V0C;TF)9ZGGD)DF!&oe{nfG(tM5G}=dJdF=A67dWI#30;n%Nh0NUfN zrLs2{u_rGV6aT4Ufx;1^LoABJa2t?4qVJo(K35vE3kctjfl z2PVL-UQ{J^pJhE_NUsHTk*R0nLUp?KxIByaY@jLoX8go~{u-a|2?s3Bh!aK>KQf_LUzcc@2 zx|IKnCT8E56oA;a8WT-&U;2D?3p0H-x`>Z7ZsAy$i;2B7>TvVu*XQEvF^aci_AIQd z9=@JNc&Iu4+pJe-qrs0sw}?S30kT>{37M=Y83$fZJ8GZWMLw5%R|Jvt zgH+4<$OIOae+3FiB5Y*W*H3$}_hS|?5#akeI5}lL7BgJ9GQr+Ok#(&A z5CR#izXJn3@afZ{z_Q;aVd94x0!Ti8&2JyR?UJg8JyZZL_kBMfxdjMFaamcx;_hv% zN?u1>Or8<={OKx}Hlx$p-bRP+8b^IGDI=MGGUW5$!d@9-pm}p#T^+^;AAp_f(U<~t zrQ;;qX6`$Pq6j$Zo0S`;K9Lf%3WyBh62(glR0>P+X|GfXibe~tB?guxl76fI?*^HP z6DD$C-oRQyej$AW16IGaaH#*csCQChjYsTvCXVM@p_ZR6H~4IDsHGP)H$Pv{*!RQ! z!m!8l*`B5xKuoViI)UzJ|G*jK7SQ|wC9>BV)?_e3?_Z!P(rIa1+>UqZ-`4V1sPuz`@m%O}06)RCeTJQ|w zNVqBr(zyBQFXcl;MGqDSIJ>6)TZ~izyF8F&Z$N8dhj{zF3Wy3;z!Gyt(8*`9>4_?3 zVZ8~8$3|73HmnYVY3n1ZarWq`Z*#G)*StIXQ>MmG)ltHIa{#S?U)*}OZ7It0i>A0@ z0x`mX)h#%U4sLFnZG#==cS*MNY=`2fD$-h#5))Uy{0yN%Z#kKB5{GhPcfLLzYJ!nY zgMrT~|5Ad}S4RO5bwDf2%KrEzUGxxiRdAYMATaqZ_k_~mgf#9)EeR#%Fw{pBB$#!q zxB`2By@EVYY6*;+c7y{@;RetKez|9icI>5dm>QF+=;^pdTc_J&(5+>mD~{mjL7i@F zVxp*~RzFDgu2DhFC>I&T6KrDj*~v=lktfae^YkMPPi~uDt|?0(wJnmWy=!aM@_d>d z1y3l!7Z?*T$RHxZQ7D<+xE>K)>zM#%CwOM{*m0sTQYsnixcAMOmy%!(XgOcxO**o2 z0H6NGEBMTZz2pl4KU;Sfn^UVtM4d=RH05m{BMR`Ja5KOa21v)Sjix$95SD@VwsVP^yBnyH*A6d6i_Y(1c-rv?rGRzB*;Eln_s(h ze$7gRLF8&Ge*ARYw#`70#yctr2P%uzY~F z*}>!wA+|)w@#@mmZ$J1i`OsFAvkQSO`%KmGg@st9fe~jS`p%8O&(j3zKIE~sD)1IwNilL=DXafx+4TZjRr0~zyBGiD3laAF;;`RBR<^rzSTl;x2yPf)A}VZG zwsS`UnZt>+2S5kT&bbL1oU;$*w@B-P@&9hkony(~@;^9`8$5Wd70;6kHt#*d!`J{x z0>TC9DnqXrbCn_`KZZr~sv=ILI-jCsZ?&z>5oI|chG5IZtq_RYplrXqy!^4V1EvZO z=Dwc%`IAbbxKS0&IClpEDKL5ETPlqFvAur80{of6N!m6pk%@(k4VwiX!03AYX7H!? z#qBx;-xJD|HJ(~cxi)86{nJNK;Hxo5OWVclLN5+LE+q5E!r$CMX};h!@d!~H1H-b% zVUvk|4gGkNyD>JFawbEp!lXF`s6FtQvx2g~<_P+?$oTj^%SteRklJ6%s@&y)hXj=g zJRU^NreyQ}V{yE6et5gv>e9CXhn2O*oWMw8=2oS`e`M<c;1#8OP1q#bxYg9c|0m`^-8qzq-(;ykxj>F^^&vENbW9`%DF7atuw62aZ zI;H=CIR!i0fP*?wJ3>7L)i+S~{Qb|<{uo*uAEdR9ySpuzY@FDM!8ln}6$j$ZDYyO* z;VAX3ig~^RS`e(?~suxSz4}^)>g<~ zq}D2<6p!INyz=tNt$+SK@{nC3udxgOqUOJ%l*bNs^oA*sm?AH~mvz=32M>~*F7F9e zONGY=stXuD!ai8;M882v`ILTRTYhb2rK2+()1blqt#%K%R%-G_mVNft?U@Ap7G%GG ziwOukF+Mas9ll&L<_c;5cq-B0*NU^nL&q1t`05(qw zm&rDKrSAdFw6P~PVxw28iFf*gQCj+cIw!uL_^6$_jVH@j6I7&%!L>W->}pl}c34J>fEAvw60ZNkX8rJ~`-%EQumko|z&ndi)XZ_!^^87!`MT`huj7Zq#0L(JXusK}YOhyQ=( zz8E0Gr{^!ZQ46gk!Tf*kb!JXOI2aoYX7VgtlmQAJ(m0Pa1!^z&^uTy< zcIKeGA%9iVh7n?xRjaS0md*+l4d}a)Qd1RxR*{hK(hrwP%sUq9_^dV%h#31UKB+oQ zO4s}M;j7`L-Igj4%ST%PB@o~MpqW@RjEw9f<-DSBn2w$zhsTJx-0wqexcgMS={Md} zeAtym7SG9+eKq~LfW$+@9G)0JjSja<4~L4t-?cBEx(|#12*O^vI)Fmi!Q186YAC(^ z1vb!?-y)6Q5fQv*Mn`FzHM!S%sX7KTeztIzKQ-u z%ctDc+Gz22_ub$Oq1q-4Nr7<*ydIpGwza-^&wj?#gCF_vN7cE1RiLvw#z6qG0oor9 zpKf*k9Dax9+`P+^5dS6(m3P7h7pC?QvmoIh2x^7NDlcZPh#M_T_;*rbvoiTevEYaS z`Pa53($Yy1#&TQR+pzG!WNw^@od;(9-oy1##2jjBql)vX6Vf%oidcVorxz=y&vOZf z12yL3VhHw)*VO#CqYY|lV75X@m|qA*2~hqvczW1UN=iykm|qr zmvCHJZ!jV-p)!veH1Tg$eX?-i0N{r?Gw{)c8SW$Mkio&O|MAObPfB4ti-L|d>b>ag zIuzL#33#V~M%^v&FdjCDGRlBs4b3>1-7`d`fIQd(t&+tuG%U=Z$*Xu`#HDa==|A}s z8B)2))*zab?#R}oZ-VliJUkkX4uXP$nudnazyyFx49eB^{UHmF8@_dauOY5LF_cVq zh}3j5R{ZG7o&9g`?=gn((N;8UEX)ZAMZnsKijD>hNR?=o(8cKvF>&RS9o?Y6J7%7+ zhGcM6&Gs9jEAtz*4!%$6fE<$(Ae!ykjUesW-~v4$IA%a=H! zqN48>JSusAtgnZ|bAwgFkg&t&U7O>;4@?blh{^u7 zTWuh`gE_MeOet|jMTSx5&jiCd?t`2Q@%E>tfPe;kU2_nTsNM-SxgqaZ6V`s9`^(F3 zyGwI`Kg_q8?QEXd;k|9;o|p-gtpFx6(b7Ic3?eG^-<{QkhN2)T*38r`i+Ras`2S)F zc^x^6ACgz;@9eybKCyp;*zg;>t3vTV2EUHPpJ}a9B`$biG7k>N^08Ozyn)%SXY7Fn zLm6=9;A;J)N#*~8*5I;#DB$0ypZ;4r2g%^;W9wC2GvM^Yml5j9$oDnPN{)caJuQtC z>T6Ib!T=c2W)4q(`JxU70#wHDnwm18+R@a~qCENFi3)PGq+5>mKPT4qKjbZ$)3$33 zhm3VDNtLbl$E1RJIP=*LG&Ho~*~UlDGc(D-aY|NR9(bo}zzPFOIY5#R%Y5w8)i(LD z>P6Qcw4xtmedFza8)|rUAuIM~`HxWw!G@TSXVwW#bOOxol&A&49Qaob5XcBPEip}N z(pdK>KNTv=h#z|RT)9YJE)q&G%K5)g87KLzHYK0*7-Mx4p(uwIf{DpiR_8IVyqi-S z*fCwM;0CvY4ky(tC|ynJ?`v=&KbT*-n%YWu$;&$;1J(U;PO)M8y;>xM&jsi&o>uAqDRQCxg; z25o+Lv>;`KD$!cr&-BXpoNA~W0RDqM5wiB^?9UXZ?S8^{n1y0)Oy zC2}^ULdbjuL$k4=y@C>g!|YRFr#4Zz%(MT4vLXoiPeW$SzF@LM9cp|5Bb<3ATcneI|4On?bkpKee#1uy5yPhGJ=E!&~4zJTx`U z;TO{fQwfE-HgsKwn9ciy=XfXsjqgS3P3_JoGDc8Fu|1Lw{iEi5+jUu5mGY5UsXw~yLUJL0az&EGPJeU3VdTs z2LRdFlI=`^L;OhkXFU0a z94fAD?5oSPzt49(=E(&5QMlZYT@dag7!$=qMXfC@3_QXKijRN3ic%W>IP39(@+$Zr z6T0IqFSdeVMBB-ase;7!l9SN5f|-^?{zLH?oj0fCFq1Dy1yT&FjtRJZgS7@dhwW}H z_cmyTKYnyyxLg21V8n?IlKb#Bi{_mXju^itKwng|gr&W_YPwb{YHO3>8F=nDZXS&W zUwKYR&Gdi%&j{iqw*L0+=c-%6R6A4OzNrG`v}Tb-GNQ~RLO6_Z1d9yoCgij1oPwR7 zE1kq!_?H*h;$TNbFJr(UvB=3&WnOFVdKjQc?u-MXJg6kiz6K zbIRFZ?x4;_O#h*LwXQBLgVxn4X1-$W@2AM)NYIYbi;D*i{kc9@6vzm=0d6o2e{U&M zNct$?u?OcN*V026taG02L?~6ker{uEO7XwmWuwNf=57sNio|Y@ppehm(K?LyR8MEd z`1CEss>{n?CMFU9B2=)wtnw(Y;$b{ZcVBWgGnq*7md!c}ceyF`;Q5SJ+W{>QbA>y} zJ(!4JBU0ao5|)}aNs$&LUY(2SI<~W*W^T$G*&Me|zV9Dz=$6Jt?3KRb+Ys)#TzZX8 ztv!%nsS4r>T^3Lbjn~!vKfKjwR_;9PVcFwI1*c|TF`tI4^Urcoz70d!z672T;FCy6 z5)9)TW3Mr}QvJ$MJ+r4eknM^%5v2@gS}B^d5ps>mNt$!M8`E(R-Rgp&fO?C zrfgQ6?=df`f!ahb7+n-2^VYn$K0&#&+2AD1#>&vWC=W7N3-8X~|?zkdHt$jTBgKPA7H=6A5TGWGL%FqXmdaDnq7HWrrqYWHnWMpB%# z>~3#c?o5?Ihjx6vQ!$@xO;68fmax>yTO*OToD6j`3!IyJmJhYce*2;Oz+p2u1sOwJem%wE{q|VoW z17RPA)ld=kO|Y(RgkcpP1?6ZN_d3c!Of^Y$V9Mp;t{mQTe39{qe#e|xlv{-NAr)!= zB}9D&y9bnXI^diDPqyG^47KHz_nVXyQwjW_{RM%DNo^iN5jwg~3%(O5YpSZNp+Z{? zzWx)OEm%cM@A(0tCUA(CdHbfk9H(SbbgSwX&G5zWyCdd0E{F^Iz$Tt3K>`fnKN|tY z;kw%TdM!|HK>5p{e+i`OthPxy+)rS<;9ht7!!!nozZU{!nM(=IrVjU&T*cJA)4z$q14&i#+4K*O-R zIN1XsRHaqlOA*kWC8R(4S9jbSquv9s9e^z;=xu;5M;OHBGgYMkj3KBK{->zgalNjq zcCj2R1CtC0l|&ff3^yS;6*LOKYuDACYH3!42@&{}1`G@psl`SISG(9nvl7EYg2Q## zS1-9)B8u!&8>&#xlqVTZo&yoLvr`5lF0`KBJ-Bnq8p_ltc@5=D2A+I zjrZ|@q@uhn6ZDH4HFC!P=2#pY&YFip&>n?Ar39p&cW+J`d+XMj$QbJ3-5DCPadsf| z_>PkwVB#pt-CI{z=P;@yniO>u+%vQSUJKQ}hn64ap6vdmyA`5rNu0%T8GYrpI)Mne zZ0&_W57q)nVUHy(5U{pf`eYogMk{j_VMGeb@T9pfKoq~y%6q_P&IpRNTU<)!?<-;e z6Jm{%ZK)d}(gUv!ux1H`VMxTL%B@?s8ZVCqK?eocuAZQG%9$!HVs1mg6C=dKlkn^r za&eUqnceBd?8w*Q)F1ptVc)??=b@()JX7vZr=id&^b@*qp$*#0vM0m&2K--b7Tc@c zzObg2nzRv_gX>--D=~0_F21q$+2ad?OUnhfa)PtE^KenmKuPt+LGJ@&dGObIS5PoK z(D|$ZxrOfWxnv+H0JZh>k}@(x9p)aHT5(?=>kuO0x+z*)_~Jdk75^~}(KdjU0W~%C z)`OQ4$K`+tUqSmr=}!foGdg-E;Q)4a;YwxstP%0@^8Mq}psi9L941P@s0bQptE)IZ z!Kc6mhQbN}x6#3LQH}PQb6gxOEW{e6C~F>0dVYQ{)pnyj-O&>;dfGWK(D`G*fxgMoASFr$TV0`6Fq zmOar14oMiH=GDmJtr+GoY5}UtyTU@%ZRz(y2B(YP9;4C(*a+M8#9to3zVGnV)H^YE zxKmb%05t#zj?iC36+RSf9EboqMnq3;hxu}!)D;gfuL6IKDHN4Da?8|CIJGC^DTu82R8taK$heIEiUt3SiqhD%w8VbW(dk)D1=8y7-m{g1noI* z?#ba{0_vc1w1G$F)bMZ|SeI+LrXIRn&bt>33Q9jH{jmY`?fd_ZEY@;L&20}035QKPm?Nv5|8#+TtH0W@) zCKeXNZ7`mL)`mZpTUQ@SY?y^0O8aDKGc&Uyor?cv57~OtsstfpSDIo{5)vi^_io(e z`_rcNLMOz9y|a}D^8>3xRQZSxnl@mtfB%T zEFActui;5uLE(h7{B}5MB6_s?_haKGFIM>V=;scA{XsweaCv^f?EZc5ELX%a5v!gZ z!WLS%w|m#`$Cnn;Kd%zfIse5_DD&B$exT-L>1KEZLNbi-&bOH%GrA_gvVlL)3TMRdQWN#@TT;xE04b0sLPNA&0Z`N<$+vxA2 z8j3o&?lXbsfGpatQEI)3nKHX)x#VrLq*8lUDz=iGT%wW5(@`W|-LIpeD~vEoop^hY zc_tO`_u4u9SbK|Jz8XOQ>|<-+Sbuv-<6&twQVe=lP-3nE)C7PWkQYC+OaWJ6e_s_~ zd3f0nL?Xhgdn{KrrLIMwdk4Q@Kuz!>Mh)aQy#DzHqT%D?6JAN{0w20`NhMXHyW=E^ zS4E8xDA|x8P^Hs6l)?3p5bOy6vX0!=f$ley=HX=W){IwVrwI59|M;P`lWC_ctwqfg zb3ddRMehOkR=&z>Tph0iZ&!AVl)7& zpIc|W?|OfK!|$xqT4$Z-iM_wyd$_L8blvQ^sda#-I1ZNgx2ZR)modR@);fMGTvwh` zN2ifd_kGvVJ1C?O!-g3o6+hg>HwK^7n}v)HOquW4_Wa%Z*3bb04EB01+r5exI@OPQ z0mm7L$#>)D7qJP~m;q-3D{XIY2cG`?EdPmIrxJIo2a3nw0K@+o%*t$L@ITQMr^g>g z$m}9RD&Jw8$%)EyH9%9D@QYG)hydHRvk5w`UdKd3#or9Kyo%F!+)?bvpC6d2zx` z&01|Og#Kj*Ja}M&f(xuUCPl_;^KLLKg4bbhe_2t9oE@gjK{(Pk>a{3MycRgXWX1}X zTmE=+n`i$fApbk->z|?E|_7w7;^F zQux-ESDE*;cYh8O<~w+8l7DZ&esEj>sZsWz&aI%gITWg6rdglT4Ta^)!EJ>`@1AW= zPF~~C>(9$`#2|$|$1UcXfoj_^ORr8zsMH8E6_;XoFtPs?1TO&Tq@-M`awE9bppky5 zm0ULvZ7t`~k5J*WFF2;&t-pB~W(~AtCIKL2 zo@n8MgGGir^pff;XGimE9BO3C(7vrKxVv$C_OECDG|CU_L%-c-Cft#rV3KXpXJN&7cYK=jKPM61{bM7bCC$hd5$D#*!4x2(J6&CzJGrR zD(v{HqhJyFjBLM}caCuEe*2frR!88?x!32I*9BxndS6C4^O3KKkUGs}ddU4^ z#7o+(&OyDiXB8lHGxYcy%1oW?@)t&!U`}7;RnyL@d=m&sNGLx7DBxZu(-q>p7Nopp zTROZiCB?uUPV#*QIuH(<=KA*vf_`|{A7)Ejs%HV?blRr-XuN!sDTJaAb~v;)n8!_` zXb;7xO>M>{*?23Bc0Uw zG;D!!p-qxBmTPNBOnhDAt+A|vv18rgBMeBWPQXIOHnw=T%}};M$?l|l-pG|5%`(y~ z;?HOfI0(+ot-V>GsV-ZRzwlRh6T9SL6_s@qEcAYf{OJc7>Mr^tA4cT}`v{()=h6&V z9D6K@x`=}Q-8Ad&E0Iln7QMa4aaa*z zE-Vc6I6@YA(a)Tp-EM9$$D7?qs$DQT&8$N~$Vf1o4XQE#*2vvb z>idW`>l=e7DvfvvO|t`V_Zb-M0Fr`Bqb7$I?FWYiewprD^PslAACP6Bya9rQSf@uR zHo(2naPSP7n7;4%`J5F;{*!C7igU|&Kfj@EPq{XIalc-+jrxFuRpmt?12G8$8^C~i zdIAx9qy=;wubsbZeuG3&0**7jfe=EGqhFP$^I`UQt(|oCx|+Ir+}HnH$$sn@-dARx zX0K?;)g%z(rHY(-D6Qf5OGm1&Pf3Tra9%m)XP06QD4dBe@VE>-|0WsQb$CoEzivgS zDP&77LuqB%cq|DKrx6l-bLIMYFGM=1MVN<9TQ4D_H&vs=)9ktx@$WX}IPhU+_D`Ms znJowlF>T4TvTVtGfcyu$JHA*b^7399b|zQbw{P9b*AxeG1pA8HHxxBqqD~)%x*^Xp zTxWnS6V(%B6iP};T>Si%Py1P|0tAJmpadm^7>Jp(Qqd!iFQakFTmA)Nl5ee76$<=R z*3RGUlq{f~7KZFro#Ve3=^oyP)weF#)(-Jm6=YGPpM_Rh_6i4jF8cM=;etMU_U!#~ z@5d`HJ`zTOLxeJ`S7UZscqW6_{2=c{)XbQ@*7z7pzo90;22j4ZWfVj6aW;qg>~Ot@ zxtlPZpHGbKzQ%^=FNrVZS2)_WVL;o7z~berH!0mrO-&6<&71LyvdMEvQP=5{zMs2N z(BVAy8anu?hMM2`$4EO(K;8jEk|HQbJgUo(4xtQ^n;Sa@&m)Regg5$FpGFJ!HY@9S zt@+o1Yr$I$%*}t@7tp~MKyFX9lKvOoE?#@$dbB!$JSpXqo6ra1WP)P!yPMjn|1en# znuk!&Bmu9vC&7h%a>?4uZk71e3fhj+U)Iem@EKf?=3GWA+p<^Gk2hnh-P30o86l+4 z6R+7DsSX8o^ShY2-vYaN1#<1KbN_*6c>_9h)!C2w9b)B^{ejOV^rq+g6ok|RZGXG? zheG;Y0g#COmEZcC1>0?MKgmwD7PWtLBZ{dQ*MNYflBj|&4%R^7htbieuwf+Ht})3^ zSExI8`SRiT4lm>Ma=*P1F50J-%Xzxbl1`@?ZcKb(Ogi_$L$jxiT|!Qj8idNDd9y;o z`Ew#ISvF3UX^8$neT-*S*Vx!^+p7XK?UIHwM$4{uPi7Zj7OhP+$*Nmh;_s)V2tjon zUm5y3?76uXByJ>Q(SEqgC=ljM9$=a`6pXZefZ&iM&Jqa*_T=f!ozEEm zV3UF$qDS2Ns>PYce3Uk_Hpe4IIZ-zPq*+6UBYMeYSRTX!Y_M}+paPL&O?e*D=pNNR zoIx%yq3$X}QT;=izlbkNwO4V<|F>(m!k0FS6hYl|h4Z_R#pzgMlIN1^dqVEN!lL6P((*OCDt$j6 z1c(GhBZZKOu|91d-6Z5^n`)eq(1Tkkqpn(AvG2o8ndN(^F~p-3x9}$sB&~7=85L{N zm~vrm*v5Q=+C9FX;9$!A_%VTbv)(cj@uLk+Bl!k)h`NjnD!GGW&Pe4chfPT^&(7MIuw&UO<2+wXl3&DWgJcn3=I3b z+;H&`jVa~_kf99#v3DN`fBd-bVBjHu?ggaJC+rg9z#UT9K zUp`ZzS_X~;3F19p-=J5wa9iG<_l46rYtCk;55IJR*KFP2+Ex#Pq2#zY^X?1+M{X4` z7mu3mtc!cj#!PG6QV|^@nWme8p4iDL%UF&Bsa6-uANV!l(89__*1- z^GFcBPMaC-$d9|4l5|{(C6ZP8q8JwCrwj-N2jwT*VTkuz^E{0!hlgAk;UIcv1|0_r(60iy`i}{ujcX>rxyFZ zjE3E>a$ou;Za8mfsIAC10mzNkKL$g?f@5O7yuUtZs$W4wE=Y!llL2D$6IE{!gyOIz zLVO3_CtingL^*Z%g%pYg+$~7k#t;Nmeo1^oDed-htJ(7u+cSk7Sqed+tYTM(9dCWw ze>A#UVq8#L&0l)uf~)((a4z-s4<>)##wEVfscGoXGc7RhYz~!6-pKm4#obi|919>m zJWKe-*%v&d?R(Zhw8mB=H!FxC-^00XoS)09B>Hp$k1F;t|LpAC>9Tek^xe}vOpYV1 z73kdHYgwt6X?Y3XEc$wbZk@G1&}rPimGI6YW_ZOz)8=B97eB(2etYD79osGf-yg7Y1U(7iP^;AdItvP8og6IXX$9`47Hh7kRe%|9m5xmI4qv<9H+ ztG&Fu9`Dl=7cPlA7ct~SUAgTXo7j8;&p!{<2G)IqsR-Me@bv7(tm!`rXyPnP?RmRK zf7L$M_nhvX`D#c{ zzv&Zz6+oC7eQVW0k%_hay_9*y4U{A3qD;l)b(j;;cWn=n+;7NG2bq`=3~Xs_iz81x zdUMg;{4QC@o$J=xvt`}O2Ya;*+ge+99r$JbS1b9HpE5gD_1Zlvi+{0PpHyvF?B8Za ztrkA@v+{nqg=}N&O>IxAnp6t^ZfToJE^*@_QyNf9oa7K%pf@`auI}dI@*qd<84Z>) zT)0v)4z6=FJIq%q8@%5nqDNmyl41GP)VBa8 zLPg}WA)UJ2t?9zo(tT#aJ0!vy08G^DAW;YHQ}M;ahrKV{)I*~b5^~we#6AUJI@ESb zRu;_b8WyGT-gga0i zkQ#yrTX&pH&hy)z+I1?*`9T>|D=`%J_A+vCSUCwz=yO-^g5FMfy{uUCG9lNItF84a zd3mF!zB_7%t~~OyA}WOJgH6WY8yht>H62v=Lo#oEz^g=xf8RELD+@bQ`@Bp0 zSxE{SSO|1HFC%jV@URJy(?K7>WeKA9j~lt-U1rS%js#4M+3; zBHX?tS`kSTrmK_iNn&$=7&6F?LROpP4M43zxnoBW;H(PmByQn^7ccIjrwYXR2WXuI zmr;!4^KGoG4~w73P8UNBi@yQ+F=8S_tM@yKKX>q$pPN&gp1#NSwMz_VfMQ}D5`Ry^ zr@U7-e;j%vETo_P{UWbHIi%c8|FGviWAb5flB1gi&uJE*=N*>>RVJ;7_WLN zZTByj?1)h+=E%5lP9F6RswHGM0q%s>Q|Y`dYs??}R}5J}%jxJ~_-qQFYM9)b_u{B% zL5Hwm_4LW%&oOydPaNQUd_@@-)}m)*z~>F_%q5mR@bW^!UOG?VUMj#Lj8P0%pQX{B zoaKL+l46Q?qJO)oL`20M9pmea)ZrxL;pNNfT; z2-H@G8b*Xvn)UDD5#>Np&C|UC*DToF9aC=j-WM+H^Zf+ytRdA<9o>jtmi2C6wgc0} zNKpgwjOvL{pIltt5ZqKrM{Q2y{|=zOFWB}N)tSnel=Cri6)g(jq@P52E zDIz2!q_JKUrj&*f%&p)8o(GjE!`8Thit3%PvHIVViNEZ1Jw0l0kuPqgr!{-_<+evP zO|p`$GsEor$1_A(?8@jLOS*gF`D#{lM`Z1OmDs#aspVjd^nQK6y?0c)M zaOA8G6tpB$r76>DJEogEaHkNMCy&;(UJfI|?DB=z(19JndzPJ>2+Ln_Hwams}cWStwjkFSI~hOBcGVQg%o{^I<+Ze=%Ze8&?~XXp@LBh^x4kJ%Jq*P-}ZW$%*-XRNV(;d z14m-##-AyCdp7SfPIZlyJNLQN+G6*U(|- zp#yyw6x8_;NZ`7m(g)k629qx7QC+&O{Ubz`q+Kdn1sNTz zQ~Mz;oa@?}{}cHuE1n?H8$C4iD?~o=XS7;w)>8ZR`4Z6@8PP$q5r`?CN0c6lHh_kX zqwP!P9+XNDB58#32nkhN2Q{>|o^o-SzAes=VJBBEVK@u;Dk0xbIpusLB`=RF(ivE{ zWbQ+e=9*e#kuN_A%N~O*$GShFps-@zdgmOiKDV>mlj@N`r>~{bo`dnFa
9$%fm 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 S@)({5MftdzLdwyLH~$}KZ#}^P literal 0 HcmV?d00001 diff --git a/apps/__init__.py b/apps/__init__.py new file mode 100644 index 0000000..f23857d --- /dev/null +++ b/apps/__init__.py @@ -0,0 +1,44 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from flask import Flask +from flask_login import LoginManager +from flask_sqlalchemy import SQLAlchemy +from importlib import import_module + + +db = SQLAlchemy() +login_manager = LoginManager() + + +def register_extensions(app): + db.init_app(app) + login_manager.init_app(app) + + +def register_blueprints(app): + for module_name in ('authentication', 'home'): + module = import_module('apps.{}.routes'.format(module_name)) + app.register_blueprint(module.blueprint) + + +def configure_database(app): + + @app.before_first_request + def initialize_database(): + db.create_all() + + @app.teardown_request + def shutdown_session(exception=None): + db.session.remove() + + +def create_app(config): + app = Flask(__name__) + app.config.from_object(config) + register_extensions(app) + register_blueprints(app) + configure_database(app) + return app diff --git a/apps/authentication/__init__.py b/apps/authentication/__init__.py new file mode 100644 index 0000000..25243ec --- /dev/null +++ b/apps/authentication/__init__.py @@ -0,0 +1,12 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from flask import Blueprint + +blueprint = Blueprint( + 'authentication_blueprint', + __name__, + url_prefix='' +) diff --git a/apps/authentication/forms.py b/apps/authentication/forms.py new file mode 100644 index 0000000..89f43da --- /dev/null +++ b/apps/authentication/forms.py @@ -0,0 +1,31 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField +from wtforms.validators import Email, DataRequired + +# login and registration + + +class LoginForm(FlaskForm): + username = StringField('Username', + id='username_login', + validators=[DataRequired()]) + password = PasswordField('Password', + id='pwd_login', + validators=[DataRequired()]) + + +class CreateAccountForm(FlaskForm): + username = StringField('Username', + id='username_create', + validators=[DataRequired()]) + email = StringField('Email', + id='email_create', + validators=[DataRequired(), Email()]) + password = PasswordField('Password', + id='pwd_create', + validators=[DataRequired()]) diff --git a/apps/authentication/models.py b/apps/authentication/models.py new file mode 100644 index 0000000..265faf6 --- /dev/null +++ b/apps/authentication/models.py @@ -0,0 +1,49 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from flask_login import UserMixin + +from apps import db, login_manager + +from apps.authentication.util import hash_pass + + +class Users(db.Model, UserMixin): + + __tablename__ = 'Users' + + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(64), unique=True) + email = db.Column(db.String(64), unique=True) + password = db.Column(db.LargeBinary) + + def __init__(self, **kwargs): + for property, value in kwargs.items(): + # depending on whether value is an iterable or not, we must + # unpack it's value (when **kwargs is request.form, some values + # will be a 1-element list) + if hasattr(value, '__iter__') and not isinstance(value, str): + # the ,= unpack of a singleton fails PEP8 (travis flake8 test) + value = value[0] + + if property == 'password': + value = hash_pass(value) # we need bytes here (not plain str) + + setattr(self, property, value) + + def __repr__(self): + return str(self.username) + + +@login_manager.user_loader +def user_loader(id): + return Users.query.filter_by(id=id).first() + + +@login_manager.request_loader +def request_loader(request): + username = request.form.get('username') + user = Users.query.filter_by(username=username).first() + return user if user else None diff --git a/apps/authentication/routes.py b/apps/authentication/routes.py new file mode 100644 index 0000000..490b2d2 --- /dev/null +++ b/apps/authentication/routes.py @@ -0,0 +1,122 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from flask import render_template, redirect, request, url_for +from flask_login import ( + current_user, + login_user, + logout_user +) + +from apps import db, login_manager +from apps.authentication import blueprint +from apps.authentication.forms import LoginForm, CreateAccountForm +from apps.authentication.models import Users + +from apps.authentication.util import verify_pass + + +@blueprint.route('/') +def route_default(): + return redirect(url_for('authentication_blueprint.login')) + +# Login & Registration + + +@blueprint.route('/login', methods=['GET', 'POST']) +def login(): + login_form = LoginForm(request.form) + if 'login' in request.form: + + # read form data + username = request.form['username'] + password = request.form['password'] + + # Locate user + user = Users.query.filter_by(username=username).first() + + # Check the password + if user and verify_pass(password, user.password): + + login_user(user) + return redirect(url_for('authentication_blueprint.route_default')) + + # Something (user or pass) is not ok + return render_template('accounts/login.html', + msg='Wrong user or password', + form=login_form) + + if not current_user.is_authenticated: + return render_template('accounts/login.html', + form=login_form) + return redirect(url_for('home_blueprint.index')) + + +@blueprint.route('/register', methods=['GET', 'POST']) +def register(): + create_account_form = CreateAccountForm(request.form) + if 'register' in request.form: + + username = request.form['username'] + email = request.form['email'] + + # Check usename exists + user = Users.query.filter_by(username=username).first() + if user: + return render_template('accounts/register.html', + msg='Username already registered', + success=False, + form=create_account_form) + + # Check email exists + user = Users.query.filter_by(email=email).first() + if user: + return render_template('accounts/register.html', + msg='Email already registered', + success=False, + form=create_account_form) + + # else we can create the user + user = Users(**request.form) + db.session.add(user) + db.session.commit() + + # Delete user from session + logout_user() + + return render_template('accounts/register.html', + msg='User created successfully.', + success=True, + form=create_account_form) + + else: + return render_template('accounts/register.html', form=create_account_form) + + +@blueprint.route('/logout') +def logout(): + logout_user() + return redirect(url_for('authentication_blueprint.login')) + +# Errors + +@login_manager.unauthorized_handler +def unauthorized_handler(): + return render_template('home/page-403.html'), 403 + + +@blueprint.errorhandler(403) +def access_forbidden(error): + return render_template('home/page-403.html'), 403 + + +@blueprint.errorhandler(404) +def not_found_error(error): + return render_template('home/page-404.html'), 404 + + +@blueprint.errorhandler(500) +def internal_error(error): + return render_template('home/page-500.html'), 500 diff --git a/apps/authentication/util.py b/apps/authentication/util.py new file mode 100644 index 0000000..9130da8 --- /dev/null +++ b/apps/authentication/util.py @@ -0,0 +1,34 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +import os +import hashlib +import binascii + +# Inspiration -> https://www.vitoshacademy.com/hashing-passwords-in-python/ + + +def hash_pass(password): + """Hash a password for storing.""" + + salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') + pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'), + salt, 100000) + pwdhash = binascii.hexlify(pwdhash) + return (salt + pwdhash) # return bytes + + +def verify_pass(provided_password, stored_password): + """Verify a stored password against one provided by user""" + + stored_password = stored_password.decode('ascii') + salt = stored_password[:64] + stored_password = stored_password[64:] + pwdhash = hashlib.pbkdf2_hmac('sha512', + provided_password.encode('utf-8'), + salt.encode('ascii'), + 100000) + pwdhash = binascii.hexlify(pwdhash).decode('ascii') + return pwdhash == stored_password diff --git a/apps/config.py b/apps/config.py new file mode 100644 index 0000000..5f0e1e3 --- /dev/null +++ b/apps/config.py @@ -0,0 +1,52 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +import os + + +class Config(object): + + basedir = os.path.abspath(os.path.dirname(__file__)) + + # Set up the App SECRET_KEY + # SECRET_KEY = config('SECRET_KEY' , default='S#perS3crEt_007') + SECRET_KEY = os.getenv('SECRET_KEY', 'S#perS3crEt_007') + + # This will create a file in FOLDER + SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'db.sqlite3') + SQLALCHEMY_TRACK_MODIFICATIONS = False + + # Assets Management + ASSETS_ROOT = os.getenv('ASSETS_ROOT', '/static/assets') + + +class ProductionConfig(Config): + DEBUG = False + + # Security + SESSION_COOKIE_HTTPONLY = True + REMEMBER_COOKIE_HTTPONLY = True + REMEMBER_COOKIE_DURATION = 3600 + + # PostgreSQL database + SQLALCHEMY_DATABASE_URI = '{}://{}:{}@{}:{}/{}'.format( + os.getenv('DB_ENGINE' , 'mysql'), + os.getenv('DB_USERNAME' , 'appseed_db_usr'), + os.getenv('DB_PASS' , 'pass'), + os.getenv('DB_HOST' , 'localhost'), + os.getenv('DB_PORT' , 3306), + os.getenv('DB_NAME' , 'appseed_db') + ) + + +class DebugConfig(Config): + DEBUG = True + + +# Load all possible configurations +config_dict = { + 'Production': ProductionConfig, + 'Debug' : DebugConfig +} diff --git a/apps/home/__init__.py b/apps/home/__init__.py new file mode 100644 index 0000000..dec76db --- /dev/null +++ b/apps/home/__init__.py @@ -0,0 +1,12 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from flask import Blueprint + +blueprint = Blueprint( + 'home_blueprint', + __name__, + url_prefix='' +) diff --git a/apps/home/forms.py b/apps/home/forms.py new file mode 100644 index 0000000..906133f --- /dev/null +++ b/apps/home/forms.py @@ -0,0 +1,14 @@ +from flask_wtf import FlaskForm +from wtforms import FloatField, IntegerField +from wtforms.validators import NumberRange, DataRequired + + +class MREForm(FlaskForm): + consumer_fico = IntegerField('FICO Score', id='consumer_fico', + validators=[DataRequired(), + NumberRange(300, 850, message='Fico scores must be between 300 and 850')]) + down_payment_percent = FloatField('Down Payment %', id='down_payment_percent', + validators=[DataRequired(), + NumberRange(0.0, 99.99, message='Down Payment must be between 0 and 99.99')]) + home_value = FloatField('Home Price', id='home_value', validators=[DataRequired()]) + diff --git a/apps/home/routes.py b/apps/home/routes.py new file mode 100644 index 0000000..3fc69ff --- /dev/null +++ b/apps/home/routes.py @@ -0,0 +1,79 @@ +# -*- encoding: utf-8 -*- +""" +Copyright (c) 2019 - present AppSeed.us +""" + +from apps.home import blueprint +from apps.home.forms import MREForm +from apps.MRE import compute_mre, ConsumerNotEligibleError +from flask import render_template, request, flash, jsonify +from flask_login import login_required +from jinja2 import TemplateNotFound + + +@blueprint.route('/') +@blueprint.route('/index') +def index(): + mre_form = MREForm(request.form) + # if request.method == 'POST' and mre_form.validate_on_submit(): + # try: + # mre = compute_mre(mre_form.home_value, mre_form.down_payment_percent, mre_form.consumer_fico) + # return render_template('home/mre.html', form=mre_form) + # + # except ConsumerNotEligibleError as ex: + # flash(f'Error: {ex.reason}') + + return render_template('home/mre.html', form=mre_form) + + +@blueprint.route('/compute_mre', methods=['GET']) +def ajax_compute_mre(): + home_value = float(request.args.get('home_value').replace(',', '')) + consumer_fico = int(request.args.get('consumer_fico')) + down_payment_percent = float(request.args.get('down_payment_percent')) + try: + result = compute_mre(home_value, down_payment_percent, consumer_fico) + except ConsumerNotEligibleError as ex: + return jsonify({'error': ex.reason}) + + print(f'Computed MRE: {result}') + + return jsonify({'computed_mre': result}) + + +@blueprint.route('/