├── diag_app ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0003_notification.py │ ├── 0004_auto_20170330_1221.py │ ├── 0002_auto_20170111_1403.py │ └── 0001_initial.py ├── static │ ├── js │ │ ├── profile.js │ │ ├── current_url.js │ │ ├── helpers │ │ │ └── format-time.js │ │ ├── getCookie.js │ │ ├── bike_detail.js │ │ ├── create_account.js │ │ ├── problem_list.js │ │ ├── main.js │ │ ├── problem_detail.js │ │ └── script.js │ ├── css │ │ ├── _system-problem-list.scss │ │ ├── _variables.scss │ │ ├── _services.scss │ │ ├── _mixins.scss │ │ ├── sass │ │ ├── remodal.css │ │ ├── _problem-list.scss │ │ ├── _bikedetails.scss │ │ ├── _aboutus.scss │ │ ├── _main.scss │ │ ├── _createaccount.scss │ │ ├── _base.scss │ │ ├── _profile.scss │ │ ├── _problem-detail.scss │ │ ├── style.scss │ │ ├── remodal-default-theme.css │ │ └── _index.scss │ ├── images │ │ ├── test.png │ │ ├── favicon.ico │ │ ├── BikeMDLogo.png │ │ ├── search-icon.png │ │ ├── transport-3.png │ │ ├── logo-template.png │ │ ├── transpsort-3.jpg │ │ ├── about-background.jpg │ │ ├── background-logo.png │ │ ├── index-background.jpg │ │ ├── main-background.jpg │ │ ├── bikedetails-background.jpg │ │ ├── media-index-background.jpg │ │ └── register-page-background-image.png │ └── .sass-cache │ │ ├── 055012ce12ad48c5e6d661a124c470b766c0ba78 │ │ ├── _index.scssc │ │ ├── _main.scssc │ │ ├── style.scssc │ │ ├── _aboutus.scssc │ │ ├── _mixins.scssc │ │ ├── _profile.scssc │ │ ├── _variables.scssc │ │ ├── _bikedetails.scssc │ │ ├── _problem-list.scssc │ │ ├── _createaccount.scssc │ │ ├── _problem-detail.scssc │ │ └── _system-problem-list.scssc │ │ ├── 0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62 │ │ ├── _index.scssc │ │ ├── _main.scssc │ │ ├── style.scssc │ │ ├── _aboutus.scssc │ │ ├── _mixins.scssc │ │ ├── _profile.scssc │ │ ├── _variables.scssc │ │ ├── _bikedetails.scssc │ │ ├── _problem-list.scssc │ │ ├── _createaccount.scssc │ │ ├── _problem-detail.scssc │ │ ├── about.scssc │ │ └── _system-problem-list.scssc │ │ └── 9adae882a639de1d92c92bc901b39cbd1e85dd20 │ │ ├── _index.scssc │ │ ├── _main.scssc │ │ ├── style.scssc │ │ ├── _aboutus.scssc │ │ ├── _mixins.scssc │ │ ├── _profile.scssc │ │ ├── _variables.scssc │ │ ├── _bikedetails.scssc │ │ ├── _problem-list.scssc │ │ ├── _createaccount.scssc │ │ ├── _problem-detail.scssc │ │ ├── _problem-detail-solution.scssc │ │ └── _system-problem-list.scssc ├── templates │ ├── build_templates │ │ ├── base.html │ │ ├── index.html │ │ ├── create_account.html │ │ ├── problem_listing.html │ │ ├── test_model.html │ │ ├── profile.html │ │ ├── bike_detail.html │ │ ├── load.html │ │ └── problem_detail.html │ ├── notifications.html │ ├── profile.html │ ├── services.html │ ├── registration │ │ └── login.html │ ├── bike_detail.html │ ├── problem_list.html │ ├── about_us.html │ ├── create_account.html │ ├── problem_detail.html │ ├── main.html │ └── base.html ├── admin.py ├── tests.py ├── apps.py ├── permissions.py ├── urls.py ├── forms.py ├── models.py ├── serializers.py └── views.py ├── diag_project ├── __init__.py ├── wsgi.py ├── urls.py ├── settings.py └── settings_example.py ├── runtime.txt ├── Procfile ├── migrate_data ├── system.csv ├── brand.csv └── migrate_data.py ├── .gitignore ├── requirements.txt ├── manage.py └── README.md /diag_app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /diag_project/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.5.2 2 | -------------------------------------------------------------------------------- /diag_app/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /diag_app/static/js/profile.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /diag_app/templates/build_templates/base.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn diag_project.wsgi --log-file - 2 | -------------------------------------------------------------------------------- /migrate_data/system.csv: -------------------------------------------------------------------------------- 1 | Chassis 2 | Electrical 3 | Fuel 4 | Mechanical 5 | -------------------------------------------------------------------------------- /diag_app/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /diag_app/static/css/_system-problem-list.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | -------------------------------------------------------------------------------- /diag_app/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /diag_app/static/images/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/test.png -------------------------------------------------------------------------------- /diag_app/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/favicon.ico -------------------------------------------------------------------------------- /migrate_data/brand.csv: -------------------------------------------------------------------------------- 1 | Honda 2 | Suzuki 3 | Yamaha 4 | Triumph 5 | Harley Davidson 6 | Ducati 7 | Victory 8 | Kawasaki 9 | -------------------------------------------------------------------------------- /diag_app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DiagAppConfig(AppConfig): 5 | name = 'diag_app' 6 | -------------------------------------------------------------------------------- /diag_app/static/images/BikeMDLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/BikeMDLogo.png -------------------------------------------------------------------------------- /diag_app/static/images/search-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/search-icon.png -------------------------------------------------------------------------------- /diag_app/static/images/transport-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/transport-3.png -------------------------------------------------------------------------------- /diag_app/static/images/logo-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/logo-template.png -------------------------------------------------------------------------------- /diag_app/static/images/transpsort-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/transpsort-3.jpg -------------------------------------------------------------------------------- /diag_app/static/images/about-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/about-background.jpg -------------------------------------------------------------------------------- /diag_app/static/images/background-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/background-logo.png -------------------------------------------------------------------------------- /diag_app/static/images/index-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/index-background.jpg -------------------------------------------------------------------------------- /diag_app/static/images/main-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/main-background.jpg -------------------------------------------------------------------------------- /diag_app/static/images/bikedetails-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/bikedetails-background.jpg -------------------------------------------------------------------------------- /diag_app/static/images/media-index-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/media-index-background.jpg -------------------------------------------------------------------------------- /diag_app/static/images/register-page-background-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/images/register-page-background-image.png -------------------------------------------------------------------------------- /diag_app/static/css/_variables.scss: -------------------------------------------------------------------------------- 1 | $charcoal: #313237; 2 | $seafoam: #a8bdba; 3 | $golden: #ba973a; 4 | $teal: #8ac3c3; 5 | $darkteal: #87b0aa; 6 | $overlaylt: #cab298; 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_store 2 | .direnv 3 | __pycache__ 4 | .cache 5 | scrap.py 6 | .envrc 7 | .zshrc 8 | .map 9 | .css.map 10 | style.css.map 11 | settings.py 12 | development_photos 13 | Procfile 14 | -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_index.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_index.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_main.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_main.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/style.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/style.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_index.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_index.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_main.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_main.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/style.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/style.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_index.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_index.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_main.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_main.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/style.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/style.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_aboutus.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_aboutus.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_mixins.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_mixins.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_profile.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_profile.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_aboutus.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_aboutus.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_mixins.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_mixins.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_profile.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_profile.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_aboutus.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_aboutus.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_mixins.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_mixins.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_profile.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_profile.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_variables.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_variables.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_variables.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_variables.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_variables.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_variables.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_bikedetails.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_bikedetails.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_problem-list.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_problem-list.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_bikedetails.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_bikedetails.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_problem-list.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_problem-list.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_bikedetails.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_bikedetails.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_problem-list.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_problem-list.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_createaccount.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_createaccount.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_problem-detail.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_problem-detail.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_createaccount.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_createaccount.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_problem-detail.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_problem-detail.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_createaccount.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_createaccount.scssc -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_problem-detail.scssc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bike-md/bike_md/HEAD/diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_problem-detail.scssc -------------------------------------------------------------------------------- /diag_app/templates/build_templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Main Page 6 | 7 | 8 |

Index Page

9 | 10 | 11 | -------------------------------------------------------------------------------- /diag_project/wsgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | from django.core.wsgi import get_wsgi_application 3 | from whitenoise.django import DjangoWhiteNoise 4 | 5 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diag_project.settings") 6 | 7 | application = get_wsgi_application() 8 | application = DjangoWhiteNoise(application) 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | dj-database-url==0.4.1 2 | Django==1.10.4 3 | django-crispy-forms==1.6.1 4 | django-filter==1.0.1 5 | django-model-utils==2.6.1 6 | django-notifications-hq==1.2 7 | djangorestframework==3.5.3 8 | gunicorn==19.6.0 9 | jsonfield==2.0.1 10 | psycopg2==2.6.2 11 | pytz==2016.10 12 | whitenoise==3.2.2 13 | -------------------------------------------------------------------------------- /diag_app/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework import permissions 2 | 3 | 4 | class IsStaffOrTargetUser(permissions.BasePermission): 5 | 6 | def has_permission(self, request, view): 7 | return view.action == 'retrieve' or request.user.is_staff 8 | 9 | def has_object_permission(self, request, view, obj): 10 | return request.user.is_staff or obj == request.user 11 | -------------------------------------------------------------------------------- /diag_app/static/js/current_url.js: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////// 2 | // function: currentUrl 3 | // parameters: none 4 | // description: gets current url from browser and calls showBike() 5 | ////////////////////////////////////////////////////////////////// 6 | function currentURL() { 7 | var url = window.location.href; 8 | showProblem(url); 9 | } 10 | -------------------------------------------------------------------------------- /diag_app/static/css/_services.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .servicespagebody { 5 | background-image: 6 | linear-gradient( 7 | rgba(black, 0.75), 8 | rgba(black, 0.75) 9 | ), 10 | url("../images/index-background.jpg"); 11 | background-size:cover; 12 | margin:0; 13 | } 14 | .sabout-us-details { 15 | width: auto; 16 | height: auto; 17 | background-color: #2B7F7B; 18 | margin: 1%; 19 | } 20 | -------------------------------------------------------------------------------- /diag_app/templates/build_templates/create_account.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Create Account 6 | 7 | 8 |

Create New Account

9 |
10 | {%csrf_token%} 11 | {{user_form}} 12 | {{tech_form}} 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/about.scssc: -------------------------------------------------------------------------------- 1 | 3.4.22 (Selective Steve) 2 | da39a3ee5e6b4b0d3255bfef95601890afd80709 3 | o:Sass::Tree::RootNode :@children[:@filename0: @options{:@templateI":ET: 4 | @linei:@source_rangeo:Sass::Source::Range :@start_poso:Sass::Source::Position; i: @offseti: @end_poso;; i;i: 5 | @fileI"css/about.scss; 6 | T:@importero: Sass::Importers::Filesystem: 7 | @rootI"2/Users/Eloisa/Desktop/bike_md/diag_app/static; 8 | T:@real_rootI"2/Users/Eloisa/Desktop/bike_md/diag_app/static; 9 | T:@same_name_warningso:Set: 10 | @hash{ -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_problem-detail-solution.scssc: -------------------------------------------------------------------------------- 1 | 3.4.22 (Selective Steve) 2 | da39a3ee5e6b4b0d3255bfef95601890afd80709 3 | o:Sass::Tree::RootNode :@children[:@filename0: @options{:@templateI":ET: 4 | @linei:@source_rangeo:Sass::Source::Range :@start_poso:Sass::Source::Position; i: @offseti: @end_poso;; i;i: 5 | @fileI"f/Users/terrimckeown/Desktop/bike_MD_app/bike_md/diag_app/static/css/_problem-detail-solution.scss; 6 | T:@importero: Sass::Importers::Filesystem: 7 | @rootI"D/Users/terrimckeown/Desktop/bike_MD_app/bike_md/diag_app/static; 8 | T:@real_rootI"D/Users/terrimckeown/Desktop/bike_MD_app/bike_md/diag_app/static; 9 | T:@same_name_warningso:Set: 10 | @hash{ -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diag_project.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /diag_app/static/css/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin smallfont{ 2 | font-size:1rem; 3 | font-family:gill sans; 4 | text-transform:uppercase; 5 | } 6 | 7 | @mixin smallregularfont { 8 | font-size:1.2rem; 9 | font-family:gill sans; 10 | text-transform:uppercase; 11 | } 12 | @mixin regularfont{ 13 | font-size:1.5rem; 14 | font-family:gill sans; 15 | text-transform:uppercase; 16 | } 17 | 18 | @mixin regularmedfont{ 19 | font-size:1.6rem; 20 | font-family:gill sans; 21 | text-transform:uppercase; 22 | 23 | } 24 | 25 | @mixin mediumfont{ 26 | font-size:2rem; 27 | font-family:gill sans; 28 | text-transform:uppercase; 29 | font-weight: normal; 30 | 31 | } 32 | 33 | @mixin largefont{ 34 | font-size:3rem; 35 | font-family:gill sans; 36 | text-transform:uppercase; 37 | 38 | } 39 | 40 | @mixin verticalcenter{ 41 | position:relative; 42 | top:50%; 43 | transform:translateY(-50%); 44 | } 45 | -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/9adae882a639de1d92c92bc901b39cbd1e85dd20/_system-problem-list.scssc: -------------------------------------------------------------------------------- 1 | 3.4.22 (Selective Steve) 2 | a9615ec117227d2b39c3fe67a85c5ddb38b3b789 3 | o:Sass::Tree::RootNode :@children[o:Sass::Tree::ImportNode :@imported_filenameI"variables:ET;[:@filename0: @options{:@template0: 4 | @linei:@source_rangeo:Sass::Source::Range :@start_poso:Sass::Source::Position; i: @offseti: @end_poso;; i;i: 5 | @fileI""css/_system-problem-list.scss; T:@importero: Sass::Importers::Filesystem: 6 | @rootI"D/Users/terrimckeown/Desktop/bike_MD_app/bike_md/diag_app/static; T:@real_rootI"D/Users/terrimckeown/Desktop/bike_MD_app/bike_md/diag_app/static; T:@same_name_warningso:Set: 7 | @hash{:@imported_file0o; ;I" mixins; T;[; 8 | 0; @ 9 | ; 0; i;o; ;o;; i;i;o;; i;i;@;@;0; 10 | 0; @ 11 | ; I",@import "variables"; 12 | @import "mixins"; 13 | ; T; i;o; ;o;; i;i;o;; i;i;@;@:@has_childrenT -------------------------------------------------------------------------------- /diag_app/migrations/0003_notification.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-03-30 00:28 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('diag_app', '0002_auto_20170111_1403'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Notification', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('message', models.CharField(max_length=20)), 21 | ('posted', models.DateTimeField(auto_now=True)), 22 | ('tech', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='techs', to='diag_app.Tech')), 23 | ], 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/0b6692ef5a7bad6c8bd4c59ea4ee7dc4d4947e62/_system-problem-list.scssc: -------------------------------------------------------------------------------- 1 | 3.4.22 (Selective Steve) 2 | a9615ec117227d2b39c3fe67a85c5ddb38b3b789 3 | o:Sass::Tree::RootNode :@children[o:Sass::Tree::ImportNode :@imported_filenameI"variables:ET;[:@filename0: @options{:@template0: 4 | @linei:@source_rangeo:Sass::Source::Range :@start_poso:Sass::Source::Position; i: @offseti: @end_poso;; i;i: 5 | @fileI"P/Users/Eloisa/Desktop/bike_md/diag_app/static/css/_system-problem-list.scss; T:@importero: Sass::Importers::Filesystem: 6 | @rootI"2/Users/Eloisa/Desktop/bike_md/diag_app/static; T:@real_rootI"2/Users/Eloisa/Desktop/bike_md/diag_app/static; T:@same_name_warningso:Set: 7 | @hash{:@imported_file0o; ;I" mixins; T;[; 8 | 0; @ 9 | ; 0; i;o; ;o;; i;i;o;; i;i;@;@;0; 10 | 0; @ 11 | ; I",@import "variables"; 12 | @import "mixins"; 13 | ; T; i;o; ;o;; i;i;o;; i;i;@;@:@has_childrenT -------------------------------------------------------------------------------- /diag_app/migrations/0004_auto_20170330_1221.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-03-30 12:21 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('diag_app', '0003_notification'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='notification', 18 | name='commit', 19 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='commit', to='diag_app.Commit'), 20 | ), 21 | migrations.AddField( 22 | model_name='notification', 23 | name='solution', 24 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='solution', to='diag_app.Solution'), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /diag_app/static/.sass-cache/055012ce12ad48c5e6d661a124c470b766c0ba78/_system-problem-list.scssc: -------------------------------------------------------------------------------- 1 | 3.4.22 (Selective Steve) 2 | a9615ec117227d2b39c3fe67a85c5ddb38b3b789 3 | o:Sass::Tree::RootNode :@children[o:Sass::Tree::ImportNode :@imported_filenameI"variables:ET;[:@filename0: @options{:@template0: 4 | @linei:@source_rangeo:Sass::Source::Range :@start_poso:Sass::Source::Position; i: @offseti: @end_poso;; i;i: 5 | @fileI"u/Users/josiahsdubose/Desktop/tiy/python/final_project/vehicle_diag/diag_app/static/css/_system-problem-list.scss; T:@importero: Sass::Importers::Filesystem: 6 | @rootI"W/Users/josiahsdubose/Desktop/tiy/python/final_project/vehicle_diag/diag_app/static; T:@real_rootI"W/Users/josiahsdubose/Desktop/tiy/python/final_project/vehicle_diag/diag_app/static; T:@same_name_warningso:Set: 7 | @hash}F:@imported_file0o; ;I" mixins; T;[; 8 | 0; @ 9 | ; 0; i;o; ;o;; i;i;o;; i;i;@;@;0; 10 | 0; @ 11 | ; I",@import "variables"; 12 | @import "mixins"; 13 | ; T; i;o; ;o;; i;i;o;; i;i;@;@:@has_childrenT -------------------------------------------------------------------------------- /diag_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from . import views 3 | import diag_app 4 | from django.views.generic import TemplateView 5 | 6 | 7 | urlpatterns = [ 8 | url((r'^$'), views.main_page, name='main'), 9 | url((r'^problem_list/([A-Za-z0-9]+)/([0-9]+)'), views.problem_list, name='problem_list'), 10 | url((r'^model_detail/([0-9]+)'), views.model_detail, name='model_detail'), 11 | url((r'^problem_detail/([0-9]+)'), views.problem_detail, name='problem_detail'), 12 | url((r'^profile/$'), views.profile, name='profile'), 13 | url(r'^create_account/$', views.create_account, name='create_account'), 14 | url((r'^about/$'), views.about_us, name='about'), 15 | url((r'^notifications/$'), views.notifications, name='notifications'), 16 | 17 | ### developemnt urls ### 18 | url((r'^problems/'),TemplateView.as_view(template_name="build_templates/problem_listing.html")), 19 | url((r'^model_details/([0-9]+)'), TemplateView.as_view(template_name='build_templates/bike_detail.html')), 20 | url((r'^problem_details/([0-9]+)'), TemplateView.as_view(template_name='build_templates/problem_detail.html')), 21 | ] 22 | -------------------------------------------------------------------------------- /diag_app/templates/build_templates/problem_listing.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | Problem list 7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /diag_app/static/js/helpers/format-time.js: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Helper: formatTime 3 | // parameters: timestamp 4 | // description: Formats timestamps to make them pretty and more human readable. 5 | // return: String of month/day/year 6 | //////////////////////////////////////////////////////////////////////////////// 7 | Handlebars.registerHelper('formatTime', function (posted) { 8 | var time = posted.replace('T', ':'); 9 | var date = time.split(":")[0]; 10 | var year = Number(date.split("-")[0]); 11 | var month = Number(date.split("-")[1]); 12 | var day = Number(date.split("-")[2]); 13 | var months = { 14 | "January": 1, 15 | "February ": 2, 16 | "March": 3, 17 | "April": 4, 18 | "May": 5, 19 | "June": 6, 20 | "July": 7, 21 | "August": 8, 22 | "September": 9, 23 | "October": 10, 24 | "November": 11, 25 | "December": 12, 26 | }; 27 | for(var i in months){ 28 | if(month == months[i]){ 29 | month = i; 30 | } 31 | } 32 | return month + " " + day + " " + year; 33 | }) 34 | -------------------------------------------------------------------------------- /diag_app/templates/build_templates/test_model.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | test 7 | 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /diag_app/static/js/getCookie.js: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////////// 2 | // function: getCookie 3 | // paramters: csrftoken 4 | // returns: cookie 5 | /////////////////////////////////////////////////////////////////////////////////// 6 | function getCookie(name) { 7 | var cookieValue = null; 8 | if (document.cookie && document.cookie !== '') { 9 | var cookies = document.cookie.split(';'); 10 | for (var i = 0; i < cookies.length; i++) { 11 | var cookie = jQuery.trim(cookies[i]); 12 | if (cookie.substring(0, name.length + 1) === (name + '=')) { 13 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 14 | break; 15 | } 16 | } 17 | } 18 | return cookieValue; 19 | } 20 | 21 | 22 | var csrftoken = getCookie('csrftoken'); 23 | function csrfSafeMethod(method) { 24 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 25 | } 26 | 27 | 28 | $.ajaxSetup({ 29 | beforeSend: function(xhr, settings) { 30 | if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 31 | xhr.setRequestHeader("X-CSRFToken", csrftoken); 32 | } 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /migrate_data/migrate_data.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | import csv 3 | 4 | def load_brands(apps, schema_editor): 5 | with open("brand.csv", "r") as f: 6 | dict_reader = csv.DictReader(f, fieldnames= ['name']) 7 | Brand = apps.get_model("diag_app", "Brand") 8 | for row in dict_reader: 9 | brand_object = Brand(**row) 10 | brand_object.save() 11 | 12 | 13 | def load_systems(apps, schema_editor): 14 | with open("system.csv", "r") as f: 15 | dict_reader = csv.DictReader(f, fieldnames= ['name']) 16 | System = apps.get_model("diag_app", "System") 17 | for row in dict_reader: 18 | system_object = System(**row) 19 | system_object.save() 20 | 21 | 22 | def load_models(apps, schema_editor): 23 | with open("model.csv", "r") as f: 24 | dict_reader = csv.DictReader(f, fieldnames= ['brand_id','name','year']) 25 | Model = apps.get_model("diag_app", "Model") 26 | for row in dict_reader: 27 | model_object = Model(**row) 28 | model_object.save() 29 | 30 | 31 | 32 | 33 | operations = [ 34 | migrations.RunPython(load_brands), 35 | migrations.RunPython(load_systems), 36 | migrations.RunPython(load_models), 37 | ] 38 | -------------------------------------------------------------------------------- /diag_app/templates/notifications.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Notifications 13 | 14 | 15 | 16 | 17 |
18 |

About Bike MD

19 |
20 | 21 |
22 |
23 |
24 | 25 |

Bike MD This feature is still under construction. 26 |

27 |
28 | 29 |
30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /diag_app/forms.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.contrib.auth import authenticate 3 | from django import forms 4 | from .models import Tech 5 | 6 | 7 | class UserForm(forms.ModelForm): 8 | password = forms.CharField(widget=forms.PasswordInput) 9 | 10 | class Meta: 11 | model = User 12 | fields = ['username', 'password', 'email'] 13 | 14 | 15 | class TechForm(forms.ModelForm): 16 | 17 | class Meta: 18 | model = Tech 19 | fields = ['experience', 'job_title', 'shop'] 20 | 21 | 22 | class LoginForm(forms.Form): 23 | username = forms.CharField(max_length=40, required=True) 24 | password = forms.CharField(widget=forms.PasswordInput, required=True) 25 | 26 | def clean(self): 27 | username = self.cleaned_data.get('username') 28 | password = self.cleaned_data.get('password') 29 | user = authenticate(username=username, password=password) 30 | if not user or not user.is_active: 31 | raise forms.ValidationError("Sorry, that login was invalid. Please try again.") 32 | return self.cleaned_data 33 | 34 | def login(self, request): 35 | username = self.cleaned_data.get('username') 36 | password = self.cleaned_data.get('password') 37 | user = authenticate(username=username, password=password) 38 | return user 39 | -------------------------------------------------------------------------------- /diag_app/templates/profile.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Notifications 13 | 14 | 15 | 16 | 17 |
18 |

About Bike MD

19 |
20 | 21 |
22 |
23 |
24 | 25 |

26 | Bike MD This feature is still under construction. 27 |

28 |
29 | 30 |
31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /diag_project/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url, include 2 | from django.contrib import admin 3 | from django.contrib.auth import views as auth_views 4 | from rest_framework import routers 5 | from diag_app import views 6 | from django.views.generic import TemplateView 7 | 8 | 9 | router = routers.DefaultRouter() 10 | router.register(r'accounts', views.UserView, 'list') 11 | router.register(r'systems', views.SystemViewSet) 12 | router.register(r'brands', views.BrandViewSet) 13 | router.register(r'models', views.ModelViewSet) 14 | router.register(r'get-problems', views.ProblemGetViewSet) 15 | router.register(r'post-problems', views.ProblemPostViewSet) 16 | router.register(r'get-solutions', views.SolutionGetViewSet) 17 | router.register(r'post-solutions', views.SolutionPostViewSet) 18 | router.register(r'votes', views.VoteViewSet) 19 | router.register(r'get-techs', views.TechGetViewSet) 20 | router.register(r'post-techs', views.TechPostViewSet) 21 | router.register(r'ratings', views.RatingViewSet) 22 | router.register(r'notifications', views.NotificationViewSet) 23 | 24 | 25 | urlpatterns = [ 26 | url(r'^api/', include(router.urls)), 27 | url(r'^', include('diag_app.urls')), 28 | url(r'^admin/', admin.site.urls), 29 | url(r'^login/$', views.login_user, name='login'), 30 | url(r'^logout/$', auth_views.logout, {'next_page': 'login'}, 31 | name='logout'), 32 | ] 33 | -------------------------------------------------------------------------------- /diag_app/static/css/sass: -------------------------------------------------------------------------------- 1 | /* 2 | Errno::ENOENT: No such file or directory - sass 3 | 4 | Backtrace: 5 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:484:in `read' 6 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:484:in `update_stylesheet' 7 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:215:in `block in update_stylesheets' 8 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:209:in `each' 9 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:209:in `update_stylesheets' 10 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:294:in `watch' 11 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin.rb:109:in `method_missing' 12 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/exec/sass_scss.rb:360:in `watch_or_update' 13 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/exec/sass_scss.rb:51:in `process_result' 14 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/exec/base.rb:52:in `parse' 15 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/exec/base.rb:19:in `parse!' 16 | /Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/bin/sass:13:in `' 17 | /usr/local/bin/sass:23:in `load' 18 | /usr/local/bin/sass:23:in `
' 19 | */ 20 | body:before { 21 | white-space: pre; 22 | font-family: monospace; 23 | content: "Errno::ENOENT: No such file or directory - sass"; } 24 | -------------------------------------------------------------------------------- /diag_app/templates/services.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
  • 15 | 16 |
    17 | 18 | 19 | 20 | 25 |
    26 | 27 |
    28 |

    Services

    29 |

    30 | 31 | 32 |
    33 | 34 | Services Broken Down 35 | 36 |
    37 | 38 | 39 | 40 | 41 | 42 |
    43 |

    Copyright Bike MD 2017. All rights reserved.

    44 |
    45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /diag_app/migrations/0002_auto_20170111_1403.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-01-11 14:03 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | import csv 7 | 8 | def load_brands(apps, schema_editor): 9 | with open("migrate_data/brand.csv", "r") as f: 10 | dict_reader = csv.DictReader(f, fieldnames= ['name']) 11 | Brand = apps.get_model("diag_app", "Brand") 12 | for row in dict_reader: 13 | brand_object = Brand(**row) 14 | brand_object.save() 15 | 16 | 17 | def load_systems(apps, schema_editor): 18 | with open("migrate_data/system.csv", "r") as f: 19 | dict_reader = csv.DictReader(f, fieldnames= ['name']) 20 | System = apps.get_model("diag_app", "System") 21 | for row in dict_reader: 22 | system_object = System(**row) 23 | system_object.save() 24 | 25 | 26 | def load_models(apps, schema_editor): 27 | with open("migrate_data/model.csv", "r") as f: 28 | dict_reader = csv.DictReader(f, fieldnames= ['brand_id','name','year']) 29 | Model = apps.get_model("diag_app", "Model") 30 | for row in dict_reader: 31 | model_object = Model(**row) 32 | model_object.save() 33 | 34 | 35 | class Migration(migrations.Migration): 36 | 37 | dependencies = [ 38 | ('diag_app', '0001_initial'), 39 | ] 40 | 41 | operations = [ 42 | migrations.RunPython(load_brands), 43 | migrations.RunPython(load_systems), 44 | migrations.RunPython(load_models), 45 | ] 46 | -------------------------------------------------------------------------------- /diag_app/templates/registration/login.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | 7 | 8 | 9 | Login 10 | 11 | 12 | 13 | 14 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /diag_app/templates/build_templates/profile.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | Profile 7 | 8 | 9 | 10 | 11 |
    12 |
    13 |
    14 | 15 | 16 | 23 | 24 | 34 | 35 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /diag_app/static/js/bike_detail.js: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////// 2 | // Set up Dom 3 | //////////////////////////////////////////// 4 | function setup() { 5 | //get url for the ajax call 6 | var url = window.location.href; 7 | showBike(url); 8 | } 9 | setup(); 10 | ////////////////////////////////////////////////////////////////// 11 | // function: showBike 12 | // parameters: URL of current page 13 | // description: preforms ajax call to get info for curent bike. 14 | ////////////////////////////////////////////////////////////////// 15 | function showBike(url) { 16 | var id = url.split('/'); 17 | id = id[4]; 18 | var url = '/api/models/' + id; 19 | $.ajax({ 20 | url: url, 21 | type: 'GET', 22 | }).done(function(results) { 23 | var source = $("#bike-template").html(); 24 | var template = Handlebars.compile(source); 25 | var html = template(results); 26 | $("#bikeSpecs").append(html); 27 | loadSystemModals(results.id); 28 | }); 29 | } 30 | ////////////////////////////////////////////////////////////////// 31 | // function: loadSystemModals 32 | // parameters: model ID 33 | // description: load system modals on bike detail page. 34 | ////////////////////////////////////////////////////////////////// 35 | function loadSystemModals(modelID) { 36 | var model = modelID; 37 | $.ajax({ 38 | url: '/api/systems/', 39 | type: 'GET', 40 | }).done(function(results) { 41 | var system = results.results; 42 | var list = {}; 43 | var index = 0; 44 | $.each(system, function() { 45 | list[index] = { 46 | system : system[index], 47 | model : model 48 | }; 49 | index++; 50 | }); 51 | var source = $('#system-template').html(); 52 | var template = Handlebars.compile(source); 53 | var html = template(list); 54 | $('#system').append(html); 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /diag_app/static/css/remodal.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Remodal - v1.1.0 3 | * Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. 4 | * http://vodkabears.github.io/remodal/ 5 | * 6 | * Made by Ilya Makarov 7 | * Under MIT License 8 | */ 9 | 10 | /* ========================================================================== 11 | Remodal's necessary styles 12 | ========================================================================== */ 13 | 14 | /* Hide scroll bar */ 15 | 16 | html.remodal-is-locked { 17 | overflow: hidden; 18 | 19 | -ms-touch-action: none; 20 | touch-action: none; 21 | } 22 | 23 | /* Anti FOUC */ 24 | 25 | .remodal, 26 | [data-remodal-id] { 27 | display: none; 28 | } 29 | 30 | /* Necessary styles of the overlay */ 31 | 32 | .remodal-overlay { 33 | position: fixed; 34 | z-index: 9999; 35 | top: -5000px; 36 | right: -5000px; 37 | bottom: -5000px; 38 | left: -5000px; 39 | 40 | display: none; 41 | } 42 | 43 | /* Necessary styles of the wrapper */ 44 | 45 | .remodal-wrapper { 46 | position: fixed; 47 | z-index: 10000; 48 | top: 0; 49 | right: 0; 50 | bottom: 0; 51 | left: 0; 52 | 53 | display: none; 54 | overflow: auto; 55 | 56 | text-align: center; 57 | 58 | -webkit-overflow-scrolling: touch; 59 | } 60 | 61 | .remodal-wrapper:after { 62 | display: inline-block; 63 | 64 | height: 100%; 65 | 66 | 67 | content: ""; 68 | } 69 | 70 | /* Fix iPad, iPhone glitches */ 71 | 72 | .remodal-overlay, 73 | .remodal-wrapper { 74 | -webkit-backface-visibility: hidden; 75 | backface-visibility: hidden; 76 | } 77 | 78 | /* Necessary styles of the modal dialog */ 79 | 80 | .remodal { 81 | position: relative; 82 | outline: none; 83 | 84 | -webkit-text-size-adjust: 100%; 85 | -ms-text-size-adjust: 100%; 86 | text-size-adjust: 100%; 87 | } 88 | 89 | .remodal-is-initialized { 90 | /* Disable Anti-FOUC */ 91 | display: inline-block; 92 | } 93 | -------------------------------------------------------------------------------- /diag_app/templates/bike_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {%load static%} 3 | {% block titlecontent %}Bike Detail{% endblock %} 4 | 5 | {% block bodycontent %} 6 | 7 | {% endblock %} 8 | 9 | {%block content%} 10 |

    Select System

    11 | 12 |
    13 | 14 | 15 |
    16 | 17 | 18 |
    19 |
    20 |
    21 | 22 | {% block footercontent %} 23 |

    Copyright Bike MD 2017. All rights reserved.

    24 | {% endblock %} 25 | 26 | 27 | 37 | 38 | 39 | 40 | 51 | {%endblock%} 52 | 53 | {% block jscontent %} 54 | 55 | {% endblock %} 56 | 57 | -------------------------------------------------------------------------------- /diag_app/templates/problem_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {%load static%} 3 | 4 | {% block titlecontent %} 5 | Problem List 6 | {% endblock %} 7 | 8 | {% block bodycontent %} 9 | 10 | {% endblock %} 11 | {%block content%} 12 |

    13 | 14 |
    15 | 16 | 17 |
    18 | 19 | {% block footercontent %} 20 |

    Copyright Bike MD 2017. All rights reserved.

    21 | {% endblock %} 22 | 23 | 41 | 42 | 49 | {% endblock %} 50 | 51 | {% block jscontent %} 52 | 53 | {% endblock %} 54 | 55 | -------------------------------------------------------------------------------- /diag_app/static/css/_problem-list.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .problist-page-title{ 5 | @include mediumfont; 6 | font-weight:500; 7 | color:white; 8 | text-align:left; 9 | margin-bottom:0; 10 | } 11 | 12 | .problist-page-title a{ 13 | text-decoration:none; 14 | color:white; 15 | } 16 | 17 | .problem-list-box { 18 | display:block; 19 | width:80%; 20 | background-color: white; 21 | height: 390px; 22 | margin:0 auto; 23 | overflow:auto; 24 | } 25 | 26 | .problist-problem-title{ 27 | @include smallregularfont; 28 | color: $charcoal; 29 | font-weight: 400; 30 | text-decoration:none; 31 | 32 | a{ 33 | text-decoration:none; 34 | color:$charcoal; 35 | } 36 | } 37 | 38 | h2, h5{ 39 | margin:0; 40 | } 41 | 42 | .problem-header{ 43 | @include mediumfont; 44 | text-align:center; 45 | background-color:$charcoal; 46 | color:white; 47 | margin:0; 48 | } 49 | 50 | .problist-date-time { 51 | @include smallfont; 52 | color: $charcoal; 53 | font-weight: 400; 54 | text-decoration: none; 55 | } 56 | 57 | .problist-posted-by { 58 | @include smallfont; 59 | color: $charcoal; 60 | font-weight: 400; 61 | text-decoration: none; 62 | padding-top: 0; 63 | } 64 | 65 | .problist-date-time, .problist-posted-by{ 66 | display: inline; 67 | } 68 | 69 | .problist-date-time{ 70 | @include smallfont; 71 | font-weight: 400; 72 | text-decoration: none; 73 | color:$teal; 74 | margin:0; 75 | 76 | } 77 | 78 | .tech-rating{ 79 | display:inline-block; 80 | border-left:1px solid black; 81 | border-right:1px solid black; 82 | padding:0 .5%; 83 | color:$charcoal; 84 | } 85 | 86 | .problem-list-box{ 87 | 88 | .unsolved-problem-link{ 89 | 90 | .problist-problem-container { 91 | background-color:gray; 92 | padding:1%; 93 | } 94 | 95 | .problist-problem-container:nth-child(odd){ 96 | background-color:white; 97 | } 98 | 99 | .problist-problem-container:hover{ 100 | background-color:rgba($teal, .3); 101 | transition: .5s; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /diag_app/static/js/create_account.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file needs to be re-worked. Currently the call to create a tech 3 | does not succeed. 4 | */ 5 | // Click events 6 | $("#createAccount").click(createUser) 7 | ////////////////////////////////////////////////////////////////////// 8 | // function: createUser 9 | // parameters: none 10 | // description: creates basic django user object with username, password, email. 11 | // Calls createTeach 12 | // return: none 13 | ////////////////////////////////////////////////////////////////////// 14 | function createUser(){ 15 | var userName = $("#newUsername").val() 16 | var userPassword = $("#userPassword").val() 17 | var emailAddress = $("#emailAddress").val() 18 | var context = { 19 | username: userName, 20 | password: userPassword, 21 | email: emailAddress, 22 | } 23 | $.ajax({ 24 | url: '/api/accounts/', 25 | type: 'POST', 26 | data: context 27 | }).done(function(results){ 28 | createTech(results.id) 29 | linkLogin() 30 | }) 31 | } 32 | ////////////////////////////////////////////////////////////////////// 33 | // function: createTech 34 | // parameters: user ID 35 | // description: creates tech: yrs experience, job title, current shop. 36 | // return: none 37 | ////////////////////////////////////////////////////////////////////// 38 | function createTech(id){ 39 | var yrsExperience = $("#yrsExperience").val(); 40 | var jobTitle = $("#jobTitle").val(); 41 | var currentShop = $("#currentShop").val(); 42 | var userId = id; 43 | var context = { 44 | experience: yrsExperience, 45 | job_title: jobTitle, 46 | shop: currentShop, 47 | user: userId, 48 | } 49 | $.ajax({ 50 | url: '/api/post-techs/', 51 | type: 'POST', 52 | data: context 53 | }).done(function(results){ 54 | }); 55 | } 56 | ////////////////////////////////////////////////////////////////////// 57 | // function: linkLogin 58 | // parameters: none 59 | // description: Attempt to redirect and login after user creation. Does not 60 | // work as intended in current form. 61 | // return: none 62 | ////////////////////////////////////////////////////////////////////// 63 | function linkLogin(){ 64 | url = '/login/'; 65 | window.location = url; 66 | } 67 | -------------------------------------------------------------------------------- /diag_app/static/css/_bikedetails.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | 5 | .bikedetailsbody{ 6 | background: 7 | url("../images/bikedetails-background.jpg"); 8 | background-size:cover; 9 | margin:0; 10 | } 11 | 12 | .background-white { 13 | height:auto; 14 | width: 80%; 15 | padding:1% 3% 3% 3%; 16 | box-sizing:border-box; 17 | margin: 0 10%; 18 | } 19 | 20 | .details-main-title{ 21 | @include mediumfont; 22 | font-weight:500; 23 | color:white; 24 | text-align:left; 25 | margin-left: 10%; 26 | } 27 | 28 | hr { 29 | margin-left: 10%; 30 | margin-right: 10%; 31 | } 32 | 33 | .specs{ 34 | @include regularfont; 35 | text-align:center; 36 | } 37 | 38 | .bikedetailsline{ 39 | @include regularfont; 40 | text-align:center; 41 | color:white; 42 | font-weight:500; 43 | } 44 | 45 | .bikedetailsline { 46 | display: inline; 47 | 48 | } 49 | 50 | .problem-title-modal{ 51 | @include regularfont; 52 | color: white; 53 | } 54 | 55 | 56 | .systems-background{ 57 | background-color:rgba(white, .40); 58 | padding:2% 0; 59 | margin:0 10%; 60 | } 61 | 62 | .systems{ 63 | text-align:center; 64 | 65 | a{ 66 | text-align:center; 67 | text-decoration:none; 68 | @include regularfont; 69 | color:$charcoal; 70 | margin:0; 71 | 72 | .circle{ 73 | height:170px; 74 | width:170px; 75 | margin:0 3%; 76 | display:inline-block; 77 | background-color:rgba(white, .9); 78 | border-radius:100%; 79 | padding-top:7.5%; 80 | box-sizing:border-box; 81 | } 82 | } 83 | 84 | .circle:hover{ 85 | color:white; 86 | background-color:rgba($teal, 1); 87 | transform:rotate(360deg); 88 | transition:.5s; 89 | } 90 | } 91 | 92 | .specs{ 93 | background-color:$charcoal; 94 | margin:0 10%; 95 | padding:.5% 0; 96 | } 97 | 98 | .bikedetails{ 99 | color:white; 100 | @include regularfont; 101 | text-transform:none; 102 | font-weight:300; 103 | display:inline; 104 | padding:0 2%; 105 | } 106 | 107 | .brand, .year, .model{ 108 | color:$teal; 109 | font-weight:100; 110 | text-transform:none; 111 | } 112 | 113 | .bikedetails-footer{ 114 | position:absolute; 115 | bottom:0; 116 | } 117 | -------------------------------------------------------------------------------- /diag_app/static/css/_aboutus.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .aboutbody{ 5 | background-image: 6 | linear-gradient( 7 | rgba($charcoal, 0.5), 8 | rgba($charcoal, 0.5) 9 | ), 10 | url("../images/index-background.jpg"); 11 | background-repeat: no-repeat; 12 | background-attachment: fixed; 13 | background-size: cover; 14 | margin:0; 15 | } 16 | 17 | 18 | //Color background for title// 19 | .goldendecor { 20 | background-color: $charcoal; 21 | width: 70%; 22 | padding-top: 1%; 23 | height: 70px; 24 | margin-left: 15%; 25 | margin-top: 7%; 26 | margin-right: 15%; 27 | } 28 | 29 | 30 | //Title 31 | .about-page-title { 32 | @include mediumfont; 33 | color: white; 34 | text-align: center; 35 | font-weight: 400; 36 | margin-top: 0%; 37 | margin-bottom: -7%; 38 | font-weight: 400; 39 | padding-top: 1%; 40 | padding-bottom: 1%; 41 | hr { 42 | margin-top: 0; 43 | margin-bottom: 0; 44 | margin-left: 5%; 45 | } 46 | } 47 | 48 | 49 | //Box wrapper 50 | .about-container { 51 | display:block; 52 | width:70%; 53 | background-attachment:fixed; 54 | background-repeat: no-repeat; 55 | padding-bottom: 3%; 56 | margin: 0 auto; 57 | } 58 | 59 | 60 | //Box background image 61 | .about-background-box { 62 | // background: linear-gradient( 63 | // rgba(white, 0.55), 64 | // rgba(white, 0.55) 65 | // ), url("../images/register-page-background-image.png"); 66 | // width: 80%; 67 | background-color: white; 68 | height: auto; 69 | background-size: auto; 70 | background-repeat: no-repeat; 71 | padding: 10%; 72 | margin-bottom: 7%; 73 | } 74 | 75 | 76 | .about-box { 77 | p { 78 | @include smallfont; 79 | color: $charcoal; 80 | text-align: left; 81 | margin-top: -3%; 82 | font-weight: 400; 83 | text-transform:none; 84 | 85 | } 86 | .bikemd { 87 | @include smallregularfont; 88 | } 89 | } 90 | 91 | 92 | .about-exit-button { 93 | height:40px; 94 | width:100%; 95 | box-sizing:border-box; 96 | @include smallfont; 97 | float: right; 98 | text-align: right; 99 | margin-right: -5%; 100 | margin-top: 4%; 101 | a{ 102 | text-decoration:none; 103 | color:white; 104 | padding:1%; 105 | background-color:$charcoal; 106 | } 107 | a:hover{ 108 | color:$charcoal; 109 | background-color:white; 110 | border: solid $charcoal 1px; 111 | 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /diag_app/templates/about_us.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | About Us 13 | 14 | 15 | 16 | 17 |
    18 |

    About Bike MD

    19 |
    20 | 21 |
    22 |
    23 |
    24 | 25 |

    Bike MD is a community of motorcycle technicians, a place 26 | where they can connect with each other and ask technical questions, 27 | browse common problems and solutions by model, and help each other 28 | troubleshoot vehicles more efficiently. To get started simply 29 | create an account and login in. The first page you're directed to 30 | is the diagnostic page. Here you can select the make, year, and 31 | model you are working on. Then you can view previously posted 32 | problems for each system of that bike. Selecting a problem from 33 | the list will show you all the details for that porblem. Here you view 34 | solutions and vote on the ones that are the most accurate and helpful. 35 | The nav bar is present on every page and allows users to post new problems, 36 | help others solve problems, and search problems by keyword 37 | instead of by model. 38 |

    39 |
    40 | 41 |
    42 |
    43 | 44 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bike MD 1.0 2 | #### See the live site [HERE](http://bike-md.herokuapp.com) 3 | Bike MD is a community based site for motorcycle technicians and owners, a place where they can connect with each other and ask technical questions, browse common problems and solutions by model, and help each other troubleshoot bikes more efficiently. This website was originally built as a final project for the Iron Yard. It was developed with Python, Django, JavaScript, and PostgreSQL. The complete refactoring of several files is currently in progress. The goal is to bring the code up to a higher standard and strive to follow best practice guide lines. We're always open to suggestions so if you see something that could be done a better way, and I'm sure that there are a lot of things that could be, please don't hesitate to say so. The list of improvements and bugs that need to be addressed is ever growing. Check back often if nothing catches your eye today. This is an open source project and all contributors of all skill levels are welcome! We're in the process of creating documentation, wiki guides, and commenting existing code to make the project easier to work on and understand. There are a few small issues posted already that are on the easy side and will give you a chance to learn the code base. 4 | Please contact us with any questions regarding contributing to BikeMD! 5 | 6 | ### Local Build: 7 | 1. clone the repo: 8 | 9 | 10 | `git clone https://github.com/bike-md/bike_md.git` 11 | 12 | 13 | 2. Create a virtualenv, I prefer this because it eliminates a lot of package install issues: 14 | 15 | 16 | `echo layout python3 > .envrc` 17 | 18 | 19 | 4. Install packages: 20 | 21 | 22 | `pip install -r requirements.txt` 23 | 24 | 25 | 5. Set up postgreSQL if you don't have it already. Install guide [here](http://postgresguide.com/) 26 | 27 | 28 | a. Set up a database called BIKEMD, I recommend using Potico to manage postgreSQL DBs. 29 | 30 | 6. Configure your settings_example.py file if needed: 31 | 32 | 33 | a. Remove the word 'example', making the new title settings.py. This will be your local settings file and will be ignored when you push. 34 | *Note: IF you used the DB name recommended above there shouldn't be anything to configure.* 35 | 36 | 7. Run migrations: 37 | 38 | 39 | `python manage.py migrate` 40 | 41 | 42 | 8. Create super user and tech. For now run the following command 1st. 43 | 44 | `python manage.py createsuperuser` 45 | 46 | And then create a tech manually using a DB tool. The tech creation method is on the list 47 | of things to do. 48 | 49 | 50 | 9. Thats it! you can now run a fully functional local copy of the site: 51 | 52 | 53 | `python manage.py runserver` 54 | 55 | ### Please refer to the wiki's for information on how to contribute. 56 | -------------------------------------------------------------------------------- /diag_app/static/css/_main.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .mainbody { 5 | background: url("../images/bikedetails-background.jpg"); 6 | background-size:cover; 7 | background-repeat: no-repeat; 8 | margin:0; 9 | } 10 | 11 | 12 | hr { 13 | margin-left: 10%; 14 | margin-right: 10%; 15 | } 16 | 17 | .outer-box{ 18 | background-color:rgba(white , .40); 19 | height:auto; 20 | width: auto; 21 | max-width: 80%; 22 | padding-bottom:2%; 23 | margin:0 auto; 24 | margin-top: 2%; 25 | 26 | .diag-select-box{ 27 | background-color:white; 28 | height:45px; 29 | width:30%; 30 | height:35px; 31 | width:21%; 32 | display:inline-block; 33 | margin-left: 2.85%; 34 | margin-top:1%; 35 | margin-bottom: 1%; 36 | 37 | .modaltext { 38 | color: $teal; 39 | @include smallfont; 40 | color: $golden; 41 | margin-top: 5%; 42 | } 43 | 44 | .modaltext:hover { 45 | color: white; 46 | } 47 | 48 | .diag-select-text{ 49 | @include regularfont; 50 | @include verticalcenter; 51 | text-align:center; 52 | color:$charcoal; 53 | font-size:16px; 54 | margin:0; 55 | } 56 | } 57 | 58 | a{ 59 | text-decoration:none; 60 | } 61 | 62 | .diag-select-box:hover{ 63 | background-color:$charcoal; 64 | color:white; 65 | cursor:pointer; 66 | transition: .5s; 67 | 68 | .diag-select-text{ 69 | color:white; 70 | } 71 | } 72 | } 73 | 74 | .diag-select-title { 75 | @include mediumfont; 76 | background-color:$charcoal; 77 | text-transform: uppercase; 78 | font-weight: 400; 79 | color: white; 80 | padding:.5% 0 .5% 38%; 81 | margin-top: -18px; 82 | } 83 | 84 | .stepscircles { 85 | display: inline-block; 86 | height: 30px; 87 | width: 30px; 88 | border-radius: 50%; 89 | float:right; 90 | margin:0.5% 2% 0 .5%; 91 | background-color:white; 92 | text-align:center; 93 | transition: .5s; 94 | } 95 | 96 | .stepscircles:hover{ 97 | cursor:pointer; 98 | } 99 | 100 | .step-number { 101 | margin: 0px; 102 | @include regularfont; 103 | color:$charcoal; 104 | font-weight: 400; 105 | transition: .5s; 106 | margin-left: 1%; 107 | } 108 | 109 | .selected{ 110 | background-color:$teal; 111 | 112 | } 113 | 114 | .selected-number{ 115 | color:white; 116 | } 117 | 118 | .mainfooter{ 119 | position:absolute; 120 | bottom:0; 121 | } 122 | 123 | .new-problem-header { 124 | background-color: $golden; 125 | width: 100%; 126 | height: 60px; 127 | } 128 | 129 | .new-problem-header-title { 130 | margin-top: 0%; 131 | margin-bottom: 0%; 132 | } 133 | 134 | .contact{ 135 | display:inline-block; 136 | } 137 | -------------------------------------------------------------------------------- /diag_app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | from django.urls import reverse 4 | 5 | 6 | class Tech(models.Model): 7 | experience = models.IntegerField(default=0) 8 | job_title = models.CharField(max_length=25) 9 | shop = models.CharField(max_length=25) 10 | user = models.OneToOneField(User) 11 | tech_rating = models.IntegerField(default=0) 12 | 13 | 14 | class Rating(models.Model): 15 | tech = models.ForeignKey(Tech) 16 | value = models.IntegerField(default=5) 17 | 18 | 19 | class System(models.Model): 20 | name = models.CharField(max_length=25) 21 | 22 | 23 | class Brand(models.Model): 24 | name = models.CharField(max_length=25) 25 | 26 | def __repr__(self): 27 | return str(self.name) 28 | 29 | 30 | class Model(models.Model): 31 | name = models.CharField(max_length=40) 32 | brand = models.ForeignKey(Brand) 33 | year = models.IntegerField(default=0) 34 | 35 | def __repr__(self): 36 | return str(self.name) 37 | 38 | 39 | class Problem(models.Model): 40 | title = models.CharField(max_length=65) 41 | system = models.ForeignKey(System, related_name='problems') 42 | description = models.TextField(max_length=500) 43 | tech = models.ForeignKey(Tech, related_name='problems') 44 | model = models.ForeignKey(Model) 45 | posted = models.DateTimeField(auto_now=True) 46 | 47 | 48 | class Solution(models.Model): 49 | description = models.TextField(max_length=500) 50 | time_required = models.FloatField(default=0) 51 | parts_cost = models.DecimalField(max_digits=6, decimal_places=2) 52 | problem = models.ForeignKey(Problem, related_name='solutions') 53 | tech = models.ForeignKey(Tech, related_name='solutions') 54 | posted = models.DateTimeField(auto_now=True) 55 | score = models.IntegerField(default=0) 56 | 57 | 58 | class Commit(models.Model): 59 | solution = models.ForeignKey(Solution, related_name='commits') 60 | tech = models.ForeignKey(Tech) 61 | posted = models.DateTimeField(auto_now=True) 62 | text = models.TextField(max_length=200) 63 | 64 | 65 | class Notification(models.Model): 66 | tech = models.ForeignKey(Tech, related_name='techs') 67 | message = models.CharField(max_length=20) 68 | # is this the best way to link notifications to events? 69 | solution = models.ForeignKey(Solution, related_name='solution',null=True) 70 | commit = models.ForeignKey(Commit, related_name='commit',null=True) 71 | posted = models.DateTimeField(auto_now=True) 72 | 73 | 74 | class Vote(models.Model): 75 | tech = models.ForeignKey(Tech) 76 | solution = models.ForeignKey(Solution, related_name='votes') 77 | value = models.IntegerField(default=1) 78 | 79 | 80 | class Problem_Model(models.Model): 81 | problem = models.ForeignKey(Problem) 82 | model = models.ForeignKey(Model) 83 | -------------------------------------------------------------------------------- /diag_app/templates/build_templates/bike_detail.html: -------------------------------------------------------------------------------- 1 | . 2 | {%load static%} 3 | 4 | 5 | 6 | Bike Detail 7 | 9 | 10 | 11 |
    12 |
    13 |
    14 |
    15 |
    16 |
    17 |
    18 | {%csrf_token%} 19 | 20 | 21 |

    Enter a title for your post:

    22 | 23 |
    24 |
    25 |

    Choose System:

    26 |
    27 |
    28 |

    Describe you problem:

    29 | 30 | 31 |
    32 | 33 |
    34 |
    35 |
    36 |
    37 | 38 | 39 | 48 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /diag_app/templates/create_account.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Create Account 12 | 13 | 14 | 15 | 16 |
    17 |

    Register

    18 |
    19 |
    20 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /diag_app/static/css/_createaccount.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .createaccountbody { 5 | background-image: 6 | linear-gradient( 7 | rgba($charcoal, 0.5), 8 | rgba($charcoal, 0.5) 9 | ), 10 | url("../images/index-background.jpg"); 11 | background-size:cover; 12 | background-position:center; 13 | background-repeat: no-repeat; 14 | margin:0; 15 | } 16 | //Color background for title 17 | .goldendecor { 18 | background-color: $golden; 19 | width: 70%; 20 | padding-top: 1%; 21 | height: 70px; 22 | margin-left: 15%; 23 | margin-top: 7%; 24 | margin-right: 15%; 25 | } 26 | //Title 27 | .register-user-title { 28 | @include mediumfont; 29 | color: white; 30 | font-weight: 400; 31 | margin-left: 10%; 32 | margin-top: 2%; 33 | margin-bottom: -2%; 34 | font-weight: 200; 35 | padding-top: 1%; 36 | padding-bottom: 1%; 37 | hr { 38 | margin-top: 0; 39 | margin-bottom: 0; 40 | margin-left: 5%; 41 | } 42 | } 43 | //Box wrapper 44 | .create-account-container{ 45 | display:block; 46 | width:70%; 47 | background-position: relative; 48 | right: 0; 49 | background-attachment:fixed; 50 | background-repeat: no-repeat; 51 | height: auto; 52 | padding-bottom: 3%; 53 | margin: 0 auto; 54 | } 55 | //Box background image 56 | .create-account-box { 57 | background: linear-gradient( 58 | rgba(white, 0.55), 59 | rgba(white, 0.55) 60 | ), url("../images/register-page-background-image.png"); 61 | width: 96%; 62 | background-size: cover; 63 | padding: 2%; 64 | margin-bottom: 7%; 65 | } 66 | //Start Form area 67 | .profile-information { 68 | width: 66%; 69 | height: 224px; 70 | float: right; 71 | background-color: #000000; 72 | margin: 1%; 73 | } 74 | .register-form { 75 | padding-top: 2%; 76 | padding-left:10%; 77 | padding-bottom: 5%; 78 | } 79 | .register-form-questions { 80 | margin: 0; 81 | @include smallfont; 82 | font-weight: normal; 83 | color: $teal; 84 | padding: .3%; 85 | } 86 | .register-form input { 87 | width: 40%; 88 | height: 20px; 89 | margin-right: 5%; 90 | @include smallfont; 91 | font-weight: normal; 92 | } 93 | .register-form-submit-button { 94 | background-color:rgba($golden, .8); 95 | border: none; 96 | @include smallfont; 97 | font-weight: normal; 98 | color: white; 99 | float: left; 100 | margin-top: 1%; 101 | margin-bottom: 0%; 102 | } 103 | .register-form-submit-button:hover { 104 | background-color:rgba($charcoal, .8); 105 | border: none; 106 | } 107 | //Close Button 108 | .register-exit-button { 109 | height:40px; 110 | width:100%; 111 | box-sizing:border-box; 112 | @include smallfont; 113 | float: right; 114 | text-align: right; 115 | margin-right: 1%; 116 | margin-top: -4%; 117 | a{ 118 | text-decoration:none; 119 | color:$golden; 120 | padding:1%; 121 | background-color:$charcoal; 122 | } 123 | a:hover{ 124 | color:$charcoal; 125 | background-color:$golden; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /diag_app/templates/build_templates/load.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | Load 7 | 9 | 10 | 11 | 12 |
    13 |
    14 |
    15 |
    16 | 17 | 18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 | 25 | 40 | 41 | 56 | 57 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /diag_project/settings.py: -------------------------------------------------------------------------------- 1 | import dj_database_url 2 | import os 3 | 4 | 5 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 6 | 7 | ### heroku config:set SECRET_KEY=''whjwxt79_m_8impz71nc1@qdzzh99z(h%tkuvrkb8r8f4e'' 8 | ### export SECRET_KEY='whjwxt79_m_8impz71nc1@qdzzh99z(h%tkuvrkb8r8f4e' 9 | SECRET_KEY = os.environ['SECRET_KEY'] 10 | 11 | DEBUG = True 12 | 13 | ALLOWED_HOSTS = ['0.0.0.0', '127.0.0.1', 'pacific-depths-50874.herokuapp.com', 14 | 'bike-md.herokuapp.com'] 15 | 16 | INSTALLED_APPS = [ 17 | 'diag_app.apps.DiagAppConfig', 18 | 'django.contrib.admin', 19 | 'django.contrib.auth', 20 | 'django.contrib.contenttypes', 21 | 'django.contrib.sessions', 22 | 'django.contrib.messages', 23 | 'django.contrib.staticfiles', 24 | 'rest_framework', 25 | 'crispy_forms', 26 | 27 | ] 28 | 29 | MIDDLEWARE = [ 30 | 'django.middleware.security.SecurityMiddleware', 31 | 'django.contrib.sessions.middleware.SessionMiddleware', 32 | 'django.middleware.common.CommonMiddleware', 33 | 'django.middleware.csrf.CsrfViewMiddleware', 34 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 35 | 'django.contrib.messages.middleware.MessageMiddleware', 36 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 37 | ] 38 | 39 | ROOT_URLCONF = 'diag_project.urls' 40 | 41 | REST_FRAMEWORK = { 42 | 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',), 43 | 'PAGE_SIZE': 500, 44 | 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) 45 | } 46 | 47 | TEMPLATES = [ 48 | { 49 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 50 | 'DIRS': [], 51 | 'APP_DIRS': True, 52 | 'OPTIONS': { 53 | 'context_processors': [ 54 | 'django.template.context_processors.debug', 55 | 'django.template.context_processors.request', 56 | 'django.contrib.auth.context_processors.auth', 57 | 'django.contrib.messages.context_processors.messages', 58 | ], 59 | }, 60 | }, 61 | ] 62 | 63 | WSGI_APPLICATION = 'diag_project.wsgi.application' 64 | 65 | 66 | DATABASES = { 67 | 'default': { 68 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 69 | 'NAME': 'BIKEmD', 70 | 'USER': '', 71 | 'PASSWORD': '', 72 | 'HOST': 'localhost', 73 | 'PORT': '5432', 74 | } 75 | } 76 | 77 | db_from_env = dj_database_url.config(conn_max_age=500) 78 | 79 | # DATABASES['default'].update(db_from_env) 80 | # 81 | # DATABASES['default'] = dj_database_url.config() 82 | 83 | 84 | AUTH_PASSWORD_VALIDATORS = [ 85 | { 86 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 87 | }, 88 | { 89 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 90 | }, 91 | { 92 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 93 | }, 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 96 | }, 97 | ] 98 | 99 | LOGIN_REDIRECT_URL = '/diag_app/' 100 | 101 | LANGUAGE_CODE = 'en-us' 102 | 103 | TIME_ZONE = 'UTC' 104 | 105 | USE_I18N = True 106 | 107 | USE_L10N = True 108 | 109 | USE_TZ = True 110 | 111 | STATIC_URL = '/static/' 112 | 113 | PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) 114 | 115 | STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles') 116 | 117 | STATIC_URL = '/static/' 118 | 119 | STATICFILES_DIRS = ( 120 | os.path.join(PROJECT_ROOT, '../diag_app/static'), 121 | ) 122 | 123 | STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage' 124 | -------------------------------------------------------------------------------- /diag_app/templates/build_templates/problem_detail.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | Problem Detail 7 | 9 | 10 | 11 | 12 |
    13 |
    14 |
    15 |
    16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |

    Post a new Solution

    25 | {%csrf_token%} 26 | 27 | 28 | 29 |

    Enter the time required for your solution:

    30 | 31 | 32 |

    Enter the parts cost, if any, for this solution:

    33 | 34 | 35 |

    Describe your solution:

    36 | 37 | 38 |
    39 |
    40 |
    41 |
    42 | 43 | 54 | 55 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /diag_app/static/css/_base.scss: -------------------------------------------------------------------------------- 1 | $charcoal: #313237; 2 | $seafoam: #a8bdba; 3 | $golden: #b68d58; 4 | $teal: #45756e; 5 | 6 | @mixin smallfont{ 7 | font-size:1em; 8 | font-family:gill sans; 9 | text-transform:uppercase; 10 | } 11 | 12 | @mixin regularfont{ 13 | font-size:1.5em; 14 | font-family:gill sans; 15 | text-transform:uppercase; 16 | } 17 | 18 | @mixin mediumfont{ 19 | font-size:2.25em; 20 | font-family:gill sans; 21 | text-transform:uppercase; 22 | } 23 | 24 | @mixin largefont{ 25 | font-size:3.75em; 26 | font-family:gill sans; 27 | text-transform:uppercase; 28 | } 29 | 30 | header { 31 | height: 58px; 32 | padding-left: 10%; 33 | padding-top: 1%; 34 | padding-bottom: 2%; } 35 | header .templatelogo { 36 | width: 15%; 37 | float: left; 38 | margin-right: 4%; 39 | margin-top: 1.5%; } 40 | 41 | header .templatenav { 42 | display: inline; } 43 | header .templatenav li { 44 | list-style-type: none; 45 | display: inline-block; 46 | margin-left: 2%; 47 | float: left; } 48 | header .templatenav li a { 49 | text-decoration: none; 50 | color: white; 51 | font-size: 1.5rem; 52 | font-family: gill sans; 53 | text-transform: uppercase; 54 | font-weight: normal; 55 | text-align: right; } 56 | header .templatenav li a:after { 57 | display: block; 58 | position: absolute; 59 | bottom: 30px; 60 | height: 5px; 61 | width: 0px; 62 | background-color: #a8bdba; 63 | transition: width .5s; 64 | content: ""; } 65 | header .templatenav li .link1:hover:after { 66 | width: 100%; } 67 | header .templatenav li .link2:hover:after { 68 | width: 100%; } 69 | header .templatenav li .link3:hover:after { 70 | width: 100%; } 71 | 72 | .dropdown { 73 | margin-top: 2%; 74 | display: none; 75 | padding: 5%; 76 | position: absolute; 77 | top: 500px; } 78 | 79 | header .templatenav .dropdown li a { 80 | font-size: 1.1rem; } 81 | 82 | header .templatenav .dropdown li a:hover { 83 | color: #ba973a; } 84 | 85 | .droplist { 86 | display: block; 87 | clear: both; 88 | width: 100%; } 89 | 90 | .droplist:hover { 91 | transition: .5s; } 92 | 93 | #searchThis { 94 | height: 30px; 95 | margin-bottom: 1.5rem; 96 | text-align: center; 97 | text-transform: uppercase; 98 | width: 20% !important; } 99 | 100 | #searchBox { 101 | appearance: none; 102 | -moz-appearance: none; 103 | -webkit-appearance: none; 104 | border: 0px; } 105 | 106 | #searchButton { 107 | background-color: rgba(255, 255, 255, 0.55); 108 | border: 0px; 109 | width: 40px; } 110 | 111 | #searchButton:hover { 112 | background-color: #a8bdba; 113 | border: 0px; 114 | transition: .5s; } 115 | 116 | .templatenav{ 117 | 118 | li{ 119 | display:inline-block; 120 | padding:2% .75%; 121 | margin:0; 122 | float:left; 123 | 124 | a{ 125 | text-decoration:none; 126 | color:white; 127 | @include smallfont; 128 | font-size: 1.25rem; 129 | position:relative; 130 | } 131 | 132 | } 133 | 134 | a:after{ 135 | display:block; 136 | position:absolute; 137 | bottom:30px; 138 | height:5px; 139 | width:0px; 140 | background-color:$golden; 141 | transition:width .5s; 142 | content:""; 143 | z-index:-1; 144 | } 145 | 146 | .link1:hover:after{ 147 | width:100%; 148 | } 149 | 150 | .link2:hover:after{ 151 | width:100%; 152 | } 153 | 154 | .link3:hover:after{ 155 | width:100%; 156 | } 157 | } 158 | } 159 | } 160 | 161 | 162 | footer{ 163 | background-color:rgba(white, .25); 164 | height:30px; 165 | position:relative; 166 | top:540px; 167 | p{ 168 | color:white; 169 | text-align:center; 170 | padding-top:.5%; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /diag_app/static/js/problem_list.js: -------------------------------------------------------------------------------- 1 | 2 | //get url for the ajax call 3 | var url = window.location.href; 4 | //kick off ajax 5 | getModel(url); 6 | getProblem(url); 7 | //////////////////////////////////////////////////////////////////////////////// 8 | // Helper: getProblem 9 | // parameters: URL 10 | // description: Ajax call for problem and it's solutions. 11 | // return: none 12 | //////////////////////////////////////////////////////////////////////////////// 13 | function getProblem(url) { 14 | var id = url.split('/'); 15 | var model = id[4]; 16 | var id = id[5]; 17 | var url = '/api/get-problems?system=' + id + '&model=' + model; 18 | $.ajax({ 19 | url: url, 20 | type: 'GET', 21 | }).done(function(results) { 22 | var problems = results.results; 23 | if (problems.length > 0) { 24 | var source = $('#problem-list-template').html(); 25 | var template = Handlebars.compile(source); 26 | var html = template(problems); 27 | var systemKey = problems[0].system; 28 | var context = { 29 | problem: problems, 30 | system: systemKey, 31 | }; 32 | var source = $('#problem-list-template').html(); 33 | var template = Handlebars.compile(source); 34 | var html = template(context); 35 | $('#problemList').append(html); 36 | } else { 37 | $('#problemList').append("

    No problem was found about this system!

    "); 38 | } 39 | }) 40 | } 41 | //////////////////////////////////////////////////////////////////////////////// 42 | // Helper: getModel 43 | // parameters: URL 44 | // description: Ajax call for bike model. 45 | // return: none 46 | //////////////////////////////////////////////////////////////////////////////// 47 | function getModel(url) { 48 | let splited_url = url.split('/'); 49 | let model = splited_url[4]; 50 | 51 | $.ajax({ 52 | url: '/api/models/' + model, 53 | type: 'GET', 54 | }).done(function(results){ 55 | var source2 = $('#model-template').html(); 56 | var template2 = Handlebars.compile(source2); 57 | var html2 = template2(results); 58 | $('#model').append(html2); 59 | }) 60 | } 61 | //////////////////////////////////////////////////////////////////////////////// 62 | // Helper: formatTime 63 | // parameters: timestamp 64 | // description: Formats timestamps to make them pretty and more human readable. 65 | // return: String of month/day/year 66 | //////////////////////////////////////////////////////////////////////////////// 67 | Handlebars.registerHelper('formatTime', function (posted) { 68 | var time = posted.replace('T', ':'); 69 | var date = time.split(":")[0]; 70 | var year = Number(date.split("-")[0]); 71 | var month = Number(date.split("-")[1]); 72 | var day = Number(date.split("-")[2]); 73 | var months = { 74 | "January": 1, 75 | "February ": 2, 76 | "March": 3, 77 | "April": 4, 78 | "May": 5, 79 | "June": 6, 80 | "July": 7, 81 | "August": 8, 82 | "September": 9, 83 | "October": 10, 84 | "November": 11, 85 | "December": 12, 86 | }; 87 | for (var i in months) { 88 | if (month === months[i]) { 89 | month = i; 90 | } 91 | } 92 | return month + " " + day + " " + year; 93 | }) 94 | //////////////////////////////////////////////////////////////////////////////// 95 | // Helper: linkURLModel 96 | // parameters: model object 97 | // description: Formats URL to link to detail page. 98 | // return: String of URL for bike's detail page 99 | //////////////////////////////////////////////////////////////////////////////// 100 | Handlebars.registerHelper('linkURLModel', function (object) { 101 | id = Handlebars.Utils.escapeExpression(object.id) 102 | name = Handlebars.Utils.escapeExpression(object.name) 103 | url = '/model_detail/' + id 104 | return '' + '' + name + '' + '' 105 | 106 | }) 107 | -------------------------------------------------------------------------------- /diag_project/settings_example.py: -------------------------------------------------------------------------------- 1 | import dj_database_url 2 | import os 3 | 4 | 5 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 6 | DEBUG = True 7 | 8 | ### SECRET KEY ### 9 | #secret can be set as an environment varible in your shell or here: 10 | 11 | ### For Shell ### 12 | #un-comment this line 13 | #SECRET_KEY = os.environ['SECRET_KEY'] 14 | ### Run this command in your terminal ### 15 | #export SECRET_KEY='whjwxt79_m_8impz71nc1@qdzzh99z(h%tkuvrkb8r8f4e' 16 | 17 | ### For setting the key here just un-comment the following line ### 18 | #SECRET_key = 'whjwxt79_m_8impz71nc1@qdzzh99z(h%tkuvrkb8r8f4e' 19 | 20 | #secret keys can be generated at http://www.miniwebtool.com/django-secret-key-generator/ 21 | ############ 22 | 23 | ### Needed for local development ### 24 | ALLOWED_HOSTS = ['0.0.0.0', '127.0.0.1'] 25 | 26 | INSTALLED_APPS = [ 27 | 'diag_app.apps.DiagAppConfig', 28 | 'django.contrib.admin', 29 | 'django.contrib.auth', 30 | 'django.contrib.contenttypes', 31 | 'django.contrib.sessions', 32 | 'django.contrib.messages', 33 | 'django.contrib.staticfiles', 34 | 'rest_framework', 35 | 'crispy_forms', 36 | ] 37 | 38 | MIDDLEWARE = [ 39 | 'django.middleware.security.SecurityMiddleware', 40 | 'django.contrib.sessions.middleware.SessionMiddleware', 41 | 'django.middleware.common.CommonMiddleware', 42 | 'django.middleware.csrf.CsrfViewMiddleware', 43 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 44 | 'django.contrib.messages.middleware.MessageMiddleware', 45 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 46 | ] 47 | 48 | ROOT_URLCONF = 'diag_project.urls' 49 | 50 | REST_FRAMEWORK = { 51 | 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',), 52 | 'PAGE_SIZE': 500, 53 | 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) 54 | } 55 | 56 | TEMPLATES = [ 57 | { 58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 | 'DIRS': [], 60 | 'APP_DIRS': True, 61 | 'OPTIONS': { 62 | 'context_processors': [ 63 | 'django.template.context_processors.debug', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.auth.context_processors.auth', 66 | 'django.contrib.messages.context_processors.messages', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = 'diag_project.wsgi.application' 73 | 74 | #create postgreSQL DB with the name BIKEMD in order use this preset connection. 75 | DATABASES = { 76 | 'default': { 77 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 78 | 'NAME': 'BIKEMD', 79 | 'USER': '', 80 | 'PASSWORD': '', 81 | 'HOST': 'localhost', 82 | 'PORT': '5432', 83 | } 84 | } 85 | 86 | ### Needed for deployment to heroku.### 87 | # db_from_env = dj_database_url.config(conn_max_age=500) 88 | # DATABASES['default'].update(db_from_env) 89 | # DATABASES['default'] = dj_database_url.config() 90 | 91 | 92 | AUTH_PASSWORD_VALIDATORS = [ 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 104 | }, 105 | ] 106 | 107 | LOGIN_REDIRECT_URL = '/diag_app/' 108 | 109 | LANGUAGE_CODE = 'en-us' 110 | 111 | TIME_ZONE = 'UTC' 112 | 113 | USE_I18N = True 114 | 115 | USE_L10N = True 116 | 117 | USE_TZ = True 118 | 119 | STATIC_URL = '/static/' 120 | 121 | PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) 122 | 123 | STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles') 124 | 125 | STATIC_URL = '/static/' 126 | 127 | STATICFILES_DIRS = ( 128 | os.path.join(PROJECT_ROOT, '../diag_app/static'), 129 | ) 130 | 131 | STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage' 132 | -------------------------------------------------------------------------------- /diag_app/templates/problem_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {%load static%} 3 | 4 | {% block titlecontent %} 5 | Problem Detail 6 | {% endblock %} 7 | 8 | {% block bodycontent %} 9 | 10 | {% endblock %} 11 | 12 | {%block content%} 13 |
    14 |

    Problem Details

    15 |
    16 | 17 |
    18 | 19 |

    Give a solution

    20 |
    21 | 22 |
    23 | {%csrf_token%} 24 |
    25 |

    Time required for solution:

    26 |

    Hrs.

    27 |
    28 | 29 |
    30 |

    Parts cost for solution:

    31 |

    $

    32 |
    33 | 34 |
    35 |

    Describe your solution:

    36 | 37 |

    38 | 39 |
    40 |
    41 |
    42 | 43 | 44 | 56 | 57 | 80 | {% endblock %} 81 | 82 | {% block jscontent %} 83 | 84 | 85 | {% endblock %} 86 | 87 | -------------------------------------------------------------------------------- /diag_app/serializers.py: -------------------------------------------------------------------------------- 1 | from .models import Vote, Problem, Solution, Tech, Rating, System, Brand 2 | from .models import Model, Problem_Model, Commit, Notification 3 | from django.contrib.auth.models import User 4 | from rest_framework import serializers 5 | from django.contrib.auth import login 6 | 7 | 8 | class SystemSerializer(serializers.ModelSerializer): 9 | 10 | class Meta: 11 | model = System 12 | fields = ['id', 'name', 'url'] 13 | 14 | 15 | class UserSerializer(serializers.ModelSerializer): 16 | password = serializers.CharField(write_only=True) 17 | 18 | def create(self, validated_data): 19 | user = User.objects.create( 20 | username=validated_data['username'], 21 | email=validated_data['email'] 22 | ) 23 | user.set_password(validated_data['password']) 24 | user.save() 25 | return user 26 | 27 | class Meta: 28 | model = User 29 | fields = ['id', 'password', 'email', 'username'] 30 | read_only_fields = ['is_staff', 'is_superuser', 'is_active', 31 | 'date_joined',] 32 | 33 | 34 | class TechGetSerializer(serializers.ModelSerializer): 35 | user = UserSerializer(many=False, read_only=True) 36 | 37 | class Meta: 38 | model = Tech 39 | fields = ['id', 'experience', 'job_title', 'shop', 'user', 40 | 'tech_rating'] 41 | 42 | 43 | class TechPostSerializer(serializers.ModelSerializer): 44 | 45 | class Meta: 46 | model = Tech 47 | fields = ['id', 'experience', 'job_title', 'shop', 'user', 48 | 'tech_rating'] 49 | 50 | 51 | 52 | class CommitSerializer(serializers.ModelSerializer): 53 | 54 | class Meta: 55 | model = Commit 56 | fields = ['id', 'solution', 'tech', 'posted', 'text', 'url'] 57 | 58 | 59 | class VoteSerializer(serializers.ModelSerializer): 60 | 61 | class Meta: 62 | model = Vote 63 | fields = "__all__" 64 | 65 | 66 | class SolutionGetSerializer(serializers.ModelSerializer): 67 | votes = VoteSerializer(many=True, read_only=True) 68 | commits = CommitSerializer(many=True, read_only=True) 69 | tech = TechGetSerializer(many=False, read_only=True) 70 | 71 | class Meta: 72 | model = Solution 73 | fields = ['id', 'description', 'time_required', 'parts_cost', 74 | 'problem', 'tech', 'posted', 'score', 'commits', 75 | 'votes', 'url'] 76 | 77 | 78 | class SolutionPostSerializer(serializers.ModelSerializer): 79 | 80 | class Meta: 81 | model = Solution 82 | fields = ['id', 'description', 'time_required', 'parts_cost', 83 | 'problem', 'tech', 'posted', 'score', 'url'] 84 | 85 | 86 | class ProblemGetSerializer(serializers.ModelSerializer): 87 | solutions = SolutionGetSerializer(many=True, read_only=True) 88 | tech = TechGetSerializer(many=False, read_only=True) 89 | system = SystemSerializer(many=False, read_only=True) 90 | 91 | class Meta: 92 | model = Problem 93 | fields = ['id', 'title', 'system', 'description', 'tech', 94 | 'model', 'posted', 'url', 'solutions'] 95 | 96 | 97 | class ProblemPostSerializer(serializers.ModelSerializer): 98 | 99 | class Meta: 100 | model = Problem 101 | fields = ['id', 'title', 'system', 'description', 'tech', 102 | 'model', 'posted', 'url'] 103 | 104 | 105 | class RatingSerializer(serializers.ModelSerializer): 106 | 107 | class Meta: 108 | model = Rating 109 | fields = "__all__" 110 | 111 | 112 | class BrandSerializer(serializers.ModelSerializer): 113 | 114 | class Meta: 115 | model = Brand 116 | fields = ['id', 'name', 'url'] 117 | 118 | 119 | class ModelSerializer(serializers.ModelSerializer): 120 | brand = BrandSerializer(many=False, read_only=True) 121 | 122 | class Meta: 123 | model = Model 124 | fields = ['id', 'name', 'brand', 'year', 'url'] 125 | 126 | 127 | class NotificationSerializer(serializers.ModelSerializer): 128 | 129 | class Meta: 130 | model = Notification 131 | fields = ['id', 'tech', 'message', 'posted', 'solution', 'commit'] 132 | -------------------------------------------------------------------------------- /diag_app/static/css/_profile.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .userprofilebody { 5 | background-image: 6 | linear-gradient( 7 | rgba($charcoal, 0.5), 8 | rgba($charcoal, 0.5) 9 | ), 10 | url("../images/index-background.jpg"); 11 | background-repeat: no-repeat; 12 | background-attachment: fixed; 13 | background-size: cover; 14 | margin:0; 15 | } 16 | //Title background 17 | .profile-details-goldendecor { 18 | background-color: $golden; 19 | width: 70%; 20 | padding-top: 1%; 21 | height: 70px; 22 | margin-left: 15%; 23 | margin-top: 7%; 24 | margin-right: 15%; 25 | } 26 | //Page Title 27 | .profile-title { 28 | @include mediumfont; 29 | color: white; 30 | font-weight: 400; 31 | margin-left: 10%; 32 | margin-top: 2%; 33 | margin-bottom: -2%; 34 | font-weight: 200; 35 | padding-top: 1%; 36 | padding-bottom: 1%; 37 | hr { 38 | margin-top: 0; 39 | margin-bottom: 0; 40 | margin-left: 5%; 41 | } 42 | } 43 | //Container for the profile page 44 | .profile-details-container { 45 | height: auto; 46 | width:70%; 47 | background-color: white; 48 | padding-top: 2%; 49 | padding-bottom: 2%; 50 | box-sizing:border-box; 51 | margin: 0 auto; 52 | overflow:hidden; 53 | } 54 | 55 | .photo-box { 56 | // width: 30%; 57 | height: 220px; 58 | float: left; 59 | margin-top: 2%; 60 | margin-left: 2%; 61 | margin-bottom: 1%; 62 | padding:1%; 63 | background-color: white; 64 | 65 | .profile-photo img { 66 | margin: 0 auto; 67 | } 68 | } 69 | .profile-photo { 70 | 71 | } 72 | .profile-page-information-area { 73 | font-size: 1rem; 74 | font-weight: 400; 75 | margin-top: .5%; 76 | box-sizing:border-box; 77 | height: 210px; 78 | } 79 | .profile-information-details { 80 | width: 52%; 81 | float: right; 82 | background-color: white; 83 | margin-left: 1%; 84 | margin-right: 2%; 85 | margin-top: 2%; 86 | padding: 2%; 87 | overflow: scroll; 88 | h4 { 89 | margin-top: 1%; 90 | margin-bottom: 1%; 91 | } 92 | p { 93 | @include smallfont; 94 | background-color: $golden; 95 | color: white; 96 | text-align: center; 97 | margin-top: 1px; 98 | } 99 | } 100 | .profile-page-username { 101 | @include regularfont; 102 | color: $charcoal; 103 | font-weight: 200; 104 | margin-top: 1%; 105 | margin-bottom: 1%; 106 | } 107 | 108 | .modaltext { 109 | @include smallfont; 110 | color: $charcoal; 111 | font-weight: 200; 112 | } 113 | .post-container { 114 | @include smallfont; 115 | color: $charcoal; 116 | margin: 0; 117 | } 118 | 119 | .profile-box { 120 | width: 30%; 121 | height: 220px; 122 | float: left; 123 | margin-top: 2%; 124 | margin-left: 2%; 125 | margin-bottom: 1%; 126 | background-color: white; 127 | position: relative; 128 | } 129 | .profile-questions-asked { 130 | width: 92%; 131 | height: 220px; 132 | float: right; 133 | background-color: #ffffff; 134 | margin-right: 2%; 135 | margin-top: 2%; 136 | padding: 2%; 137 | overflow: scroll; 138 | p { 139 | @include smallfont; 140 | background-color: $seafoam; 141 | color: white; 142 | text-align: center; 143 | margin-top: 1px; 144 | padding: 2%; 145 | } 146 | } 147 | .techAnswers { 148 | 149 | } 150 | .profile-questions-answered { 151 | width: 92%; 152 | height: 220px; 153 | float: right; 154 | background-color: #ffffff; 155 | margin-right: 2%; 156 | margin-bottom: 1%; 157 | padding: 2%; 158 | overflow: scroll; 159 | p { 160 | @include smallfont; 161 | background-color: $seafoam; 162 | color: white; 163 | text-align: center; 164 | margin-top: 1px; 165 | padding: 2%; 166 | } 167 | } 168 | .profile-techQuestions { 169 | 170 | } 171 | .exitbutton { 172 | height:40px; 173 | width:100%; 174 | box-sizing:border-box; 175 | @include smallfont; 176 | float: right; 177 | text-align: right; 178 | margin-right: 15%; 179 | margin-top: 1%; 180 | a{ 181 | text-decoration:none; 182 | color:$charcoal; 183 | padding:.5%; 184 | background-color:$seafoam; 185 | } 186 | a:hover{ 187 | color:$seafoam; 188 | background-color:$charcoal; 189 | } 190 | } 191 | 192 | h4.tech-rating { 193 | margin-top: 1%; 194 | margin-bottom: 1%; 195 | } 196 | -------------------------------------------------------------------------------- /diag_app/templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {%load static%} 3 | 4 | {% block titlecontent %} 5 | main 6 | {% endblock %} 7 | 8 | {% block bodycontent %} 9 | 10 | {% endblock %} 11 | {%block content%} 12 |

    Diagnose

    13 | 14 |
    15 | 16 |
    17 |
    18 |
    19 | 20 | {% block footercontent %} 21 |

    Copyright Bike MD 2017. All rights reserved.

    22 | {% endblock %} 23 | 24 | 25 | 43 | 44 | 45 | 73 | 74 | 75 | 106 | {% endblock %} 107 | 108 | {% block jscontent %} 109 | 110 | {% endblock %} 111 | 112 | -------------------------------------------------------------------------------- /diag_app/static/css/_problem-detail.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | h5{ 5 | margin:0; 6 | } 7 | 8 | .prob-detailbody{ 9 | background-image:url("../images/bikedetails-background.jpg"); 10 | background-size:cover; 11 | background-attachment:fixed; 12 | margin:0; 13 | } 14 | 15 | .prob-form-background{ 16 | height:auto; 17 | width:80%; 18 | margin:0 auto; 19 | // padding:2%; 20 | 21 | .prob-header, .sol-header, .sol-name{ 22 | @include mediumfont; 23 | color:white; 24 | background-color:$charcoal; 25 | text-align:center; 26 | margin:0; 27 | padding:.5% 0; 28 | } 29 | 30 | .sol-header, .sol-name{ 31 | @include smallregularfont; 32 | background-color:#ccc; 33 | color:$charcoal; 34 | } 35 | 36 | .prob-title{ 37 | margin:0; 38 | } 39 | 40 | .posted-by{ 41 | display:inline-block; 42 | margin-bottom:10px; 43 | } 44 | 45 | .tech-rating{ 46 | display:inline-block; 47 | border-left:1px solid black; 48 | border-right:1px solid black; 49 | padding:0 .5%; 50 | } 51 | 52 | .date-time{ 53 | display:inline-block; 54 | color:$teal; 55 | margin:0; 56 | font-weight:500; 57 | } 58 | 59 | .solution-score{ 60 | display:inline-block; 61 | } 62 | 63 | .solution-time, .solution-cost{ 64 | float:right; 65 | padding:0 1%; 66 | } 67 | 68 | .timecost-value{ 69 | color:$teal; 70 | } 71 | 72 | .describe-title{ 73 | @include regularfont; 74 | color:$charcoal; 75 | text-align:center; 76 | font-weight: 400; 77 | margin-top: 7%; 78 | } 79 | 80 | .form-text{ 81 | text-align:center; 82 | @include smallfont; 83 | font-weight: 400; 84 | margin: 1%; 85 | } 86 | 87 | .counterSolution{ 88 | text-align: right; 89 | margin-right: 10%; 90 | margin-top: 1%; 91 | } 92 | 93 | 94 | .prob-box, .sol-form-container{ 95 | height:auto; 96 | background-color:rgba(white, 1); 97 | margin:0 auto; 98 | padding:2%; 99 | box-sizing:border-box; 100 | } 101 | 102 | .sol-box{ 103 | height:auto; 104 | background-color:rgba(white, 1); 105 | margin:0 auto; 106 | border-bottom:.5px solid #ccc; 107 | padding:2%; 108 | box-sizing:border-box; 109 | } 110 | 111 | .arrow{ 112 | color:white; 113 | cursor:pointer; 114 | } 115 | 116 | .vote{ 117 | border:0; 118 | border-radius:100%; 119 | background-color:$charcoal; 120 | } 121 | 122 | .solution-score{ 123 | margin-top:30px; 124 | } 125 | 126 | .time-input{ 127 | display:inline-block; 128 | height:20px; 129 | width:52%; 130 | text-align:center; 131 | margin-left:20%; 132 | } 133 | 134 | .cost-input{ 135 | display:inline-block; 136 | height:20px; 137 | width:50%; 138 | text-align:center; 139 | } 140 | 141 | .dollar, .hrs{ 142 | @include smallfont; 143 | color:$charcoal; 144 | display:inline-block; 145 | margin-top:1%; 146 | } 147 | 148 | .hrs{ 149 | text-transform:lowercase; 150 | } 151 | 152 | .dollar{ 153 | margin-left:23%; 154 | } 155 | 156 | .solution-box{ 157 | display:block; 158 | margin:0 auto; 159 | width:80%; 160 | height:auto; 161 | } 162 | 163 | .solution-text{ 164 | font-weight:500; 165 | text-transform:none; 166 | font-size:1rem; 167 | } 168 | 169 | .solution-submit{ 170 | color:white; 171 | background-color:$charcoal; 172 | @include smallfont; 173 | padding:2%; 174 | display:block; 175 | margin:0 auto; 176 | // margin-bottom: 2%; 177 | border:0px; 178 | box-sizing:border-box; 179 | cursor: pointer; 180 | } 181 | 182 | .solution-submit:hover{ 183 | background-color:white; 184 | color:$charcoal; 185 | border:1px solid $charcoal; 186 | transition: .5s; 187 | } 188 | 189 | .time-box { 190 | float: left; 191 | width: 50%; 192 | } 193 | .cost-box { 194 | float: right; 195 | width: 50%; 196 | } 197 | 198 | .prob-title { 199 | @include regularfont; 200 | font-weight: 600; 201 | } 202 | .posted-by { 203 | @include smallfont; 204 | font-weight: 600; 205 | } 206 | .question-text { 207 | @include smallfont; 208 | font-weight: 400; 209 | text-transform:none; 210 | } 211 | .prob-form { 212 | margin-left: 10%; 213 | margin-right: 10%; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /diag_app/static/js/main.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | FILE SUMMARY 3 | At base these functions all preform ajax calls in order to populate 4 | Handlebars templates. After showBrands is kicked off the rest are triggered 5 | by onclicks. Obviously this is outdated and sub par and needs to be refactored. 6 | Also, one selection box should not require a seperate function for each step. 7 | This same functionality will also need to be addressed in the model selection 8 | for the problem posting modal. 9 | *******************************************************************************/ 10 | ////////////////////////////////////////////////////////////////// 11 | // Set up Dom 12 | ////////////////////////////////////////////////////////////////// 13 | showBrands(); 14 | ////////////////////////////////////////////////////////////////// 15 | // function: showBrands 16 | // parameters: none 17 | // description: This function loads the brands currently in the DB 18 | // This is first step of the model selection box 19 | // return: none 20 | ////////////////////////////////////////////////////////////////// 21 | function showBrands() { 22 | $.ajax({ 23 | url: '/api/brands/', 24 | type: 'GET', 25 | }).done(function(results) { 26 | var source = $('#brand-template').html(); 27 | var template = Handlebars.compile(source); 28 | var html = template(results.results); 29 | $('#listing').empty(); 30 | $('#listing').append(html); 31 | $('#brandStep').toggleClass('selected'); 32 | $('#brand-number').toggleClass('selected-number'); 33 | }); 34 | } 35 | ////////////////////////////////////////////////////////////////////////// 36 | // function: showYears 37 | // parameters: none 38 | // description: This function loads the years for the brand selected. 39 | // This is the second step of the model selection box 40 | // return: none 41 | ////////////////////////////////////////////////////////////////////////// 42 | function showYears(id) { 43 | $.ajax({ 44 | url: '/api/models?brand=' + id, 45 | type: 'GET' 46 | }).done(function(results) { 47 | $('#listing').empty(); 48 | var bike = results.results; 49 | var years = []; 50 | for (var i=0; i < bike.length; i++) { 51 | if (!(years.includes(bike[i].year))) { 52 | years.push(bike[i].year); 53 | } 54 | } 55 | years.sort(function(a, b){return b-a}); 56 | var context = { 57 | modelYears : years, 58 | brandId : id, 59 | } 60 | var source = $('#year-template').html(); 61 | var template = Handlebars.compile(source); 62 | var html = template(context); 63 | $('#listing').empty(); 64 | $('#listing').append(html); 65 | $('#yearStep').toggleClass('selected'); 66 | $('#year-number').toggleClass('selected-number'); 67 | }); 68 | } 69 | ////////////////////////////////////////////////////////////////////////// 70 | // function: showModels 71 | // parameters: none 72 | // description: This function loads the models for the brand/year selected. 73 | // This is the final step of the model selection box 74 | // return: none 75 | ////////////////////////////////////////////////////////////////////////// 76 | function showModels(year) { 77 | var brandId = $("#brandId").val(); 78 | $.ajax({ 79 | url: '/api/models?brand=' + brandId + '&year=' + year, 80 | type: 'GET' 81 | }).done(function(results) { 82 | var brandID = results.results[0].brand.id; 83 | var bikeList = results.results; 84 | var context = { 85 | brand: brandID, 86 | bikes: bikeList 87 | } 88 | var source = $('#model-template').html(); 89 | var template = Handlebars.compile(source); 90 | var html = template(context); 91 | $('#listing').empty(); 92 | $('#listing').append(html); 93 | $('#modelStep').toggleClass('selected'); 94 | $('#model-number').toggleClass('selected-number'); 95 | }); 96 | } 97 | ////////////////////////////////////////////////////////////////////////// 98 | // function: linkBike 99 | // parameters: none 100 | // description: This function loads the model page for the model selected. 101 | // return: none 102 | ////////////////////////////////////////////////////////////////////////// 103 | function linkBike (id) { 104 | url = '/model_detail/' + id; 105 | window.location = url; 106 | } 107 | -------------------------------------------------------------------------------- /diag_app/static/css/style.scss: -------------------------------------------------------------------------------- 1 | @import "index.scss"; 2 | @import "createaccount.scss"; 3 | @import "main.scss"; 4 | @import "bikedetails.scss"; 5 | @import "problem-detail.scss"; 6 | @import "profile.scss"; 7 | @import "system-problem-list.scss"; 8 | @import "problem-list.scss"; 9 | @import "aboutus.scss"; 10 | @import "variables.scss"; 11 | @import "mixins.scss"; 12 | 13 | 14 | body { 15 | margin:0; 16 | overflow:auto; 17 | } 18 | 19 | header{ 20 | height:58px; 21 | padding-left: 10%; 22 | padding-top: .5%; 23 | padding-bottom: 5%; 24 | margin-top:5%; 25 | 26 | .templatelogo{ 27 | width:18%; 28 | float:left; 29 | margin-top: 1.5%; 30 | padding-right:.9%; 31 | } 32 | 33 | .templatenav{ 34 | 35 | .search-form{ 36 | height:30px; 37 | margin-bottom:1.5rem; 38 | text-align:center; 39 | margin-bottom:1%; 40 | } 41 | 42 | .search-field{ 43 | appearance:none; 44 | -moz-appearance:none; 45 | -webkit-appearance:none; 46 | border: 0px; 47 | height: 21px; 48 | width: 125px; 49 | } 50 | 51 | .search-submit{ 52 | background-color:$charcoal; 53 | color:white; 54 | border: 0px; 55 | height: 24px; 56 | @include smallfont; 57 | } 58 | 59 | .search-submit:hover{ 60 | background-color:white; 61 | color:$charcoal; 62 | transition: .5s; 63 | cursor:pointer; 64 | } 65 | 66 | li{ 67 | list-style-type:none; 68 | display:inline-block; 69 | padding: 2%; 70 | float:left; 71 | margin-top: 47px; 72 | 73 | a{ 74 | position:relative; 75 | text-decoration:none; 76 | color:white; 77 | @include smallregularfont; 78 | } 79 | 80 | // hover state overline 81 | a:after{ 82 | display:block; 83 | position:absolute; 84 | bottom:25px; 85 | height:3px; 86 | width:0px; 87 | background-color:$teal; 88 | transition:width .5s; 89 | content:""; 90 | } 91 | 92 | .link1:hover:after, .link2:hover:after, .link3:hover:after, .link4:hover:after{ 93 | width:100%; 94 | } 95 | } 96 | } 97 | } 98 | // end hover state overline 99 | 100 | // my account dropdown menu 101 | .dropdown{ 102 | display:none; 103 | padding:0; 104 | position:absolute; 105 | top: 400px; 106 | 107 | .droplist{ 108 | display:block; 109 | clear:both; 110 | width: 100%; 111 | margin-top: 40px; 112 | 113 | a { 114 | @include smallfont; 115 | } 116 | 117 | // a:hover { 118 | // color: $teal; 119 | // transition: .5s; 120 | // } 121 | } 122 | } 123 | 124 | // title of pages 125 | .page-title { 126 | @include mediumfont; 127 | font-weight:500; 128 | color:white; 129 | text-align:left; 130 | margin-left: 10%; 131 | margin-bottom:0; 132 | margin-top:3%; 133 | } 134 | 135 | 136 | // modal 137 | .remodal{ 138 | height:auto; 139 | 140 | textarea { 141 | width: 90%; 142 | } 143 | } 144 | 145 | .systems-dropdown{ 146 | display:inline-block; 147 | } 148 | 149 | .modaltext{ 150 | @include smallregularfont; 151 | margin-bottom: 1%; 152 | font-weight: 400; 153 | margin-top: 2%; 154 | } 155 | 156 | .modaltext-1{ 157 | @include smallfont; 158 | font-weight: 200; 159 | margin: 0; 160 | margin-bottom: 10%; 161 | } 162 | 163 | .problemtitle-field{ 164 | height:30px; 165 | width:90%; 166 | text-align:left; 167 | font-size:1rem; 168 | font-family:gill sans; 169 | color:$charcoal; 170 | } 171 | 172 | .submit-button{ 173 | color:white; 174 | background-color:$charcoal; 175 | color:white; 176 | @include smallfont; 177 | padding:2%; 178 | margin-top:0%; 179 | margin-bottom: 2%; 180 | border:0px; 181 | box-sizing:border-box; 182 | } 183 | 184 | .submit-button:hover{ 185 | color:$charcoal; 186 | background-color:white; 187 | border:1px solid $charcoal; 188 | cursor:pointer; 189 | transition: .5s; 190 | } 191 | 192 | // unsolved problem modal 193 | .unsolved-problem-container{ 194 | padding:2% 0; 195 | } 196 | 197 | .unsolved-problem-container:nth-child(odd){ 198 | background-color:rgba($charcoal, .1); 199 | } 200 | 201 | .unsolved-problem-container:hover{ 202 | background-color:rgba($teal, .5); 203 | } 204 | 205 | .unsolved-problem-title{ 206 | text-transform:capitalize; 207 | } 208 | 209 | .unsolved-problem-link{ 210 | text-decoration:none; 211 | color:$charcoal; 212 | } 213 | 214 | // footer 215 | footer{ 216 | clear:both; 217 | width:100%; 218 | 219 | p{ 220 | color:white; 221 | text-align:left; 222 | padding-top:.5%; 223 | margin:0; 224 | font-family:gill sans; 225 | text-transform:uppercase; 226 | text-align:center; 227 | } 228 | } 229 | 230 | .no-result-text{ 231 | 232 | h5{ 233 | @include smallregularfont; 234 | color: $charcoal; 235 | text-align: center; 236 | } 237 | } 238 | 239 | //post problem modal 240 | .remodal{ 241 | padding:0; 242 | 243 | .goldendecor { 244 | background-color: $charcoal; 245 | text-align: center; 246 | margin: 0; 247 | width: 100%; 248 | height: 60px; 249 | } 250 | } 251 | 252 | .modal-header{ 253 | @include mediumfont; 254 | color: white; 255 | font-weight: 500; 256 | text-align: center; 257 | margin-bottom: -1%;; 258 | margin-top: 1%; 259 | 260 | hr{ 261 | margin-top: 0; 262 | } 263 | } 264 | 265 | .containerDropdown{ 266 | display: flex; 267 | flex-direction: row; 268 | justify-content: space-around; 269 | align-items: center; 270 | flex-wrap: wrap; 271 | } 272 | 273 | .select-brand-model-year{ 274 | margin-bottom: 2%; 275 | 276 | p{ 277 | @include smallfont; 278 | font-weight: 200; 279 | margin-bottom: 10px; 280 | } 281 | } 282 | 283 | .dropdownlist{ 284 | background-color: white; 285 | @include smallfont; 286 | color: $charcoal; 287 | font-weight: 200; 288 | } 289 | 290 | .textarea-border{ 291 | border: 1px solid #c0bebf; 292 | } 293 | 294 | .no-result-modal{ 295 | padding: 10%; 296 | 297 | h5{ 298 | @include smallfont; 299 | color: $charcoal; 300 | font-weight: 200; 301 | margin-bottom: 10%; 302 | } 303 | 304 | .post-problem-button{ 305 | text-decoration: none; 306 | background-color: $charcoal; 307 | padding: 5%; 308 | color: white; 309 | padding: 5%; 310 | } 311 | } 312 | 313 | .post-problem-button:hover{ 314 | text-decoration: none; 315 | background-color:white; 316 | color: $charcoal; 317 | border:1px solid $charcoal; 318 | transition: .5s; 319 | } 320 | -------------------------------------------------------------------------------- /diag_app/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-01-11 14:01 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Brand', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('name', models.CharField(max_length=25)), 24 | ], 25 | ), 26 | migrations.CreateModel( 27 | name='Commit', 28 | fields=[ 29 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 30 | ('posted', models.DateTimeField(auto_now=True)), 31 | ('text', models.TextField(max_length=200)), 32 | ], 33 | ), 34 | migrations.CreateModel( 35 | name='Model', 36 | fields=[ 37 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 38 | ('name', models.CharField(max_length=40)), 39 | ('year', models.IntegerField(default=0)), 40 | ('brand', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='diag_app.Brand')), 41 | ], 42 | ), 43 | migrations.CreateModel( 44 | name='Problem', 45 | fields=[ 46 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 47 | ('title', models.CharField(max_length=65)), 48 | ('description', models.TextField(max_length=500)), 49 | ('posted', models.DateTimeField(auto_now=True)), 50 | ('model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='diag_app.Model')), 51 | ], 52 | ), 53 | migrations.CreateModel( 54 | name='Problem_Model', 55 | fields=[ 56 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 57 | ('model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='diag_app.Model')), 58 | ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='diag_app.Problem')), 59 | ], 60 | ), 61 | migrations.CreateModel( 62 | name='Rating', 63 | fields=[ 64 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 65 | ('value', models.IntegerField(default=5)), 66 | ], 67 | ), 68 | migrations.CreateModel( 69 | name='Solution', 70 | fields=[ 71 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 72 | ('description', models.TextField(max_length=500)), 73 | ('time_required', models.FloatField(default=0)), 74 | ('parts_cost', models.DecimalField(decimal_places=2, max_digits=6)), 75 | ('posted', models.DateTimeField(auto_now=True)), 76 | ('score', models.IntegerField(default=0)), 77 | ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='solutions', to='diag_app.Problem')), 78 | ], 79 | ), 80 | migrations.CreateModel( 81 | name='System', 82 | fields=[ 83 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 84 | ('name', models.CharField(max_length=25)), 85 | ], 86 | ), 87 | migrations.CreateModel( 88 | name='Tech', 89 | fields=[ 90 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 91 | ('experience', models.IntegerField(default=0)), 92 | ('job_title', models.CharField(max_length=25)), 93 | ('shop', models.CharField(max_length=25)), 94 | ('tech_rating', models.IntegerField(default=0)), 95 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 96 | ], 97 | ), 98 | migrations.CreateModel( 99 | name='Vote', 100 | fields=[ 101 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 102 | ('value', models.IntegerField(default=1)), 103 | ('solution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='votes', to='diag_app.Solution')), 104 | ('tech', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='diag_app.Tech')), 105 | ], 106 | ), 107 | migrations.AddField( 108 | model_name='solution', 109 | name='tech', 110 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='solutions', to='diag_app.Tech'), 111 | ), 112 | migrations.AddField( 113 | model_name='rating', 114 | name='tech', 115 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='diag_app.Tech'), 116 | ), 117 | migrations.AddField( 118 | model_name='problem', 119 | name='system', 120 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='problems', to='diag_app.System'), 121 | ), 122 | migrations.AddField( 123 | model_name='problem', 124 | name='tech', 125 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='problems', to='diag_app.Tech'), 126 | ), 127 | migrations.AddField( 128 | model_name='commit', 129 | name='solution', 130 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='commits', to='diag_app.Solution'), 131 | ), 132 | migrations.AddField( 133 | model_name='commit', 134 | name='tech', 135 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='diag_app.Tech'), 136 | ), 137 | ] 138 | -------------------------------------------------------------------------------- /diag_app/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, HttpResponse, HttpResponseRedirect 2 | from rest_framework import permissions 3 | from rest_framework.permissions import IsAuthenticated, AllowAny 4 | from rest_framework import viewsets, permissions, generics, filters 5 | from . import models, forms 6 | from .forms import UserForm, TechForm, LoginForm 7 | from .models import Vote, Problem, Solution, Tech, Rating, System, Brand 8 | from .models import Model, Problem_Model, Notification 9 | from .serializers import VoteSerializer, ProblemGetSerializer, UserSerializer 10 | from .serializers import TechGetSerializer, RatingSerializer, SystemSerializer 11 | from .serializers import BrandSerializer, ModelSerializer, ProblemPostSerializer 12 | from .serializers import SolutionPostSerializer, SolutionGetSerializer 13 | from .serializers import TechPostSerializer, NotificationSerializer 14 | from django.views.generic import ListView 15 | from django.contrib.auth import login, authenticate, logout 16 | from django.contrib.auth.decorators import login_required 17 | from django_filters.rest_framework import DjangoFilterBackend 18 | import django_filters 19 | from django.contrib.auth.models import User 20 | from .permissions import IsStaffOrTargetUser 21 | 22 | 23 | def create_account(request): 24 | return render(request, 'create_account.html') 25 | 26 | 27 | def about_us(request): 28 | return render(request, 'about_us.html') 29 | 30 | 31 | def login_user(request): 32 | form = LoginForm(request.POST or None) 33 | if request.POST: 34 | user = authenticate(username = request.POST['username'], 35 | password = request.POST['password']) 36 | if user is not None and user.is_active and form.is_valid(): 37 | login(request, user) 38 | return HttpResponseRedirect('/') 39 | else: 40 | return render(request, 'registration/login.html',{ 41 | 'login_message' : 'Enter the username and password correctly',}) 42 | return render(request, 'registration/login.html') 43 | 44 | 45 | @login_required(login_url='/login/') 46 | def main_page(request): 47 | return render(request, 'main.html') 48 | 49 | 50 | @login_required(login_url='/login/') 51 | def notifications(request): 52 | return render(request, 'notifications.html') 53 | 54 | 55 | @login_required(login_url='/login/') 56 | def problem_list(request, model, id): 57 | return render(request, 'problem_list.html') 58 | 59 | 60 | @login_required(login_url='/login/') 61 | def model_detail(request, id): 62 | return render(request, 'bike_detail.html') 63 | 64 | 65 | @login_required(login_url='/login/') 66 | def problem_detail(request, id): 67 | return render(request, 'problem_detail.html') 68 | 69 | 70 | @login_required(login_url='/login/') 71 | def profile(request): 72 | return render(request, 'profile.html') 73 | 74 | 75 | # class viewsets and filters 76 | class UserView(viewsets.ModelViewSet): 77 | queryset = User.objects.all() 78 | serializer_class = UserSerializer 79 | model = User 80 | 81 | def get_permissions(self): 82 | return (AllowAny() if self.request.method == 'POST' 83 | else IsStaffOrTargetUser()), 84 | 85 | 86 | class SystemViewSet(viewsets.ModelViewSet): 87 | queryset = System.objects.all() 88 | serializer_class = SystemSerializer 89 | permission_classes = (permissions.IsAuthenticated,) 90 | 91 | 92 | class ModelFilter(django_filters.rest_framework.FilterSet): 93 | 94 | class Meta: 95 | model = Model 96 | fields = ['brand', 'year'] 97 | 98 | 99 | class ModelViewSet(viewsets.ModelViewSet): 100 | queryset = Model.objects.all().order_by('name') 101 | serializer_class = ModelSerializer 102 | filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) 103 | filter_class = ModelFilter 104 | permission_classes = (permissions.IsAuthenticated,) 105 | 106 | 107 | class BrandViewSet(viewsets.ModelViewSet): 108 | queryset = Brand.objects.all().order_by('name') 109 | serializer_class = BrandSerializer 110 | permission_classes = (permissions.IsAuthenticated,) 111 | 112 | 113 | class ProblemFilter(django_filters.rest_framework.FilterSet): 114 | no_solutions = django_filters.BooleanFilter(name='solutions', 115 | lookup_expr='isnull') 116 | 117 | class Meta: 118 | model = Problem 119 | fields = ['system', 'model', 'tech', 'no_solutions'] 120 | 121 | 122 | class ProblemGetViewSet(viewsets.ModelViewSet): 123 | queryset = Problem.objects.all().order_by('-posted') 124 | serializer_class = ProblemGetSerializer 125 | filter_backends = (django_filters.rest_framework.DjangoFilterBackend,filters.SearchFilter,) 126 | filter_class = ProblemFilter 127 | search_fields = ['title'] 128 | permission_classes = (permissions.IsAuthenticated,) 129 | 130 | 131 | class ProblemPostViewSet(viewsets.ModelViewSet): 132 | queryset = Problem.objects.all() 133 | serializer_class = ProblemPostSerializer 134 | filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) 135 | filter_class = ProblemFilter 136 | permission_classes = (permissions.IsAuthenticated,) 137 | 138 | 139 | class SolutionGetViewSet(viewsets.ModelViewSet): 140 | queryset = Solution.objects.all() 141 | serializer_class = SolutionGetSerializer 142 | permission_classes = (permissions.IsAuthenticated,) 143 | 144 | 145 | class SolutionPostViewSet(viewsets.ModelViewSet): 146 | queryset = Solution.objects.all() 147 | serializer_class = SolutionPostSerializer 148 | permission_classes = (permissions.IsAuthenticated,) 149 | 150 | 151 | class VoteFilter(django_filters.rest_framework.FilterSet): 152 | 153 | class Meta: 154 | model = Vote 155 | fields = ['solution', 'tech'] 156 | 157 | 158 | class VoteViewSet(viewsets.ModelViewSet): 159 | queryset = Vote.objects.all() 160 | serializer_class = VoteSerializer 161 | filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) 162 | filter_class = VoteFilter 163 | permission_classes = (permissions.IsAuthenticated,) 164 | 165 | 166 | class TechGetViewSet(viewsets.ModelViewSet): 167 | queryset = Tech.objects.all() 168 | serializer_class = TechGetSerializer 169 | permission_classes = (permissions.IsAuthenticated,) 170 | 171 | 172 | class TechPostViewSet(viewsets.ModelViewSet): 173 | queryset = Tech.objects.all() 174 | serializer_class = TechPostSerializer 175 | 176 | def get_permissions(self): 177 | return (AllowAny() if self.request.method == 'POST' 178 | else IsStaffOrTargetUser()), 179 | 180 | 181 | class RatingViewSet(viewsets.ModelViewSet): 182 | queryset = Rating.objects.all() 183 | serializer_class = RatingSerializer 184 | permission_classes = (permissions.IsAuthenticated,) 185 | 186 | 187 | class NotificationViewSet(viewsets.ModelViewSet): 188 | queryset = Notification.objects.all() 189 | serializer_class = NotificationSerializer 190 | permission_classes = (permissions.IsAuthenticated,) 191 | -------------------------------------------------------------------------------- /diag_app/static/css/remodal-default-theme.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Remodal - v1.1.0 3 | * Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. 4 | * http://vodkabears.github.io/remodal/ 5 | * 6 | * Made by Ilya Makarov 7 | * Under MIT License 8 | */ 9 | 10 | /* ========================================================================== 11 | Remodal's default mobile first theme 12 | ========================================================================== */ 13 | 14 | /* Default theme styles for the background */ 15 | 16 | .remodal-bg.remodal-is-opening, 17 | .remodal-bg.remodal-is-opened { 18 | -webkit-filter: blur(3px); 19 | filter: blur(3px); 20 | } 21 | 22 | /* Default theme styles of the overlay */ 23 | 24 | .remodal-overlay { 25 | background: rgba(49, 50, 55, .8); 26 | } 27 | 28 | .remodal-overlay.remodal-is-opening, 29 | .remodal-overlay.remodal-is-closing { 30 | -webkit-animation-duration: 0.3s; 31 | animation-duration: 0.3s; 32 | -webkit-animation-fill-mode: forwards; 33 | animation-fill-mode: forwards; 34 | } 35 | 36 | .remodal-overlay.remodal-is-opening { 37 | -webkit-animation-name: remodal-overlay-opening-keyframes; 38 | animation-name: remodal-overlay-opening-keyframes; 39 | } 40 | 41 | .remodal-overlay.remodal-is-closing { 42 | -webkit-animation-name: remodal-overlay-closing-keyframes; 43 | animation-name: remodal-overlay-closing-keyframes; 44 | } 45 | 46 | /* Default theme styles of the wrapper */ 47 | 48 | .remodal-wrapper { 49 | padding: 10px 10px 0; 50 | } 51 | 52 | /* Default theme styles of the modal dialog */ 53 | 54 | .remodal { 55 | box-sizing: border-box; 56 | width: 100%; 57 | margin-bottom: 10px; 58 | padding: 0; 59 | 60 | -webkit-transform: translate3d(0, 0, 0); 61 | transform: translate3d(0, 0, 0); 62 | 63 | color: #2b2e38; 64 | background: #fff; 65 | } 66 | 67 | .remodal.remodal-is-opening, 68 | .remodal.remodal-is-closing { 69 | -webkit-animation-duration: 0.3s; 70 | animation-duration: 0.3s; 71 | -webkit-animation-fill-mode: forwards; 72 | animation-fill-mode: forwards; 73 | } 74 | 75 | .remodal.remodal-is-opening { 76 | -webkit-animation-name: remodal-opening-keyframes; 77 | animation-name: remodal-opening-keyframes; 78 | } 79 | 80 | .remodal.remodal-is-closing { 81 | -webkit-animation-name: remodal-closing-keyframes; 82 | animation-name: remodal-closing-keyframes; 83 | } 84 | 85 | /* Vertical align of the modal dialog */ 86 | 87 | .remodal, 88 | .remodal-wrapper:after { 89 | vertical-align: middle; 90 | } 91 | 92 | /* Close button */ 93 | 94 | .remodal-close { 95 | position: absolute; 96 | top: 0; 97 | left: 0; 98 | 99 | display: block; 100 | overflow: visible; 101 | 102 | width: 35px; 103 | height: 35px; 104 | margin: 0; 105 | padding: 0; 106 | 107 | cursor: pointer; 108 | -webkit-transition: color 0.2s; 109 | transition: color 0.2s; 110 | text-decoration: none; 111 | 112 | color: #95979c; 113 | border: 0; 114 | outline: 0; 115 | background: transparent; 116 | } 117 | 118 | .remodal-close:hover, 119 | .remodal-close:focus { 120 | color: #2b2e38; 121 | } 122 | 123 | .remodal-close:before { 124 | font-family: Arial, "Helvetica CY", "Nimbus Sans L", sans-serif !important; 125 | font-size: 25px; 126 | line-height: 35px; 127 | 128 | position: absolute; 129 | top: 0; 130 | left: 0; 131 | 132 | display: block; 133 | 134 | width: 35px; 135 | 136 | content: "\00d7"; 137 | text-align: center; 138 | } 139 | 140 | /* Dialog buttons */ 141 | 142 | .remodal-confirm, 143 | .remodal-cancel { 144 | font: inherit; 145 | 146 | display: inline-block; 147 | overflow: visible; 148 | 149 | min-width: 110px; 150 | margin: 0; 151 | padding: 12px 0; 152 | 153 | cursor: pointer; 154 | -webkit-transition: background 0.2s; 155 | transition: background 0.2s; 156 | text-align: center; 157 | vertical-align: middle; 158 | text-decoration: none; 159 | 160 | border: 0; 161 | outline: 0; 162 | } 163 | 164 | .remodal-confirm { 165 | color: #fff; 166 | background: #81c784; 167 | } 168 | 169 | .remodal-confirm:hover, 170 | .remodal-confirm:focus { 171 | background: #66bb6a; 172 | } 173 | 174 | .remodal-cancel { 175 | color: #fff; 176 | background: #e57373; 177 | } 178 | 179 | .remodal-cancel:hover, 180 | .remodal-cancel:focus { 181 | background: #ef5350; 182 | } 183 | 184 | /* Remove inner padding and border in Firefox 4+ for the button tag. */ 185 | 186 | .remodal-confirm::-moz-focus-inner, 187 | .remodal-cancel::-moz-focus-inner, 188 | .remodal-close::-moz-focus-inner { 189 | padding: 0; 190 | 191 | border: 0; 192 | } 193 | 194 | /* Keyframes 195 | ========================================================================== */ 196 | 197 | @-webkit-keyframes remodal-opening-keyframes { 198 | from { 199 | -webkit-transform: scale(1.05); 200 | transform: scale(1.05); 201 | 202 | opacity: 0; 203 | } 204 | to { 205 | -webkit-transform: none; 206 | transform: none; 207 | 208 | opacity: 1; 209 | } 210 | } 211 | 212 | @keyframes remodal-opening-keyframes { 213 | from { 214 | -webkit-transform: scale(1.05); 215 | transform: scale(1.05); 216 | 217 | opacity: 0; 218 | } 219 | to { 220 | -webkit-transform: none; 221 | transform: none; 222 | 223 | opacity: 1; 224 | } 225 | } 226 | 227 | @-webkit-keyframes remodal-closing-keyframes { 228 | from { 229 | -webkit-transform: scale(1); 230 | transform: scale(1); 231 | 232 | opacity: 1; 233 | } 234 | to { 235 | -webkit-transform: scale(0.95); 236 | transform: scale(0.95); 237 | 238 | opacity: 0; 239 | } 240 | } 241 | 242 | @keyframes remodal-closing-keyframes { 243 | from { 244 | -webkit-transform: scale(1); 245 | transform: scale(1); 246 | 247 | opacity: 1; 248 | } 249 | to { 250 | -webkit-transform: scale(0.95); 251 | transform: scale(0.95); 252 | 253 | opacity: 0; 254 | } 255 | } 256 | 257 | @-webkit-keyframes remodal-overlay-opening-keyframes { 258 | from { 259 | opacity: 0; 260 | } 261 | to { 262 | opacity: 1; 263 | } 264 | } 265 | 266 | @keyframes remodal-overlay-opening-keyframes { 267 | from { 268 | opacity: 0; 269 | } 270 | to { 271 | opacity: 1; 272 | } 273 | } 274 | 275 | @-webkit-keyframes remodal-overlay-closing-keyframes { 276 | from { 277 | opacity: 1; 278 | } 279 | to { 280 | opacity: 0; 281 | } 282 | } 283 | 284 | @keyframes remodal-overlay-closing-keyframes { 285 | from { 286 | opacity: 1; 287 | } 288 | to { 289 | opacity: 0; 290 | } 291 | } 292 | 293 | /* Media queries 294 | ========================================================================== */ 295 | 296 | @media only screen and (min-width: 641px) { 297 | .remodal { 298 | max-width: 700px; 299 | } 300 | } 301 | 302 | /* IE8 303 | ========================================================================== */ 304 | 305 | .lt-ie9 .remodal-overlay { 306 | background: #cab298; 307 | } 308 | 309 | .lt-ie9 .remodal { 310 | width: 700px; 311 | } 312 | -------------------------------------------------------------------------------- /diag_app/static/js/problem_detail.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | FILE SUMMARY 3 | This file handles the problem detail page as well as posting sloutions, voting 4 | for the best solution and updating the rating for the tech that posted that 5 | solution. There are a couple of repetitive functions here such as the char 6 | counter and there are also helpers here that could be split out into a help file. 7 | *******************************************************************************/ 8 | ///////////////////////////////////////////// 9 | // Set up Dom 10 | //////////////////////////////////////////// 11 | function setup() { 12 | var url = window.location.href; 13 | showProblem(url); 14 | ///start char counter/// 15 | charRemaining() 16 | //click events 17 | $("#newSolutionSubmit").click(postSolution) 18 | } 19 | setup() 20 | ////////////////////////////////////////////////////////////////// 21 | // function: showProblem 22 | // parameters: URL 23 | // description: This function loads the problem detail page for 24 | // a given problem. 25 | // return: none 26 | ////////////////////////////////////////////////////////////////// 27 | function showProblem(url) { 28 | var id = url.split('/'); 29 | id = id[4]; 30 | var url = '/api/get-problems/' + id; 31 | $.ajax({ 32 | url: url, 33 | type: 'GET', 34 | }).done(function(results){ 35 | if (typeof results !== 'undefined') { 36 | var source1 = $("#problem-template").html(); 37 | var template1 = Handlebars.compile(source1); 38 | var html1 = template1(results); 39 | $("#problemDetail").append(html1); 40 | 41 | var source2 = $("#solution-template").html(); 42 | var template2 = Handlebars.compile(source2); 43 | var html2 = template2(results.solutions); 44 | $("#solutions").append(html2); 45 | } 46 | }); 47 | } 48 | ////////////////////////////////////////////////////////////////// 49 | // function: charRemaining 50 | // parameters: none 51 | // description: Displays Chars remaining for text fields 52 | // return: none 53 | ////////////////////////////////////////////////////////////////// 54 | 55 | // TEST THIS 56 | 57 | function charRemaining() { 58 | $('#solutionText').keyup(function () { 59 | var left = 500 - $(this).val().length; 60 | if (left < 0) { 61 | left = 0; 62 | } 63 | $('#counterSolution').text('Characters left: ' + left); 64 | }); 65 | $('#probText').keyup(function () { 66 | var left = 500 - $(this).val().length; 67 | if (left < 0) { 68 | left = 0; 69 | } 70 | $('#counterProb').text('Characters left: ' + left); 71 | }); 72 | 73 | } 74 | ////////////////////////////////////////////////////////////////// 75 | // function: postSolution 76 | // parameters: none 77 | // description: Posts a new solution to the DB 78 | // return: none 79 | ////////////////////////////////////////////////////////////////// 80 | function postSolution() { 81 | var text = $("#solutionText").val(); 82 | var user = $("#userId").val(); 83 | var issue = $("#problemId").val(); 84 | var time = $("#solutionTime").val(); 85 | var cost = $("#partsCost").val(); 86 | var context = { 87 | time_required: time, 88 | description: text, 89 | tech: user, 90 | parts_cost: cost, 91 | problem: issue, 92 | } 93 | console.log(context); 94 | $.ajax({ 95 | url: '/api/post-solutions/', 96 | type: 'POST', 97 | data: context, 98 | }).done(function(results){ 99 | // fix this: reload just solution container. 100 | // location.reload(); 101 | $("#solutions").load(location.href + " #solutions"); 102 | }) 103 | 104 | } 105 | ////////////////////////////////////////////////////////////////// 106 | // function: postSolution 107 | // parameters: none 108 | // description: Validates vote so user can not vote multiple times. 109 | // Should keep user from voting on their own solutions. 110 | // return: none 111 | ////////////////////////////////////////////////////////////////// 112 | // better way to validate votes? 113 | function validateVote(solutionId, value) { 114 | var user = $("#userId").val(); 115 | var voted = []; 116 | var vote = {}; 117 | $.ajax({ 118 | url: '/api/votes?solution=' + solutionId, 119 | type: 'GET', 120 | }).done(function(results) { 121 | var votes = results.results; 122 | for (var i=0; i < votes.length; i++) { 123 | if (user == votes[i].tech) { 124 | vote['tech'] = user; 125 | voted.push(vote); 126 | } 127 | } 128 | if (voted.length > 0){ 129 | alert("You've already voted for that one!"); 130 | } else { 131 | postVote(solutionId, value); 132 | } 133 | }); 134 | } 135 | ////////////////////////////////////////////////////////////////////// 136 | // function: postVote 137 | // parameters: Vote ID and Vote value(default is 1) 138 | // description: Posts new vote to DB. Calls updateScore for solution. 139 | // return: none 140 | ////////////////////////////////////////////////////////////////////// 141 | function postVote(id, value) { 142 | var user = $("#userId").val(); 143 | var voteValue = value; 144 | var votedFor = id; 145 | var context = { 146 | tech: user, 147 | value: voteValue, 148 | solution: votedFor, 149 | } 150 | $.ajax({ 151 | url: '/api/votes/', 152 | type: 'POST', 153 | data: context, 154 | }).done(function(results) { 155 | updateScore(id, value); 156 | }); 157 | } 158 | ////////////////////////////////////////////////////////////////////// 159 | // function: updateScore 160 | // parameters: solution id and vote's value 161 | // description: GETS and updates the score for a given solution. 162 | // Calls updateRating to update the poster's rating. 163 | // return: none 164 | ////////////////////////////////////////////////////////////////////// 165 | function updateScore(id, voteValue) { 166 | $.ajax({ 167 | url: '/api/get-solutions/' + id , 168 | type: 'GET', 169 | }).done(function(results) { 170 | var tech = results.tech.id; 171 | updateRating(tech, voteValue); 172 | var currentScore = results.score; 173 | var newScore = currentScore + voteValue; 174 | var context = { 175 | score: newScore 176 | } 177 | $.ajax({ 178 | url: '/api/post-solutions/' + id + '/', 179 | type: 'PATCH', 180 | data: context, 181 | }).done(function(results) { 182 | var id_container = '#solutionScore' + results.id; 183 | $(id_container).html('Score: ' + results.score); 184 | }); 185 | }); 186 | } 187 | ////////////////////////////////////////////////////////////////////// 188 | // function: updateRating 189 | // parameters: tech's id and vote's value 190 | // description: GETS and updates the rating for a given tech. 191 | // return: none 192 | ////////////////////////////////////////////////////////////////////// 193 | function updateRating(tech, voteValue) { 194 | $.ajax({ 195 | url: '/api/get-techs/' + tech, 196 | type: 'GET', 197 | }).done(function(results) { 198 | var currentRating = results.tech_rating 199 | var newRating = currentRating + (voteValue * 5) 200 | context = { 201 | tech_rating: newRating 202 | } 203 | $.ajax({ 204 | url: '/api/post-techs/' + tech + '/', 205 | type: 'PATCH', 206 | data: context, 207 | }).done(function(results) { 208 | }) 209 | 210 | }) 211 | 212 | } 213 | //////////////////////////////////////////////////////////////////////////////// 214 | // Helper: formatTime 215 | // parameters: timestamp 216 | // description: Formats timestamps to make them pretty and more human readable. 217 | // return: String of month/day/year 218 | //////////////////////////////////////////////////////////////////////////////// 219 | Handlebars.registerHelper('formatTime', function (posted) { 220 | var time = posted.replace('T', ':'); 221 | var date = time.split(":")[0]; 222 | var year = Number(date.split("-")[0]); 223 | var month = Number(date.split("-")[1]); 224 | var day = Number(date.split("-")[2]); 225 | var months = { 226 | "January": 1, 227 | "February ": 2, 228 | "March": 3, 229 | "April": 4, 230 | "May": 5, 231 | "June": 6, 232 | "July": 7, 233 | "August": 8, 234 | "September": 9, 235 | "October": 10, 236 | "November": 11, 237 | "December": 12, 238 | }; 239 | for(var i in months){ 240 | if(month == months[i]){ 241 | month = i; 242 | } 243 | } 244 | return month + " " + day + " " + year; 245 | }) 246 | -------------------------------------------------------------------------------- /diag_app/static/css/_index.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | 5 | .indexbody{ 6 | margin: 0; 7 | background-image: 8 | url("../images/index-background.jpg"); 9 | background-repeat: no-repeat; 10 | -webkit-background-size: cover; 11 | -moz-background-size: cover; 12 | -o-background-size: cover; 13 | background-size: cover; 14 | background-repeat:no-repeat; 15 | } 16 | 17 | .indexnav{ 18 | width:11%; 19 | margin-left:14%; 20 | background-color:rgba(white, .3); 21 | padding:1%; 22 | margin-bottom:0; 23 | height: 100%; 24 | 25 | ul{ 26 | text-align:right; 27 | text-transform:uppercase; 28 | padding:0; 29 | margin:0; 30 | margin-bottom:20%; 31 | 32 | li{ 33 | clear:both; 34 | list-style-type:none; 35 | padding-bottom:1%; 36 | @include regularfont; 37 | 38 | a{ 39 | text-decoration:none; 40 | color:white; 41 | position:relative; 42 | 43 | } 44 | 45 | a:after{ 46 | display:block; 47 | position:absolute; 48 | bottom:0px; 49 | height:1.2em; 50 | width:0px; 51 | background-color:$teal; 52 | transition:width .5s; 53 | content:""; 54 | z-index:-1; 55 | } 56 | 57 | .link1:hover:after{ 58 | width:150%; 59 | } 60 | 61 | .link2:hover:after{ 62 | width:134%; 63 | } 64 | 65 | .link3:hover:after{ 66 | width:133%; 67 | } 68 | } 69 | 70 | .logobox{ 71 | width:100%; 72 | margin-bottom:56%; 73 | 74 | .logo{ 75 | width:100%; 76 | } 77 | } 78 | } 79 | } 80 | 81 | 82 | 83 | .loginform .loginfield{ 84 | height:30px; 85 | border:transparent; 86 | width: 100%; 87 | margin:5% auto; 88 | padding:5%; 89 | box-sizing:border-box; 90 | } 91 | 92 | input:-webkit-autofill { 93 | -webkit-box-shadow: 0 0 0px 1000px white inset; 94 | } 95 | 96 | .loginbutton{ 97 | height:50px; 98 | width:100%; 99 | text-align: center; 100 | padding:0; 101 | background-color:$charcoal; 102 | @include regularfont; 103 | color: white; 104 | text-align:center; 105 | margin:5% auto; 106 | -webkit-transition-duration: 0.4s; /* Safari */ 107 | transition-duration: 0.4s; 108 | border: 0; 109 | cursor: pointer; 110 | 111 | 112 | 113 | a{ 114 | text-decoration:none; 115 | color:white; 116 | padding:15%; 117 | } 118 | 119 | a:hover{ 120 | text-decoration:none; 121 | color:$teal; 122 | } 123 | } 124 | 125 | .loginbutton:hover{ 126 | background-color:white; 127 | color:$charcoal; 128 | } 129 | 130 | /* ----------- iPad mini ----------- */ 131 | 132 | /* Portrait and Landscape */ 133 | @media only screen 134 | and (min-device-width: 768px) 135 | and (max-device-width: 1024px) 136 | and (-webkit-min-device-pixel-ratio: 1) 137 | { 138 | 139 | .indexbody{ 140 | background: 141 | url("../images/media-index-background.jpg"); 142 | background-width: auto; 143 | background-height: 100%; 144 | background-position: center; 145 | background-repeat: no-repeat; 146 | background-position: 40% 10%; 147 | margin:0; 148 | } 149 | 150 | .indexnav{ 151 | height:100%; 152 | width:60%; 153 | margin:10%; 154 | background-color:rgba(#afafb2, .35); 155 | padding:10%; 156 | float:left; 157 | overflow:hidden; 158 | 159 | ul{ 160 | text-align:left; 161 | text-transform:uppercase; 162 | padding:0; 163 | margin:0; 164 | margin-bottom:5%; 165 | float:left; 166 | 167 | li{ 168 | clear:both; 169 | list-style-type:none; 170 | padding-bottom:1%; 171 | @include regularfont; 172 | 173 | a{ 174 | text-decoration:none; 175 | color:white; 176 | position:relative; 177 | } 178 | 179 | a:after{ 180 | display:block; 181 | position:absolute; 182 | bottom:0px; 183 | height:1.2em; 184 | width:0px; 185 | background-color:$golden; 186 | transition:width .5s; 187 | content:""; 188 | z-index:-1; 189 | } 190 | 191 | .link1:hover:after{ 192 | width:155%; 193 | } 194 | 195 | .link2:hover:after{ 196 | width:165%; 197 | } 198 | 199 | .link3:hover:after{ 200 | width:138%; 201 | } 202 | } 203 | 204 | .logobox{ 205 | width:100%; 206 | margin-bottom:35%; 207 | 208 | .logo{ 209 | height:160px; 210 | width: auto; 211 | } 212 | } 213 | } 214 | } 215 | } 216 | 217 | /* ----------- iPad 1 and 2 ----------- */ 218 | /* Portrait and Landscape */ 219 | @media only screen 220 | and (min-device-width: 768px) 221 | and (max-device-width: 1024px) 222 | and (-webkit-min-device-pixel-ratio: 1) 223 | { 224 | 225 | .indexbody{ 226 | background: 227 | url("../images/media-index-background.jpg"); 228 | background-width: auto; 229 | background-height: 100%; 230 | background-position: center; 231 | background-repeat: no-repeat; 232 | background-position: 40% 10%; 233 | margin:0; 234 | } 235 | 236 | .indexnav{ 237 | height:100%; 238 | width:60%; 239 | margin:10%; 240 | background-color:rgba(#afafb2, .35); 241 | padding:10%; 242 | float:left; 243 | overflow:hidden; 244 | 245 | 246 | ul{ 247 | text-align:left; 248 | text-transform:uppercase; 249 | padding:0; 250 | margin:0; 251 | margin-bottom:15%; 252 | float:left; 253 | 254 | li{ 255 | clear:both; 256 | list-style-type:none; 257 | padding-bottom:5%; 258 | @include regularfont; 259 | 260 | a{ 261 | text-decoration:none; 262 | color:white; 263 | position:relative; 264 | } 265 | 266 | a:after{ 267 | display:block; 268 | position:absolute; 269 | bottom:0px; 270 | height:1.2em; 271 | width:0px; 272 | background-color:$golden; 273 | transition:width .5s; 274 | content:""; 275 | z-index:-1; 276 | } 277 | 278 | .link1:hover:after{ 279 | width:155%; 280 | } 281 | 282 | .link2:hover:after{ 283 | width:165%; 284 | } 285 | 286 | .link3:hover:after{ 287 | width:138%; 288 | } 289 | } 290 | 291 | .logobox{ 292 | width:100%; 293 | margin-bottom:35%; 294 | 295 | .logo{ 296 | height:275px; 297 | width: auto; 298 | } 299 | } 300 | } 301 | } 302 | } 303 | 304 | 305 | /* Portrait and Landscape */ 306 | @media only screen 307 | and (min-device-width: 320px) 308 | and (max-device-width: 667px) 309 | and (-webkit-min-device-pixel-ratio: 2) { 310 | 311 | .indexbody{ 312 | background: 313 | url("../images/media-index-background.jpg"); 314 | background-width: auto; 315 | background-height: 100%; 316 | background-position: center; 317 | background-repeat: no-repeat; 318 | background-position: 40% 10%; 319 | margin:0; 320 | } 321 | 322 | .indexnav{ 323 | height:100%; 324 | width:60%; 325 | margin:10%; 326 | background-color:rgba(#afafb2, .35); 327 | padding:10%; 328 | float:left; 329 | overflow:hidden; 330 | 331 | ul{ 332 | text-align:left; 333 | text-transform:uppercase; 334 | padding:0; 335 | margin:0; 336 | margin-bottom:5%; 337 | float:left; 338 | 339 | li{ 340 | clear:both; 341 | list-style-type:none; 342 | padding-bottom:1%; 343 | @include regularfont; 344 | 345 | a{ 346 | text-decoration:none; 347 | color:white; 348 | position:relative; 349 | } 350 | 351 | a:after{ 352 | display:block; 353 | position:absolute; 354 | bottom:0px; 355 | height:1.2em; 356 | width:0px; 357 | background-color:$golden; 358 | transition:width .5s; 359 | content:""; 360 | z-index:-1; 361 | } 362 | 363 | .link1:hover:after{ 364 | width:155%; 365 | } 366 | 367 | .link2:hover:after{ 368 | width:165%; 369 | } 370 | 371 | .link3:hover:after{ 372 | width:138%; 373 | } 374 | } 375 | 376 | .logobox{ 377 | width:100%; 378 | margin-bottom:35%; 379 | 380 | .logo{ 381 | height:150px; 382 | width: auto; 383 | } 384 | } 385 | } 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /diag_app/static/js/script.js: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////// 2 | // Set up Dom 3 | ////////////////////////////////////////////////////////////////// 4 | function setupEvents() { 5 | $("#notify").click(loadNotificationsModal); 6 | $("#newProbSubmit").click(postProblem); 7 | $("#answer").click(loadUnsolvedProblemsModal); 8 | $("#searchButton").click(searchProblems); 9 | loadBrandsAskModal(); 10 | charRemainingText(); 11 | //Dropdown menu on nav bar 12 | $(document).ready(function(){ 13 | $(".link4").click(function(){ 14 | $(".dropdown").slideToggle(); 15 | }); 16 | }); 17 | } 18 | setupEvents(); 19 | ////////////////////////////////////////////////////////////////// 20 | // function: loadBrandsAskModal 21 | // parameters: none 22 | // description: This function loads the brands currently in the DB 23 | // return: none 24 | ////////////////////////////////////////////////////////////////// 25 | function loadBrandsAskModal() { 26 | loadSystemsAskModal() 27 | $.ajax({ 28 | url: '/api/brands/', 29 | type: 'GET', 30 | }).done(function(results) { 31 | $('#brandSelect').empty(); 32 | var source = $('#brand-modal-template').html(); 33 | var template = Handlebars.compile(source); 34 | var html = template(results.results); 35 | $('#brandSelect').append(html); 36 | var brand = $("#probBrand option:selected").val(); 37 | loadYearsAskModal(brand); 38 | }) 39 | } 40 | ////////////////////////////////////////////////////////////////////////// 41 | // function: loadYearsAskModal 42 | // parameters: brand id 43 | // description: This function loads the years for the brand selected. 44 | // return: none 45 | ////////////////////////////////////////////////////////////////////////// 46 | function loadYearsAskModal(id) { 47 | $.ajax({ 48 | url: '/api/models?brand=' + id, 49 | type: 'GET' 50 | }).done(function(results) { 51 | $('#yearSelect').empty() 52 | var bike = results.results 53 | var years = [] 54 | for (var i=0; i < bike.length; i++) { 55 | if (!(years.includes(bike[i].year))) { 56 | years.push(bike[i].year); 57 | } 58 | } 59 | years.sort(function(a, b) { return b-a }) 60 | var source = $('#year-modal-template').html(); 61 | var template = Handlebars.compile(source); 62 | var html = template(years); 63 | $('#yearSelect').append(html); 64 | loadModelsAskModal(); 65 | }); 66 | } 67 | ////////////////////////////////////////////////////////////////////////// 68 | // function: loadModelsAskModal 69 | // parameters: year 70 | // description: This function loads the models for the brand/year selected. 71 | // return: none 72 | //////////////////////////////////////////////////////////////////////////3 73 | function loadModelsAskModal(year) { 74 | var brandId = $("#probBrand option:selected").val(); 75 | $.ajax({ 76 | url: '/api/models?brand=' + brandId + '&year=' + year, 77 | type: 'GET' 78 | }).done(function(results) { 79 | $('#modelSelect').empty(); 80 | var bikes = results.results; 81 | var source = $('#model-modal-template').html(); 82 | var template = Handlebars.compile(source); 83 | var html = template(bikes); 84 | $('#modelSelect').append(html); 85 | }); 86 | } 87 | ////////////////////////////////////////////////////////////////////////// 88 | // function: loadSystemsAskModal 89 | // parameters: none 90 | // description: This function loads the systems for the select menu 91 | // return: none 92 | ////////////////////////////////////////////////////////////////////////// 93 | function loadSystemsAskModal() { 94 | $.ajax({ 95 | url: '/api/systems/', 96 | type: 'GET', 97 | }).done(function(results) { 98 | $('#systemSelect').empty(); 99 | var systems = results.results; 100 | var source = $('#system-modal-template').html(); 101 | var template = Handlebars.compile(source); 102 | var html = template(systems); 103 | $('#systemSelect').append(html); 104 | }) 105 | } 106 | 107 | ////////////////////////////////////////////////////////////////////////// 108 | // function: charRemainingText 109 | // parameters: none 110 | // description: This function displys chars remainging for problem text. 111 | // return: none 112 | ////////////////////////////////////////////////////////////////////////// 113 | function charRemainingText() { 114 | $('#probText').keyup(function () { 115 | var left = 500 - $(this).val().length; 116 | if (left < 0) { 117 | left = 0; 118 | } 119 | $('#counter').text('Characters left: ' + left); 120 | }) 121 | } 122 | ////////////////////////////////////////////////////////////////////////// 123 | // function: postProblem 124 | // parameters: none 125 | // description: This function posts a problem to the DB via Ajax. 126 | // return: none 127 | ////////////////////////////////////////////////////////////////////////// 128 | function postProblem() { 129 | var modal = $('[data-remodal-id=askModal]').remodal(); 130 | var bike = $("#probModel option:selected"); 131 | var sys = $("#probSystem option:selected"); 132 | var text = $("#probText"); 133 | var user = $("#userId"); 134 | var header = $("#probTitle"); 135 | var context = { 136 | system: sys.val(), 137 | description: text.val(), 138 | tech: user.val(), 139 | model: bike.val(), 140 | title: header.val(), 141 | } 142 | $.ajax({ 143 | url: '/api/post-problems/', 144 | type: 'POST', 145 | data: context, 146 | success: function (response) { 147 | modal.close(); 148 | alert("Problem saved successfully!"); 149 | }, 150 | error: function (xhr, ajaxOptions, thrownError) { 151 | $("p.error-message").empty(); 152 | if (thrownError === 'Bad Request') { 153 | $("p.error-message").append("All the fields are required to create a problem, please check if you've filled them properly and try again."); 154 | } else if (thrownError === 'Internal Server Error') { 155 | $("p.error-message").append("There were an internal error saving your problem. Please try again later or contact us describing your issue."); 156 | } else if (thrownError === 'Forbidden') { 157 | $("p.error-message").append("You don't have permissions to create a problem."); 158 | } else { 159 | $("p.error-message").append("Some weird problem occurred, the description is: Error code = " + xhr.status + " Error Message = " + thrownError + " please try again later or contact us!"); 160 | } 161 | } 162 | }).done(function(){ 163 | $('.newProblem').trigger('reset'); 164 | }); 165 | } 166 | 167 | // This function controls if the all the form fields have been filled and unblocks the submit button 168 | $(".newProblem").on('change mouseover', function(){ 169 | var bike = $("#probModel option:selected").val(); 170 | var sys = $("#probSystem option:selected").val(); 171 | var text = $("#probText").val(); 172 | var user = $("#userId").val(); 173 | var header = $("#probTitle").val(); 174 | if ((bike === 'none') || (sys === 'none') || !(text) || !(user) || !(header)){ 175 | $("input#newProbSubmit.submit-button").prop("disabled", true); 176 | } else { 177 | $("input#newProbSubmit.submit-button").prop("disabled", false); 178 | } 179 | }); 180 | ////////////////////////////////////////////////////////////////////////// 181 | // function: loadUnsolvedProblemsModal 182 | // parameters: none 183 | // description: This functions pulls unsolved problems from the DB via Ajax. 184 | // return: none 185 | ////////////////////////////////////////////////////////////////////////// 186 | function loadUnsolvedProblemsModal() { 187 | console.log("loadUnsolvedProblemsModal"); 188 | $.ajax({ 189 | url: '/api/get-problems?no_solutions=True', 190 | type: 'GET', 191 | }).done(function(results) { 192 | var problems = results.results; 193 | var source = $('#unsolved-problem-template').html(); 194 | var template = Handlebars.compile(source); 195 | var html = template(problems); 196 | $('#modalProblemList').empty(); 197 | $('#modalProblemList').append(html); 198 | }); 199 | } 200 | //////////////////////////////////////////////////////////////////////////////// 201 | // Helper: formatTime 202 | // parameters: timestamp 203 | // description: Formats timestamps to make them pretty and more human readable. 204 | // return: String of month/day/year 205 | //////////////////////////////////////////////////////////////////////////////// 206 | Handlebars.registerHelper('formatTime', function (posted) { 207 | var time = posted.replace('T', ':'); 208 | var date = time.split(":")[0]; 209 | var year = Number(date.split("-")[0]); 210 | var month = Number(date.split("-")[1]); 211 | var day = Number(date.split("-")[2]); 212 | var months = { 213 | "January": 1, 214 | "February ": 2, 215 | "March": 3, 216 | "April": 4, 217 | "May": 5, 218 | "June": 6, 219 | "July": 7, 220 | "August": 8, 221 | "September": 9, 222 | "October": 10, 223 | "November": 11, 224 | "December": 12, 225 | } 226 | for (var i in months) { 227 | if (month === months[i]) { 228 | month = i; 229 | } 230 | } 231 | return month + " " + day + " " + year; 232 | }) 233 | //////////////////////////////////////////////////////////////////////////////// 234 | // Helper: linkURL 235 | // parameters: problem object 236 | // description: Formats URL for a given problem. 237 | // return: String of URL for problem detail page. 238 | //////////////////////////////////////////////////////////////////////////////// 239 | Handlebars.registerHelper('linkURL', function (object) { 240 | id = Handlebars.Utils.escapeExpression(object.id); 241 | title = Handlebars.Utils.escapeExpression(object.title); 242 | url = '/problem_detail/' + id; 243 | return '' + title + ''; 244 | }); 245 | //////////////////////////////////////////////////////////////////////////////// 246 | // Helper: searchProblems 247 | // parameters: none 248 | // description: Search function for nav bar. 249 | // return: none 250 | //////////////////////////////////////////////////////////////////////////////// 251 | function searchProblems() { 252 | var searchTerm = $("#searchBox").val(); 253 | $.ajax({ 254 | url: '/api/get-problems?search=' + searchTerm, 255 | type: 'GET' 256 | }).done(function(results) { 257 | var problems = results.results; 258 | var length = problems.length; 259 | var message = '
    ' + "There are no problems that match your search."+ '
    ' + 260 | '' + "Post a new problem here" + ''; 261 | var noResults = { 262 | message: message, 263 | } 264 | if (length == 0) { 265 | var source = $('#search-problem-template-two').html(); 266 | var template = Handlebars.compile(source); 267 | var html = template(noResults); 268 | $('#searchProblemList').empty(); 269 | $('#searchProblemList').append(html); 270 | } else { 271 | var source = $('#search-problem-template').html(); 272 | var template = Handlebars.compile(source); 273 | var html = template(problems); 274 | $('#searchProblemList').empty(); 275 | $('#searchProblemList').append(html); 276 | } 277 | }); 278 | } 279 | //////////////////////////////////////////////////////////////////////////////// 280 | // Helper: loadNotificationsModal 281 | // parameters: tech id 282 | // description: Load notifications for a given user via Ajax. 283 | // return: String of month/day/year 284 | //////////////////////////////////////////////////////////////////////////////// 285 | /*IN PROGRESS*/ 286 | function loadNotificationsModal() { 287 | $.ajax({ 288 | url: '/api/notifications/', 289 | type: 'GET', 290 | }).done(function(results) { 291 | var notifications = results.results; 292 | var source = $('#notification-template').html(); 293 | var template = Handlebars.compile(source); 294 | var html = template(notifications); 295 | $('#notifyList').empty(); 296 | $('#notifyList').append(html); 297 | }); 298 | } 299 | -------------------------------------------------------------------------------- /diag_app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | {%load static%} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% block stylecontent %}{% endblock %} 13 | 14 | {% block titlecontent %}Base{% endblock %} 15 | 16 | {% block bodycontent %}{% endblock %} 17 |
    18 | 19 | 20 | 21 | 140 |
    141 | 142 | {% block content %} 143 | {% endblock %} 144 | 145 | {% block footercontent %} 146 |

    Copyright Bike MD 2017. All rights reserved.

    147 | {% endblock %} 148 | 149 | 150 | 151 | 152 | 153 | 154 | 168 | 169 | 170 | 179 | 180 | 181 | 182 | 196 | 197 | 198 | 199 | 211 | 212 | 213 | 225 | 226 | 227 | 239 | 240 | 241 | 253 | 254 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | {% block jscontent %}{% endblock %} 278 | 279 | 280 | --------------------------------------------------------------------------------