├── ocdaction ├── core │ ├── admin.py │ ├── __init__.py │ ├── models.py │ └── views.py ├── tests │ ├── __init__.py │ ├── apps.py │ ├── test_initial.py │ ├── profiles │ │ └── test_profiles.py │ ├── README.md │ └── factories.py ├── challenges │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0020_merge.py │ │ ├── 0011_remove_anxietyscore_user.py │ │ ├── 0008_anxiety-score-card-model-delete-user.py │ │ ├── 0019_challenge_in_progress.py │ │ ├── 0005_created_initial_anxiety_score_model_rename_task_name.py │ │ ├── 0015_removed-anxiety-score-model.py │ │ ├── 0022_auto_20180803_2007.py │ │ ├── 0024_auto_20180803_2008.py │ │ ├── 0004_created_initial_anxiety_score_model.py │ │ ├── 0007_anxiety-score-card-model-add-user.py │ │ ├── 0025_add_date_fields_to_anxietyscorecard.py │ │ ├── 0023_auto_20180803_2008.py │ │ ├── 0019_rename_fields_in_challenges.py │ │ ├── 0002_added_fears_compultions_goals_fields.py │ │ ├── 0001_split_up_tasks_separate_app.py │ │ ├── 0003_created_initial_anxiety_score_model.py │ │ ├── 0013_added-created-and-updated-fields-to-task-model.py │ │ ├── 0012_anxiety-score-card-model-add-time-periods.py │ │ ├── 0006_created_initial_anxiety_score_card_model.py │ │ ├── 0009_anxiety-score-card-model-allow-null-scores.py │ │ ├── 0010_anxiety-score-card-model-allow-blank-scores.py │ │ ├── 0017_auto_20170812_1624.py │ │ ├── 0016_updated-names-of-the-score-choices.py │ │ ├── 0018_updated_anxiety_score_card_model.py │ │ ├── 0021_auto_20180204_1956.py │ │ └── 0014_moved-score-choices-to-anxiety-score-card-model.py │ ├── templatetags │ │ ├── __init__.py │ │ └── challenges_tags.py │ ├── admin.py │ ├── urls.py │ ├── forms.py │ └── models.py ├── ocdaction │ ├── __init__.py │ ├── templates │ │ ├── core │ │ │ ├── team.html │ │ │ ├── contact.html │ │ │ └── learn.html │ │ ├── registration │ │ │ ├── password_reset_done.html │ │ │ ├── registration_complete.html │ │ │ ├── password_reset_complete.html │ │ │ ├── activation_complete.html │ │ │ ├── password_reset_confirm.html │ │ │ └── password_reset.html │ │ ├── profiles │ │ │ ├── my_account_delete.html │ │ │ ├── my_account_confirm.html │ │ │ ├── logout.html │ │ │ ├── login.html │ │ │ └── my_account.html │ │ ├── challenge │ │ │ ├── challenge_erase_my_record.html │ │ │ ├── challenge_add.html │ │ │ ├── challenge_edit.html │ │ │ ├── challenge_results.html │ │ │ ├── challenge_list_archived.html │ │ │ ├── challenge_view.html │ │ │ ├── challenge_chart.html │ │ │ ├── challenge_summary.html │ │ │ └── challenge_score_form.html │ │ ├── base.html │ │ ├── index.html │ │ └── layout.html │ ├── wsgi.py │ ├── settings │ │ ├── test.py │ │ ├── development.py │ │ ├── production.py │ │ └── __init__.py │ └── urls.py ├── profiles │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0005_delete_task.py │ │ ├── 0008_auto_20170102_1847.py │ │ ├── 0011_auto_20180202_2022.py │ │ ├── 0003_ocdactionuser_have_ocd.py │ │ ├── 0002_ocdactionuser_date_birth.py │ │ ├── 0007_auto_20170102_1102.py │ │ ├── 0004_task.py │ │ ├── 0010_auto_20170219_1943.py │ │ ├── 0006_task.py │ │ ├── 0009_remove_tasks_into_separate_app.py │ │ └── 0001_initial.py │ ├── apps.py │ ├── models.py │ ├── admin.py │ ├── forms.py │ ├── views.py │ └── urls.py ├── pytest.ini ├── static │ ├── img │ │ ├── nhslogo.jpg │ │ └── icon-hamburger-dark.svg │ └── scripts │ │ ├── ocdaction.min.js │ │ └── ocdaction.min.js.map └── manage.py ├── frontend ├── src │ ├── css │ │ └── styles.css │ ├── sass │ │ ├── inspinia │ │ │ ├── _mixins.scss │ │ │ ├── _typography.scss │ │ │ ├── inspinia.scss │ │ │ ├── _variables.scss │ │ │ ├── _badges_labels.scss │ │ │ ├── _metismenu.scss │ │ │ ├── _chat.scss │ │ │ ├── _theme-config.scss │ │ │ ├── _media.scss │ │ │ ├── _top_navigation.scss │ │ │ ├── _sidebar.scss │ │ │ └── _rtl.scss │ │ ├── components │ │ │ ├── _video-container.scss │ │ │ ├── _menu.scss │ │ │ ├── _custom-panel.scss │ │ │ ├── _contact-us.scss │ │ │ ├── _partners.scss │ │ │ ├── _hero.scss │ │ │ ├── _challenge-chart.scss │ │ │ ├── _challenge-summary.scss │ │ │ ├── _ibox-custom.scss │ │ │ ├── _footer.scss │ │ │ ├── _button.scss │ │ │ ├── _navigation.scss │ │ │ └── _forms.scss │ │ ├── partials │ │ │ ├── _colours.scss │ │ │ ├── _helpers.scss │ │ │ ├── _normalize.scss │ │ │ └── _typography.scss │ │ └── styles.scss │ ├── .DS_Store │ └── js │ │ └── scripts.js ├── node_modules │ └── .bin │ │ └── grunt ├── .DS_Store ├── package.json └── Gruntfile.js ├── Procfile ├── requirements ├── production.txt ├── testing.txt └── base.txt ├── setup.cfg ├── .gitignore ├── requirements.txt ├── PULL_REQUEST_TEMPLATE.md ├── .editorconfig ├── app.json ├── CONTRIBUTING.md ├── .circleci └── config.yml └── README.md /ocdaction/core/admin.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/css/styles.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ocdaction/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ocdaction/core/models.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ocdaction/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ocdaction/challenges/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ocdaction/profiles/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_mixins.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ocdaction/challenges/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/node_modules/.bin/grunt: -------------------------------------------------------------------------------- 1 | ../grunt/bin/grunt -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn --pythonpath ocdaction ocdaction.wsgi:application -------------------------------------------------------------------------------- /requirements/production.txt: -------------------------------------------------------------------------------- 1 | # Production requirements 2 | 3 | -r base.txt 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = l201,D100,D101,D102,D103,D104,D105,D400 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | frontend/.sass-cache/ 2 | frontend/node_modules 3 | .DS_Store 4 | **.pyc 5 | .env 6 | -------------------------------------------------------------------------------- /frontend/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/womenhackfornonprofits/ocdaction/HEAD/frontend/.DS_Store -------------------------------------------------------------------------------- /frontend/src/sass/components/_video-container.scss: -------------------------------------------------------------------------------- 1 | .video-container { 2 | padding-bottom: 2%; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/womenhackfornonprofits/ocdaction/HEAD/frontend/src/.DS_Store -------------------------------------------------------------------------------- /ocdaction/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | DJANGO_SETTINGS_MODULE = ocdaction.settings.development 3 | testpaths = tests -------------------------------------------------------------------------------- /frontend/src/sass/components/_menu.scss: -------------------------------------------------------------------------------- 1 | .menu { 2 | text-align: center; 3 | 4 | i { 5 | font-size: 60px; 6 | } 7 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Root requirements file 2 | # Required by Heroku to be here 3 | 4 | -r requirements/production.txt 5 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_custom-panel.scss: -------------------------------------------------------------------------------- 1 | .custom-panel { 2 | padding: 60px 0; 3 | margin-bottom: 90px; 4 | } 5 | -------------------------------------------------------------------------------- /ocdaction/static/img/nhslogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/womenhackfornonprofits/ocdaction/HEAD/ocdaction/static/img/nhslogo.jpg -------------------------------------------------------------------------------- /ocdaction/tests/apps.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.apps import AppConfig 4 | 5 | 6 | class TestsConfig(AppConfig): 7 | name = 'tests' 8 | -------------------------------------------------------------------------------- /ocdaction/profiles/apps.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.apps import AppConfig 4 | 5 | 6 | class ProfilesConfig(AppConfig): 7 | name = 'profiles' 8 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_contact-us.scss: -------------------------------------------------------------------------------- 1 | .contact { 2 | &__title { 3 | margin-bottom: .5em; 4 | } 5 | 6 | a { 7 | margin: 0; 8 | display: block; 9 | } 10 | p { 11 | margin: 0; 12 | } 13 | } -------------------------------------------------------------------------------- /ocdaction/tests/test_initial.py: -------------------------------------------------------------------------------- 1 | # from django.test import TestCase 2 | 3 | 4 | # Create your tests here. 5 | def add(x, y): 6 | return x + y 7 | 8 | 9 | def test_add(): 10 | assert add(1, 1) == 2 11 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/core/team.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Meet the team{% endblock %} 4 | 5 | {% load static %} 6 | 7 | {% block main %} 8 |
9 |

Meet the team

10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /ocdaction/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", "ocdaction.settings.development") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### ISSUE/TICKET NUMBER 2 | [ISSUE_XXX]() 3 | 4 | ### Describe the changes 5 | A few sentences describing the overall changes made in this Pull Request 6 | 7 | ### Screenshots (if appropriate) 8 | Add "n/a" if nothing to attach 9 | - Before 10 | - After 11 | 12 | ### Questions or comments (if any) 13 | Add "n/a" if nothing to say -------------------------------------------------------------------------------- /ocdaction/core/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | 3 | 4 | def home_index(request): 5 | """ 6 | The Home Index view. 7 | """ 8 | 9 | template_name = "index.html" 10 | if request.user.is_authenticated: 11 | return redirect('challenge-list') 12 | else: 13 | return render(request, template_name) 14 | -------------------------------------------------------------------------------- /ocdaction/static/scripts/ocdaction.min.js: -------------------------------------------------------------------------------- 1 | const navigation=document.getElementsByClassName("js-header-nav")[0],menuEl=document.getElementsByClassName("js-header-list")[0];navigation.addEventListener("click",function(a){const b=a.target.className;b.match("js-nav-toggle")?menuEl.classList.toggle("header__list--mobile"):menuEl.classList.remove("header__list--mobile")}); 2 | //# sourceMappingURL=ocdaction.min.js.map -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0020_merge.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-11-13 21:42 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0019_challenge_in_progress'), 12 | ('challenges', '0019_rename_fields_in_challenges'), 13 | ] 14 | 15 | operations = [ 16 | ] 17 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_typography.scss: -------------------------------------------------------------------------------- 1 | h1, h2, h3, h4, h5, h6 { 2 | font-weight: 100; 3 | } 4 | 5 | h1 { 6 | font-size: 30px; 7 | } 8 | 9 | h2 { 10 | font-size: 24px; 11 | } 12 | 13 | h3 { 14 | font-size: 16px; 15 | } 16 | 17 | h4 { 18 | font-size: 14px; 19 | } 20 | 21 | h5 { 22 | font-size: 12px; 23 | } 24 | 25 | h6 { 26 | font-size: 10px; 27 | } 28 | 29 | h3, h4, h5 { 30 | margin-top: 5px; 31 | font-weight: 600; 32 | } -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0005_delete_task.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-01-01 17:50 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('profiles', '0004_task'), 12 | ] 13 | 14 | operations = [ 15 | migrations.DeleteModel( 16 | name='Challenge', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /frontend/src/js/scripts.js: -------------------------------------------------------------------------------- 1 | const navigation = document.getElementsByClassName('js-header-nav')[0]; 2 | const menuEl = document.getElementsByClassName('js-header-list')[0] 3 | 4 | navigation.addEventListener('click', function (event) { 5 | const srcElementClass = event.target.className; 6 | if (srcElementClass.match('js-nav-toggle')) { 7 | menuEl.classList.toggle('header__list--mobile') 8 | } else { 9 | menuEl.classList.remove('header__list--mobile') 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_partners.scss: -------------------------------------------------------------------------------- 1 | .partners { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | &__image { 6 | height: 3em; 7 | margin: 1em auto; 8 | } 9 | 10 | &__text { 11 | text-align: center; 12 | margin: 0; 13 | 14 | @media (min-width: 780px) { 15 | text-align: left; 16 | position: absolute; 17 | top: -18px; 18 | } 19 | } 20 | 21 | @media (min-width: 480px) { 22 | flex-direction: row; 23 | align-items: center; 24 | } 25 | } -------------------------------------------------------------------------------- /ocdaction/ocdaction/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for ocdaction project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ 8 | """ 9 | 10 | from whitenoise.django import DjangoWhiteNoise 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | application = get_wsgi_application() 15 | application = DjangoWhiteNoise(application) 16 | -------------------------------------------------------------------------------- /ocdaction/static/scripts/ocdaction.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../../frontend/src/js/scripts.js"],"names":["navigation","document","getElementsByClassName","menuEl","addEventListener","event","srcElementClass","target","className","match","classList","toggle","remove"],"mappings":"AAAA,KAAMA,YAAaC,SAASC,uBAAuB,iBAAiB,GAC9DC,OAASF,SAASC,uBAAuB,kBAAkB,EAEjEF,YAAWI,iBAAiB,QAAS,SAAUC,GAC3C,KAAMC,GAAkBD,EAAME,OAAOC,SACpCF,GAAgBG,MAAM,iBACzBN,OAAOO,UAAUC,OAAO,wBAExBR,OAAOO,UAAUE,OAAO","file":"ocdaction.min.js"} -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/registration/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Password Reset Email Sent{% endblock %} 4 | 5 | {% block main %} 6 |
7 |
8 |

Email sent

9 |

If the email provided is registered with us, you will receive password reset instructions.

10 |
11 |
12 | {% endblock %} 13 | 14 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/registration/registration_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Registration Complete{% endblock %} 4 | 5 | {% load static %} 6 | 7 | {% block main %} 8 |
9 |
10 |

Thanks for registering!

11 |

You will have to activate your account before you can login, please check your email.

12 |
13 |
14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /ocdaction/tests/profiles/test_profiles.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.factories import UserFactory 4 | from profiles.models import OCDActionUser 5 | 6 | @pytest.mark.django_db 7 | def test_create_user(): 8 | 9 | # Check there are 0 users before a new user is added 10 | number_users = OCDActionUser.objects.count() 11 | assert number_users == 0 12 | 13 | # Check there is 1 user after a new user is added 14 | UserFactory.create() 15 | number_users = OCDActionUser.objects.count() 16 | assert number_users == 1 17 | 18 | -------------------------------------------------------------------------------- /ocdaction/challenges/templatetags/challenges_tags.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | @register.simple_tag 6 | def render_anxiety_level(score): 7 | if score: 8 | anxiety_level = score 9 | else: 10 | anxiety_level = "-" 11 | return anxiety_level 12 | 13 | @register.simple_tag 14 | def get_anxiety_level(challenge): 15 | anxiety_level = challenge.get_latest_initial_anxiety_level() 16 | if anxiety_level == -1: 17 | return "-" 18 | else: 19 | return anxiety_level 20 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0011_remove_anxietyscore_user.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-21 19:43 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0010_anxiety-score-card-model-allow-blank-scores'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='anxietyscore', 17 | name='user', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0008_auto_20170102_1847.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-01-02 18:47 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('profiles', '0007_auto_20170102_1102'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='challenge', 17 | old_name='username', 18 | new_name='user', 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0008_anxiety-score-card-model-delete-user.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-17 20:02 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0007_anxiety-score-card-model-add-user'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='anxietyscorecard', 17 | name='user', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0011_auto_20180202_2022.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-02-02 20:22 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('profiles', '0010_auto_20170219_1943'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='ocdactionuser', 17 | old_name='username', 18 | new_name='nickname', 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /ocdaction/profiles/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.db import models 3 | from custom_user.models import AbstractEmailUser 4 | 5 | 6 | class OCDActionUser(AbstractEmailUser): 7 | """ User is authenticated with their email 8 | but for app purposes users also have usernames. 9 | """ 10 | nickname = models.CharField(max_length=24, null=True) 11 | date_birth = models.DateField('date of birth', null=True) 12 | have_ocd = models.BooleanField(default=False) 13 | 14 | def __str__(self): 15 | return self.nickname 16 | -------------------------------------------------------------------------------- /ocdaction/tests/README.md: -------------------------------------------------------------------------------- 1 | ## OCD Action Django App 2 | 3 | Our tests package: `ocdaction.tests` is a valid Django app and should 4 | be installed in Django's `INSTALLED_APPS` setting during test suite runs. 5 | 6 | ## Test Placement 7 | 8 | The tests folder is laid out in such a way that it mirrors as closely as 9 | possible the actual codebase. 10 | 11 | ### Example 12 | 13 | Here is an example to show you how new test files should be placed: 14 | 15 | `ocdaction/apps/profiles/example_file.py` 16 | 17 | is tested at: 18 | 19 | `ocdaction/tests/apps/profiles/test_example_file.py` 20 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0003_ocdactionuser_have_ocd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2016-10-06 19:23 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('profiles', '0002_ocdactionuser_date_birth'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='ocdactionuser', 17 | name='have_ocd', 18 | field=models.BooleanField(default=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0019_challenge_in_progress.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-11-13 19:14 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0018_updated_anxiety_score_card_model'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='challenge', 17 | name='in_progress', 18 | field=models.BooleanField(default=False), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /requirements/testing.txt: -------------------------------------------------------------------------------- 1 | # Testing Requirements 2 | # For requirements that CI needs for testing 3 | 4 | -r base.txt 5 | 6 | factory_boy==2.9.2 # For fixture generation 7 | flake8==3.3.0 # For linting the files 8 | pytest==3.3.2 # Our Python testing library 9 | pytest-django==3.1.2 # Integrates pytest with django 10 | pytest-factoryboy==1.3.0 # Factory_Boy integration with pytest fixtures 11 | pytest-sugar==0.8.0 # For better pytest output 12 | tox==2.3.2 # Standardises testing in Python 13 | pytest-pythonpath==0.7.1 # explicitly setting the project on the python path -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*.py] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.js] 13 | indent_style = space 14 | indent_size = 4 15 | trim_trailing_whitespace = true 16 | insert_final_newline = false 17 | 18 | [*.scss] 19 | indent_style = space 20 | indent_size = 4 21 | trim_trailing_whitespace = true 22 | insert_final_newline = false 23 | 24 | [*.html] 25 | indent_style = space 26 | indent_size = 4 27 | 28 | [{*.json,*.yml}] 29 | indent_style = space 30 | indent_size = 4 31 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0005_created_initial_anxiety_score_model_rename_task_name.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-05 13:58 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0004_created_initial_anxiety_score_model'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='challenge', 17 | old_name='taskname', 18 | new_name='challenge_name', 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/settings/test.py: -------------------------------------------------------------------------------- 1 | from ocdaction.settings import * 2 | 3 | import django 4 | 5 | SECRET_KEY = 'FAKEforTEST' 6 | 7 | INSTALLED_APPS = INSTALLED_APPS + [ 8 | 'tests', 9 | ] 10 | 11 | 12 | class DisableMigrations(object): 13 | 14 | def __init__(self): 15 | self._django_version = django.VERSION 16 | 17 | def __contains__(self, item): 18 | return True 19 | 20 | def __getitem__(self, item): 21 | if self._django_version >= (1, 9): 22 | return None 23 | else: 24 | return 'notmigrations' 25 | 26 | 27 | MIGRATION_MODULES = DisableMigrations() 28 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0002_ocdactionuser_date_birth.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2016-09-19 20:13 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('profiles', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='ocdactionuser', 17 | name='date_birth', 18 | field=models.DateField(blank=True, null=True, verbose_name='date of birth'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0015_removed-anxiety-score-model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-07-08 17:41 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0014_moved-score-choices-to-anxiety-score-card-model'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='anxietyscore', 17 | name='challenge', 18 | ), 19 | migrations.DeleteModel( 20 | name='AnxietyScore', 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_hero.scss: -------------------------------------------------------------------------------- 1 | .hero { 2 | position: relative; 3 | max-width: 75%; 4 | margin: auto; 5 | 6 | @media (min-width: 740px) { 7 | max-width: 90%; 8 | } 9 | 10 | &__img { 11 | width: 80%; 12 | margin: 45px auto; 13 | margin-top: 0; 14 | 15 | @media (min-width: 400px) { 16 | display:block; 17 | } 18 | 19 | @media (min-width: 480px) { 20 | width: 60%; 21 | } 22 | @media (min-width: 740px) { 23 | width: 80%; 24 | margin: 55px auto; 25 | } 26 | } 27 | 28 | &__overlay { 29 | position: absolute; 30 | bottom: 33%; 31 | 32 | @media (min-width: 480px) { 33 | bottom: 40%; 34 | } 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_challenge-chart.scss: -------------------------------------------------------------------------------- 1 | .challenge-chart { 2 | margin-top: 25px; 3 | 4 | p { 5 | font-weight: 800; 6 | text-align: center; 7 | padding-top: 1px; 8 | } 9 | 10 | hr { 11 | border-top: 1px solid $dark-grey; 12 | } 13 | } 14 | 15 | .challenge-chart--graph { 16 | margin-left: -7px; 17 | margin-bottom: 64px; 18 | 19 | @media (min-width: 740px) { 20 | margin-left: 30%; 21 | } 22 | } 23 | 24 | .challenge-chart--graph-result { 25 | margin-left: -7px; 26 | 27 | @media (min-width: 740px) { 28 | margin-top: 25px; 29 | margin-bottom: 0; 30 | margin-left: 32%; 31 | } 32 | } -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/registration/password_reset_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Password Reset Complete{% endblock %} 4 | 5 | {% load static %} 6 | 7 | {% block main %} 8 |
9 |
10 |

Your password was reset successfully. You can now sign in with your new password.

11 |
12 |
13 | Sign In 14 |
15 |
16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0007_auto_20170102_1102.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-01-02 11:02 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('profiles', '0006_task'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='challenge', 17 | old_name='challenge_name', 18 | new_name='taskname', 19 | ), 20 | migrations.RenameField( 21 | model_name='challenge', 22 | old_name='user', 23 | new_name='username', 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /requirements/base.txt: -------------------------------------------------------------------------------- 1 | # Core requirements 2 | # that are needed for all environments 3 | 4 | Django==1.11.15 5 | gunicorn==19.7.1 6 | psycopg2==2.7.3.2 7 | whitenoise==3.3.1 8 | wsgiref==0.1.2 9 | django-custom-user==0.6 10 | django-registration-redux==2.1 11 | dj-database-url==0.4.2 12 | django-widget-tweaks==1.4.1 # Tweaks form field rendering in templates, not in Python 13 | boto==2.48.0 # Sending emails using Amazon SES 14 | django-ses==0.8.2 # Sending emails using Amazon SES 15 | boto3==1.5.22 # AWS S3 setup for static and media file storage 16 | django-storages==1.6.5 # AWS S3 setup for static and media file storage 17 | -------------------------------------------------------------------------------- /frontend/src/sass/partials/_colours.scss: -------------------------------------------------------------------------------- 1 | $white-colour: #fff; 2 | $off-white-colour: #f4f4f4; 3 | $dark-grey: #979797; 4 | $grey: #fafafa; 5 | $light-grey: #9b9b9b; 6 | $black-colour: #333; 7 | $lighter-black-colour: #444; 8 | 9 | $salmon-pink: orange; 10 | $nav-link-color: #3c3c3c; 11 | $nav-link-visited-color: blue; 12 | 13 | $ocd-colour: #4cc1a7; 14 | $ocd-dark-colour: #647975; 15 | 16 | $archive-background-colour: #FDB01E19; 17 | 18 | // Body Text 19 | $body-text-colour: #333; 20 | $text-color: #333; 21 | 22 | $pastel-green: #e7f6f3; 23 | 24 | // Links 25 | $link-colour: $ocd-colour; 26 | $link-active-colour: $ocd-dark-colour; 27 | $link-disabled-colour: #808080; 28 | $ibox-link-color: #333; 29 | 30 | // Buttons 31 | $btn-disabled-color: #dddddd; 32 | -------------------------------------------------------------------------------- /ocdaction/profiles/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from custom_user.admin import EmailUserAdmin 3 | from .models import OCDActionUser 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | class OCDActionUserAdmin(EmailUserAdmin): 8 | """ 9 | You can customize the interface of your model here. 10 | """ 11 | 12 | fieldsets = ( 13 | (None, {'fields': (('email', 'username', 'date_birth', 'have_ocd', 14 | 'password'))}), 15 | (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 16 | 'groups', 'user_permissions')}), 17 | (_('Important dates'), {'fields': ('last_login', 'date_joined')}), 18 | ) 19 | 20 | 21 | admin.site.register(OCDActionUser, OCDActionUserAdmin) 22 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/registration/activation_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Activation Complete{% endblock %} 4 | 5 | {% load static %} 6 | 7 | {% block main %} 8 |
9 |
10 |

Account Active

11 |

Your account is now active. You can now sign in with your email and password.

12 |
13 |
14 | Sign In 15 |
16 |
17 |
18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0004_task.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-01-01 17:49 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('profiles', '0003_ocdactionuser_have_ocd'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Challenge', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('name', models.CharField(blank=True, max_length=100)), 20 | ('is_archived', models.BooleanField(default=False)), 21 | ], 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_challenge-summary.scss: -------------------------------------------------------------------------------- 1 | .challenge-summary { 2 | margin-top: 25px; 3 | margin-bottom: 90px; 4 | 5 | p { 6 | font-weight: 800; 7 | text-align: center; 8 | padding-top: 1px; 9 | } 10 | 11 | hr { 12 | border-top: 1px solid $dark-grey; 13 | } 14 | 15 | &--archive { 16 | background-color: $archive-background-colour; 17 | } 18 | } 19 | 20 | .challenge-summary--table { 21 | margin: 0 auto 20px; 22 | padding-bottom: 50px; 23 | color: $black-colour; 24 | 25 | th div { 26 | font-size: 0.7em; 27 | text-align: center; 28 | font-weight: 600; 29 | } 30 | 31 | td { 32 | font-size: 18px; 33 | padding: 8px 15px; 34 | text-align: left; 35 | } 36 | } -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OCD Action App", 3 | "description": "", 4 | "scripts": { 5 | }, 6 | "env": { 7 | "AWS_ACCESS_KEY_ID": { 8 | "required": true 9 | }, 10 | "AWS_SECRET_ACCESS_KEY": { 11 | "required": true 12 | }, 13 | "DEBUG_COLLECTSTATIC": { 14 | "required": true 15 | }, 16 | "DJANGO_SETTINGS_MODULE": { 17 | "required": true 18 | }, 19 | "SECRET_KEY": { 20 | "required": true 21 | }, 22 | "SENDGRID_PASSWORD": { 23 | "required": true 24 | }, 25 | "SENDGRID_USERNAME": { 26 | "required": true 27 | } 28 | }, 29 | "formation": { 30 | }, 31 | "addons": [ 32 | "heroku-postgresql", 33 | "sendgrid" 34 | ], 35 | "buildpacks": [ 36 | { 37 | "url": "heroku/python" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0010_auto_20170219_1943.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-19 19:43 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('profiles', '0009_remove_tasks_into_separate_app'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='ocdactionuser', 17 | name='date_birth', 18 | field=models.DateField(null=True, verbose_name='date of birth'), 19 | ), 20 | migrations.AlterField( 21 | model_name='ocdactionuser', 22 | name='username', 23 | field=models.CharField(max_length=24, null=True), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0022_auto_20180803_2007.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-08-03 20:07 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import uuid 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('challenges', '0021_auto_20180204_1956'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='anxietyscorecard', 18 | name='uuid', 19 | field=models.UUIDField(default=uuid.uuid4, editable=False, null=True), 20 | ), 21 | migrations.AddField( 22 | model_name='challenge', 23 | name='uuid', 24 | field=models.UUIDField(default=uuid.uuid4, editable=False, null=True), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0024_auto_20180803_2008.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-08-03 20:08 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import uuid 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('challenges', '0023_auto_20180803_2008'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='anxietyscorecard', 18 | name='uuid', 19 | field=models.UUIDField(default=uuid.uuid4, editable=False, unique=True), 20 | ), 21 | migrations.AlterField( 22 | model_name='challenge', 23 | name='uuid', 24 | field=models.UUIDField(default=uuid.uuid4, editable=False, unique=True), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0004_created_initial_anxiety_score_model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-03 20:41 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 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('challenges', '0003_created_initial_anxiety_score_model'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='anxietyscore', 20 | name='user', 21 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 22 | preserve_default=False, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0007_anxiety-score-card-model-add-user.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-17 19:58 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 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('challenges', '0006_created_initial_anxiety_score_card_model'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='anxietyscorecard', 20 | name='user', 21 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 22 | preserve_default=False, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_ibox-custom.scss: -------------------------------------------------------------------------------- 1 | .ibox-content { 2 | color: $body-text-colour; 3 | font-weight: 400; 4 | border-style: none; 5 | border: 0; 6 | 7 | a { 8 | color: $ibox-link-color; 9 | } 10 | } 11 | 12 | .table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { 13 | padding: 12px 6px; 14 | } 15 | 16 | .ibox-title { 17 | border: 0; 18 | } 19 | 20 | .ibox-tools { 21 | position: relative; 22 | 23 | * { 24 | color: $black-colour; 25 | } 26 | 27 | &-cta { 28 | position: absolute; 29 | right: 0; 30 | transform: translateY(-50%); 31 | top: 50%; 32 | display: flex; 33 | align-items: center; 34 | } 35 | 36 | @media (max-width: 350px) { 37 | display: block; 38 | } 39 | } -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0025_add_date_fields_to_anxietyscorecard.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-12-08 14:10 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('challenges', '0024_auto_20180803_2008'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='anxietyscorecard', 18 | name='created_at', 19 | field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), 20 | preserve_default=False, 21 | ), 22 | migrations.AddField( 23 | model_name='anxietyscorecard', 24 | name='updated_at', 25 | field=models.DateTimeField(auto_now=True), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ocdaction", 3 | "version": "1.0.0", 4 | "description": "OCD Action", 5 | "main": "Gruntfile.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/womenhackfornonprofits/ocdaction.git" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "bugs": { 13 | "url": "https://github.com/womenhackfornonprofits/ocdaction/issues" 14 | }, 15 | "homepage": "https://github.com/womenhackfornonprofits/ocdaction#readme", 16 | "devDependencies": { 17 | "bootstrap-sass": "^3.3.7", 18 | "grunt": "^1.0.1", 19 | "grunt-contrib-copy": "^1.0.0", 20 | "grunt-contrib-cssmin": "^1.0.1", 21 | "grunt-contrib-sass": "^1.0.0", 22 | "grunt-contrib-uglify": "^1.0.1", 23 | "grunt-contrib-watch": "^1.0.0", 24 | "scss-lint": "0.0.0" 25 | }, 26 | "dependencies": { 27 | "grunt-sync": "^0.6.2", 28 | "tar": "^4.4.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0023_auto_20180803_2008.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-08-03 20:08 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | import uuid 7 | 8 | def gen_uuid(apps, schema_editor): 9 | Challenge = apps.get_model('challenges', 'Challenge') 10 | for row in Challenge.objects.all(): 11 | row.uuid = uuid.uuid4() 12 | row.save(update_fields=['uuid']) 13 | AnxietyScoreCard = apps.get_model('challenges', 'AnxietyScoreCard') 14 | for row in AnxietyScoreCard.objects.all(): 15 | row.uuid = uuid.uuid4() 16 | row.save(update_fields=['uuid']) 17 | 18 | 19 | class Migration(migrations.Migration): 20 | 21 | dependencies = [ 22 | ('challenges', '0022_auto_20180803_2007'), 23 | ] 24 | 25 | operations = [ 26 | migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop), 27 | ] 28 | -------------------------------------------------------------------------------- /ocdaction/challenges/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Challenge, AnxietyScoreCard 3 | 4 | 5 | class ChallengeAdmin(admin.ModelAdmin): 6 | list_select_related = True 7 | list_display = ('challenge_name', 'user', 'is_archived', 'in_progress') 8 | search_fields = ['challenge_name'] 9 | 10 | 11 | class AnxietyScoreCardAdmin(admin.ModelAdmin): 12 | list_select_related = True 13 | list_display = ( 14 | 'challenge', 15 | 'user_name', 16 | 'anxiety_at_0_min', 17 | 'anxiety_at_5_min', 18 | 'anxiety_at_10_min', 19 | 'anxiety_at_15_min', 20 | 'anxiety_at_30_min', 21 | 'anxiety_at_60_min', 22 | 'anxiety_at_120_min' 23 | ) 24 | search_fields = ['challenge__challenge_name'] 25 | 26 | 27 | # Register your models here. 28 | admin.site.register(Challenge, ChallengeAdmin) 29 | admin.site.register(AnxietyScoreCard, AnxietyScoreCardAdmin) 30 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0019_rename_fields_in_challenges.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-11-13 18:55 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0018_updated_anxiety_score_card_model'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='Challenge', 17 | old_name='challenge_compulsions', 18 | new_name='compulsion', 19 | ), 20 | migrations.RenameField( 21 | model_name='Challenge', 22 | old_name='challenge_fears', 23 | new_name='exposure', 24 | ), 25 | migrations.RenameField( 26 | model_name='Challenge', 27 | old_name='challenge_goals', 28 | new_name='obsession', 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/profiles/my_account_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% load static %} 4 | {% block title %}My Account{% endblock %} 5 | 6 | {% block main %} 7 |
8 |

My Account

9 |
10 |
11 |
Are you sure you want to delete your account?
12 |

Once deleted, you cannot recover your account. 13 |

14 | 17 | 20 |
21 | 22 |
23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_footer.scss: -------------------------------------------------------------------------------- 1 | .global-footer { 2 | background: $lighter-black-colour; 3 | clear: both; 4 | 5 | &__list { 6 | list-style: none; 7 | display: block; 8 | margin: 0; 9 | text-align: left; 10 | padding: 0; 11 | } 12 | 13 | &__category { 14 | text-align: left; 15 | color: $off-white-colour; 16 | } 17 | 18 | &__item { 19 | list-style: none; 20 | display: block; 21 | margin: 0; 22 | 23 | &:hover { 24 | color: $ocd-colour; 25 | } 26 | } 27 | 28 | &__link { 29 | color: $off-white-colour; 30 | display: block; 31 | box-sizing: border-box; 32 | font-weight: 400; 33 | text-decoration: none; 34 | 35 | &:hover { 36 | text-decoration: underline; 37 | color: $off-white-colour; 38 | } 39 | 40 | } 41 | 42 | &__legal { 43 | text-align: center; 44 | color: $off-white-colour; 45 | margin: 30px 0; 46 | 47 | a { 48 | font-weight: 400; 49 | text-decoration: none; 50 | } 51 | 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/urls.py: -------------------------------------------------------------------------------- 1 | """ocdaction URL Configuration 2 | main urls file, it imports all the app urls separately for tidiness. 3 | """ 4 | from django.conf.urls import url, include 5 | from django.contrib import admin 6 | from django.views.generic.base import TemplateView 7 | 8 | from core.views import home_index 9 | 10 | urlpatterns = [ 11 | url( 12 | r'^admin/', 13 | admin.site.urls 14 | ), 15 | url( 16 | r'^contact/$', 17 | TemplateView.as_view(template_name='core/contact.html'), 18 | name="contact" 19 | ), 20 | url( 21 | r'^learn/$', 22 | TemplateView.as_view(template_name='core/learn.html'), 23 | name="learn" 24 | ), 25 | url( 26 | r'^$', 27 | home_index, 28 | name="index" 29 | ), 30 | url( 31 | r'', 32 | include('profiles.urls') 33 | ), 34 | url( 35 | r'^challenges/', 36 | include('challenges.urls') 37 | ), 38 | ] 39 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0006_task.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-01-01 18:04 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 | dependencies = [ 13 | ('profiles', '0005_delete_task'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Challenge', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('challenge_name', models.CharField(blank=True, max_length=100)), 22 | ('is_archived', models.BooleanField(default=False)), 23 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/challenge/challenge_erase_my_record.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% load static %} 4 | {% block title %}My Account{% endblock %} 5 | 6 | {% block main %} 7 |
8 |

My Account

9 |
10 |
11 |
Are you sure you want to erase all your challenges records on OCD Youth?
12 |

Once erased you cannot recover the record. 13 |

14 | 17 | 20 |
21 | 22 |
23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/profiles/my_account_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% load static %} 4 | {% block title %}My Account{% endblock %} 5 | 6 | {% block main %} 7 | 8 |
9 |

My Account

10 |
11 | 12 | {% if deleted_user %} 13 | 14 |
15 |
Your account is now deleted
16 |

You can return to the homepage. 17 |

18 |
19 | 20 | {% else %} 21 | 22 |
23 |
Your record is now deleted
24 |

You can return to the challenges page to add new challenges. 25 |

26 |
27 | 28 | {% endif %} 29 | 30 |
31 |
32 | 33 | 34 | 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 1. Come say hi in the #ocd-action Slack channel before grabbing a ticket 3 | 2. Fork the repository or ask to be added to the team on Github (you will need to have a github account and share your github username) 4 | 3. Assign an issue to yourself and start working. All changes must be done on a new branch so you can raise a PR. Checkout a new branch from `master` 5 | 4. Once ready, please raise a Pull Request. 6 | 7 | # How to raise a Pull Request 8 | In order to raise a Pull Request with your changes (please make sure all changes are done on a branch): 9 | 10 | - Save and commit your changes 11 | - Push your changes and create a pull request: `git push origin your_branch_name` 12 | - Go to the [repo](https://github.com/womenhackfornonprofits/ocdaction) and you should see a green button "Raise a Pull Request" 13 | ![](https://help.github.com/assets/images/help/pull_requests/pull-request-click-to-create.png) 14 | - Describe all the changes you have made in details and save. 15 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0002_added_fears_compultions_goals_fields.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-01-15 12:26 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0001_split_up_tasks_separate_app'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='challenge', 17 | name='challenge_compulsions', 18 | field=models.CharField(blank=True, max_length=300), 19 | ), 20 | migrations.AddField( 21 | model_name='challenge', 22 | name='challenge_fears', 23 | field=models.CharField(blank=True, max_length=300), 24 | ), 25 | migrations.AddField( 26 | model_name='challenge', 27 | name='challenge_goals', 28 | field=models.CharField(blank=True, max_length=300), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/profiles/logout.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Logout{% endblock %} 4 | 5 | {% block panel_title %}Logout{% endblock %} 6 | 7 | {% load static %} 8 | 9 | {% block main %} 10 |
11 |
12 | {% csrf_token %} 13 |
14 |
15 |

You are now logged out

16 |
Thank you and have a great day
17 |
18 |
19 |
20 | Log in again 21 |
22 |
23 |
24 |
25 |
26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0001_split_up_tasks_separate_app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-01-15 12:20 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='Challenge', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('taskname', models.CharField(max_length=100)), 24 | ('is_archived', models.BooleanField(default=False)), 25 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ], 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0003_created_initial_anxiety_score_model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-03 20:13 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 | ('challenges', '0002_added_fears_compultions_goals_fields'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='AnxietyScore', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('score', models.CharField(choices=[('0', 'Zero'), ('1', 'One'), ('2', 'Two'), ('3', 'Three'), ('4', 'Four'), ('5', 'Five'), ('6', 'Six'), ('7', 'Seven'), ('8', 'Eight'), ('9', 'Nine'), ('10', 'Ten')], default='0', max_length=2)), 21 | ('challenge', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='challenges.Challenge')), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0013_added-created-and-updated-fields-to-task-model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-03-26 16:04 3 | from __future__ import unicode_literals 4 | 5 | import datetime 6 | from django.db import migrations, models 7 | from django.utils.timezone import utc 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('challenges', '0012_anxiety-score-card-model-add-time-periods'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='challenge', 19 | name='created_at', 20 | field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2017, 3, 26, 16, 4, 44, 39503, tzinfo=utc)), 21 | preserve_default=False, 22 | ), 23 | migrations.AddField( 24 | model_name='challenge', 25 | name='updated_at', 26 | field=models.DateTimeField(auto_now=True, default=datetime.datetime(2017, 3, 26, 16, 4, 58, 789791, tzinfo=utc)), 27 | preserve_default=False, 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/inspinia.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * INSPINIA - Responsive Admin Theme 4 | * version 2.7 5 | * 6 | */ 7 | // Google Fonts 8 | @import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700"); 9 | 10 | // Variables, Mixins 11 | @import "variables"; 12 | @import "mixins"; 13 | 14 | // INSPINIA Theme Elements 15 | @import "typography"; 16 | // @import "navigation"; 17 | // @import "top_navigation"; 18 | @import "buttons"; 19 | @import "badges_labels"; 20 | @import "elements"; 21 | @import "sidebar"; 22 | @import "base"; 23 | @import "pages"; 24 | @import "chat"; 25 | @import "metismenu"; 26 | @import "spinners"; 27 | 28 | // Landing page styles 29 | //@import "landing"; 30 | 31 | // RTL Support 32 | @import "rtl"; 33 | 34 | // For demo only - config box style 35 | //@import "theme-config"; 36 | 37 | // INSPINIA Skins 38 | @import "skins"; 39 | @import "md-skin"; 40 | 41 | // Media query style 42 | @import "media"; 43 | 44 | // Clear layout on print mode 45 | @media print { 46 | nav.navbar-static-side { 47 | display: none; 48 | } 49 | body { overflow: visible !important; } 50 | 51 | #page-wrapper { 52 | margin: 0; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ocdaction/tests/factories.py: -------------------------------------------------------------------------------- 1 | import factory.django 2 | 3 | from profiles.models import OCDActionUser 4 | from challenges.models import Challenge 5 | import datetime 6 | 7 | 8 | # Factories 9 | class UserFactory(factory.django.DjangoModelFactory): 10 | class Meta: 11 | model = OCDActionUser 12 | django_get_or_create = ( 13 | 'nickname', 14 | 'date_birth', 15 | 'have_ocd' 16 | ) 17 | 18 | nickname = factory.Sequence(lambda n: 'nickname_{}'.format(n)) 19 | date_birth = datetime.date(2000, 1, 2) 20 | have_ocd = True 21 | email = factory.Sequence(lambda n: 'email_{}'.format(n)) 22 | 23 | 24 | class ChallengeFactory(factory.django.DjangoModelFactory): 25 | class Meta: 26 | model = Challenge 27 | django_get_or_create = ( 28 | 'challenge_name', 29 | 'is_archived', 30 | 'obsession', 31 | 'compulsion', 32 | 'exposure' 33 | ) 34 | 35 | challenge_name = factory.Sequence(lambda n: 'challengename_{}'.format(n)) 36 | is_archived = False 37 | in_progress = False 38 | obsession = 'obsession' 39 | compulsion = 'compulsion' 40 | exposure = 'exposure' 41 | user = factory.SubFactory(UserFactory) 42 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/challenge/challenge_add.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Add new challenge{% endblock %} 4 | 5 | {% load static widget_tweaks %} 6 | 7 | {% block main %} 8 |
9 |
10 |
11 | 12 | 13 | Back 14 | 15 | 16 |
17 |

Add new challenge

18 |
19 | 20 |
21 | {% csrf_token %} 22 | {{ challenge_form.non_field_errors }} 23 | {{ challenge_form.as_p }} 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/settings/development.py: -------------------------------------------------------------------------------- 1 | from ocdaction.settings import * 2 | 3 | 4 | SECRET_KEY = 'FAKEforDEV' 5 | 6 | DEBUG = True 7 | 8 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 9 | 10 | LOGGING = { 11 | 'version': 1, 12 | 'disable_existing_loggers': False, 13 | 'formatters': { 14 | 'verbose': { 15 | 'format': ('%(asctime)s [%(process)d] [%(levelname)s] ' + 16 | 'pathname=%(pathname)s lineno=%(lineno)s ' + 17 | 'funcname=%(funcName)s %(message)s'), 18 | 'datefmt': '%Y-%m-%d %H:%M:%S' 19 | }, 20 | 'simple': { 21 | 'format': '%(levelname)s %(message)s' 22 | } 23 | }, 24 | 'handlers': { 25 | 'null': { 26 | 'level': 'DEBUG', 27 | 'class': 'logging.NullHandler', 28 | }, 29 | 'console': { 30 | 'level': 'DEBUG', 31 | 'class': 'logging.StreamHandler', 32 | 'formatter': 'simple' 33 | } 34 | }, 35 | 'loggers': { 36 | 'django.request': { 37 | 'handlers': ['console'], 38 | 'level': 'ERROR', 39 | }, 40 | 'fashrevwall': { 41 | 'handlers': ['console'], 42 | 'level': 'INFO', 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ocdaction/static/img/icon-hamburger-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | icon-hamburger-dark 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0009_remove_tasks_into_separate_app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-01-15 12:20 3 | from __future__ import unicode_literals 4 | 5 | import datetime 6 | from django.db import migrations, models 7 | from django.utils.timezone import utc 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('profiles', '0008_auto_20170102_1847'), 14 | ] 15 | 16 | operations = [ 17 | migrations.RemoveField( 18 | model_name='challenge', 19 | name='user', 20 | ), 21 | migrations.AlterField( 22 | model_name='ocdactionuser', 23 | name='date_birth', 24 | field=models.DateField(default=datetime.datetime(2017, 1, 15, 12, 20, 56, 777790, tzinfo=utc), verbose_name='date of birth'), 25 | preserve_default=False, 26 | ), 27 | migrations.AlterField( 28 | model_name='ocdactionuser', 29 | name='have_ocd', 30 | field=models.BooleanField(default=False), 31 | ), 32 | migrations.AlterField( 33 | model_name='ocdactionuser', 34 | name='username', 35 | field=models.CharField(max_length=24), 36 | ), 37 | migrations.DeleteModel( 38 | name='Challenge', 39 | ), 40 | ] 41 | -------------------------------------------------------------------------------- /frontend/src/sass/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'partials/colours'; 2 | @import 'inspinia/inspinia'; 3 | @import 'partials/normalize'; 4 | @import 'partials/typography'; 5 | @import 'partials/helpers'; 6 | 7 | @import 'components/forms'; 8 | @import 'components/navigation'; 9 | @import 'components/footer'; 10 | @import 'components/partners'; 11 | @import 'components/hero'; 12 | @import 'components/contact-us'; 13 | @import 'components/menu'; 14 | @import 'components/custom-panel'; 15 | @import 'components/button'; 16 | @import 'components/ibox-custom'; 17 | @import 'components/video-container'; 18 | @import 'components/challenge-summary'; 19 | @import 'components/challenge-chart'; 20 | 21 | 22 | body { 23 | background: $white-colour; 24 | min-height: 100vh; 25 | } 26 | 27 | section { 28 | padding: 1.6em 0 4em; 29 | clear: both; 30 | 31 | @media (min-width: 990px) { 32 | padding: 4em 0; 33 | } 34 | } 35 | 36 | .section--custom { 37 | padding-bottom: 0; 38 | } 39 | 40 | .background-light { 41 | background-color: $white-colour; 42 | } 43 | .background-dark { 44 | background-color: $grey; 45 | } 46 | .container { 47 | max-width: 70em; 48 | margin: 0 auto; 49 | } 50 | 51 | .content__wrapper { 52 | flex: 1; 53 | 54 | section:first-of-type { 55 | padding-top: 10em; 56 | } 57 | } 58 | 59 | .errorlist { 60 | display: block !important; 61 | } 62 | 63 | img { 64 | max-width: 100%; 65 | display: block; 66 | } -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0012_anxiety-score-card-model-add-time-periods.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-22 16:05 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 | ('challenges', '0011_remove_anxietyscore_user'), 13 | ] 14 | 15 | operations = [ 16 | migrations.RemoveField( 17 | model_name='anxietyscorecard', 18 | name='score_after_20_min', 19 | ), 20 | migrations.AddField( 21 | model_name='anxietyscorecard', 22 | name='anxiety_at_0_min', 23 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_0', to='challenges.AnxietyScore'), 24 | ), 25 | migrations.AddField( 26 | model_name='anxietyscorecard', 27 | name='anxiety_at_30_min', 28 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_30', to='challenges.AnxietyScore'), 29 | ), 30 | migrations.AddField( 31 | model_name='anxietyscorecard', 32 | name='anxiety_at_60_min', 33 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_60', to='challenges.AnxietyScore'), 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0006_created_initial_anxiety_score_card_model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-16 22:14 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 | ('challenges', '0005_created_initial_anxiety_score_model_rename_task_name'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='AnxietyScoreCard', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('anxiety_at_10_min', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='score_10', to='challenges.AnxietyScore')), 21 | ('anxiety_at_15_min', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='score_15', to='challenges.AnxietyScore')), 22 | ('score_after_20_min', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='score_20', to='challenges.AnxietyScore')), 23 | ('anxiety_at_5_min', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='score_5', to='challenges.AnxietyScore')), 24 | ('challenge', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='challenges.Challenge')), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/profiles/login.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load static widget_tweaks %} 3 | 4 | {% block title %}Login{% endblock %} 5 | 6 | {% block main %} 7 | 8 |
9 |
10 |

Log in

11 | {% csrf_token %} 12 | {% if form.non_field_errors %} 13 |
14 | {% for error in form.non_field_errors %} 15 |

{{ error }}

16 | {% endfor %} 17 |
18 | {% endif %} 19 | {% for field in form %} 20 |
21 | {% if field.errors %} 22 | {% for error in field.errors %} 23 |

{{ error }}

24 | {% endfor %} 25 | {% endif %} 26 | {% render_field field placeholder=field.label class+="form-control" %} 27 |
28 | {% endfor %} 29 |
30 | 31 | Forgot password? 32 |
33 |
34 |
35 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_variables.scss: -------------------------------------------------------------------------------- 1 | // Basic Colors 2 | $navy: #1ab394; // Primary color 3 | $dark-gray: #c2c2c2; // Default color 4 | $blue: #1ab394; // Success color 5 | $lazur: #23c6c8; // Info color 6 | $yellow: #f8ac59; // Warning color 7 | $red: #ED5565; // Danger color 8 | 9 | // Various colors 10 | $text-color: #676a6c; // Body text 11 | $gray: #f3f3f4; // Background wrapper color 12 | $light-gray: #D1DADE; // Default label, badge 13 | $label-badge-color: #5E5E5E; 14 | $light-blue: #f3f6fb; 15 | 16 | // Spiner color and margin 17 | $spin-color: $navy; 18 | $spin-margin: 0 auto; 19 | 20 | 21 | // IBOX colors ( default panel colors) 22 | $border-color: #e7eaec; // IBox border 23 | $ibox-title-bg: #fff; // IBox Background header 24 | $ibox-content-bg: #fff; // IBox Background content 25 | 26 | //Sidebar width 27 | $sidebar-width: 220px; 28 | 29 | // Boxed layout width 30 | $boxed-width: 1200px; 31 | $boxed-background: url('http://res.cloudinary.com/ocd-action-app/image/upload//v1489950737/Screen_Shot_2017-03-19_at_19.11.15_qolzax.png'); 32 | 33 | //Border radius for buttons 34 | $btn-border-radius: 3px; 35 | 36 | //Navigation 37 | $nav-bg: #2F4050; 38 | $nav-profile-pattern: url("http://res.cloudinary.com/ocd-action-app/image/upload//v1489950737/Screen_Shot_2017-03-19_at_19.11.15_qolzax.png"); 39 | $nav-text-color: #a7b1c2; 40 | 41 | 42 | $ibox-title-bg: $pastel-green; 43 | $ibox-content-bg: $pastel-green; 44 | $border-color: #ced2d2; 45 | -------------------------------------------------------------------------------- /ocdaction/profiles/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from profiles.models import OCDActionUser 4 | from registration.forms import RegistrationForm 5 | import datetime 6 | 7 | 8 | class OCDActionUserRegistrationForm(RegistrationForm): 9 | """custom registration form 10 | """ 11 | yes_no_choices = ( 12 | (1, 'Yes'), 13 | (0, 'No'), 14 | ) 15 | 16 | have_ocd = forms.ChoiceField( 17 | choices=yes_no_choices, 18 | label='Do you have an OCD diagnosis?' 19 | ) 20 | 21 | nickname = forms.CharField(max_length=24, widget=forms.TextInput(attrs={'placeholder': 'Your preferred name'})) 22 | 23 | now = datetime.datetime.now() 24 | current_year = now.year 25 | users_average_age = 30 26 | date_birth = forms.DateField(widget=forms.SelectDateWidget( 27 | years=range(current_year - users_average_age, current_year)), 28 | label='Date of birth' 29 | ) 30 | terms = forms.BooleanField(widget=forms.CheckboxInput( 31 | attrs={'class': 'form-checkbox'}), 32 | error_messages={'required': 'You must accept the terms and conditions'}, 33 | label="I agree to the terms and conditions" 34 | ) 35 | 36 | def __init__(self, *args, **kwargs): 37 | super(OCDActionUserRegistrationForm, self).__init__(*args, **kwargs) 38 | self.fields['password1'].help_text = 'Password must be at least 10 characters.' 39 | 40 | class Meta: 41 | model = OCDActionUser 42 | fields = ('email', 'password1', 'password2', 'nickname', 'date_birth') 43 | 44 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_button.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | margin: 0; 3 | text-decoration: none; 4 | display: inline; 5 | text-decoration: uppercase; 6 | padding: 8px 32px; 7 | border: 0; 8 | color: $white-colour; 9 | font-weight: 600; 10 | font-weight: 16px; 11 | line-height: 24px; 12 | margin-bottom: 15px; 13 | 14 | .glyphicon { 15 | font-size: 16px; 16 | } 17 | 18 | &--primary { 19 | box-shadow: 1px 1px 1px 0px #339279; 20 | background-color: $ocd-colour; 21 | text-transform: uppercase; 22 | padding: 8px 51px; 23 | font-size: 17px; 24 | 25 | &:hover { 26 | background-color: $pastel-green;; 27 | color: $ocd-colour; 28 | } 29 | } 30 | 31 | &--primary-white { 32 | background-color: $white-colour; 33 | color: $black-colour; 34 | font-size: 17px; 35 | font-weight: 800; 36 | text-transform: uppercase; 37 | padding: 8px 0; 38 | width: 7em; 39 | border: 2px solid $ocd-colour; 40 | 41 | &:hover { 42 | background-color: $ocd-colour;; 43 | color: $white-colour; 44 | } 45 | } 46 | 47 | &--secondary { 48 | background-color: transparent; 49 | } 50 | } 51 | 52 | .button--edit { 53 | text-transform: uppercase; 54 | font-size: 18px; 55 | font-weight: 600; 56 | padding: 9px 54px; 57 | } 58 | 59 | .button--archive { 60 | text-transform: uppercase; 61 | font-size: 18px; 62 | font-weight: 600; 63 | padding: 9px 36px; 64 | } 65 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_badges_labels.scss: -------------------------------------------------------------------------------- 1 | 2 | .label { 3 | background-color: $light-gray; 4 | color: $label-badge-color; 5 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 6 | font-size: 10px; 7 | font-weight: 600; 8 | padding: 3px 8px; 9 | text-shadow: none; 10 | } 11 | 12 | .badge { 13 | background-color: $light-gray; 14 | color: $label-badge-color; 15 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 16 | font-size: 11px; 17 | font-weight: 600; 18 | padding-bottom: 4px; 19 | padding-left: 6px; 20 | padding-right: 6px; 21 | text-shadow: none; 22 | } 23 | 24 | .label-primary, .badge-primary { 25 | background-color: $navy; 26 | color: #FFFFFF; 27 | } 28 | 29 | .label-success, .badge-success { 30 | background-color: $blue; 31 | color: #FFFFFF; 32 | } 33 | 34 | .label-warning, .badge-warning { 35 | background-color: $yellow; 36 | color: #FFFFFF; 37 | } 38 | 39 | .label-warning-light, .badge-warning-light { 40 | background-color: $yellow; 41 | color: #ffffff; 42 | } 43 | 44 | .label-danger, .badge-danger { 45 | background-color: $red; 46 | color: #FFFFFF; 47 | } 48 | 49 | .label-info, .badge-info { 50 | background-color: $lazur; 51 | color: #FFFFFF; 52 | } 53 | 54 | .label-inverse, .badge-inverse { 55 | background-color: #262626; 56 | color: #FFFFFF; 57 | } 58 | 59 | .label-white, .badge-white { 60 | background-color: #FFFFFF; 61 | color: #5E5E5E; 62 | } 63 | 64 | .label-white, .badge-disable { 65 | background-color: #2A2E36; 66 | color: #8B91A0; 67 | } 68 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0009_anxiety-score-card-model-allow-null-scores.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-17 20:18 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 | ('challenges', '0008_anxiety-score-card-model-delete-user'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='anxietyscorecard', 18 | name='anxiety_at_10_min', 19 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_10', to='challenges.AnxietyScore'), 20 | ), 21 | migrations.AlterField( 22 | model_name='anxietyscorecard', 23 | name='anxiety_at_15_min', 24 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_15', to='challenges.AnxietyScore'), 25 | ), 26 | migrations.AlterField( 27 | model_name='anxietyscorecard', 28 | name='score_after_20_min', 29 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_20', to='challenges.AnxietyScore'), 30 | ), 31 | migrations.AlterField( 32 | model_name='anxietyscorecard', 33 | name='anxiety_at_5_min', 34 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_5', to='challenges.AnxietyScore'), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0010_anxiety-score-card-model-allow-blank-scores.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-02-17 20:19 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 | ('challenges', '0009_anxiety-score-card-model-allow-null-scores'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='anxietyscorecard', 18 | name='anxiety_at_10_min', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_10', to='challenges.AnxietyScore'), 20 | ), 21 | migrations.AlterField( 22 | model_name='anxietyscorecard', 23 | name='anxiety_at_15_min', 24 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_15', to='challenges.AnxietyScore'), 25 | ), 26 | migrations.AlterField( 27 | model_name='anxietyscorecard', 28 | name='score_after_20_min', 29 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_20', to='challenges.AnxietyScore'), 30 | ), 31 | migrations.AlterField( 32 | model_name='anxietyscorecard', 33 | name='anxiety_at_5_min', 34 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='score_5', to='challenges.AnxietyScore'), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /frontend/src/sass/partials/_helpers.scss: -------------------------------------------------------------------------------- 1 | .flex-wrap { 2 | display: flex; 3 | } 4 | 5 | .margin-top-0 { 6 | margin-top: 0 !important; 7 | } 8 | 9 | .margin-bottom-0 { 10 | margin-bottom: 0 !important; 11 | } 12 | 13 | .margin-top-15 { 14 | margin-top: 15px !important; 15 | } 16 | 17 | .margin-bottom-15 { 18 | margin-bottom: 15px !important; 19 | } 20 | 21 | .margin-top-30 { 22 | margin-top: 30px !important; 23 | } 24 | 25 | .margin-bottom-30 { 26 | margin-bottom: 30px !important; 27 | } 28 | 29 | .margin-right-5 { 30 | margin-right: 5px !important; 31 | } 32 | 33 | .bold { 34 | font-weight: bold !important; 35 | } 36 | 37 | .text-align-center { 38 | text-align: center !important; 39 | } 40 | 41 | .text-align-left { 42 | text-align: left !important; 43 | } 44 | 45 | .text-align-right { 46 | text-align: right !important; 47 | } 48 | 49 | .background--primary { 50 | background-color: $pastel-green; 51 | } 52 | 53 | .background--archive { 54 | background-color: $archive-background-colour; 55 | } 56 | 57 | .display-inline { 58 | display: inline; 59 | } 60 | 61 | .unstyled-list { 62 | margin: 0 !important; 63 | padding: 0 !important; 64 | 65 | li { 66 | list-style: none !important; 67 | } 68 | } 69 | .visuallyhidden { 70 | border: 0; 71 | clip: rect(0 0 0 0); 72 | height: 1px; 73 | margin: -1px; 74 | overflow: hidden; 75 | padding: 0; 76 | position: absolute; 77 | width: 1px; 78 | 79 | &.focusable:active, 80 | &.focusable:focus { 81 | clip: auto; 82 | height: auto; 83 | margin: 0; 84 | overflow: visible; 85 | position: static; 86 | width: auto; 87 | } 88 | } -------------------------------------------------------------------------------- /ocdaction/ocdaction/settings/production.py: -------------------------------------------------------------------------------- 1 | from ocdaction.settings import * 2 | import dj_database_url 3 | 4 | DEBUG = False 5 | 6 | db_from_env = dj_database_url.config(conn_max_age=500) 7 | DATABASES['default'] = dj_database_url.config() 8 | 9 | # Honor the 'X-Forwarded-Proto' header for request.is_secure() 10 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 11 | 12 | # Security 13 | SECURE_SSL_REDIRECT = True 14 | SESSION_COOKIE_SECURE = True 15 | CSRF_COOKIE_SECURE = True 16 | 17 | ALLOWED_HOSTS = ['staging-ocdaction.herokuapp.com', '.ocdyouthapp.org'] 18 | 19 | SECRET_KEY = os.environ["SECRET_KEY"] 20 | 21 | LOGGING = { 22 | 'version': 1, 23 | 'disable_existing_loggers': False, 24 | 'formatters': { 25 | 'verbose': { 26 | 'format': ('%(asctime)s [%(process)d] [%(levelname)s] ' + 27 | 'pathname=%(pathname)s lineno=%(lineno)s ' + 28 | 'funcname=%(funcName)s %(message)s'), 29 | 'datefmt': '%Y-%m-%d %H:%M:%S' 30 | }, 31 | 'simple': { 32 | 'format': '%(levelname)s %(message)s' 33 | } 34 | }, 35 | 'handlers': { 36 | 'null': { 37 | 'level': 'DEBUG', 38 | 'class': 'logging.NullHandler', 39 | }, 40 | 'console': { 41 | 'level': 'DEBUG', 42 | 'class': 'logging.StreamHandler', 43 | 'formatter': 'verbose' 44 | } 45 | }, 46 | 'loggers': { 47 | 'django.request': { 48 | 'handlers': ['console'], 49 | 'level': 'ERROR', 50 | }, 51 | 'fashrevwall': { 52 | 'handlers': ['console'], 53 | 'level': 'INFO', 54 | } 55 | } 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /ocdaction/profiles/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import TemplateView 2 | from django.shortcuts import render, redirect, get_object_or_404 3 | 4 | from registration.backends.default import views as registration_views 5 | from profiles.forms import OCDActionUserRegistrationForm 6 | from django.contrib.auth.decorators import login_required 7 | from django.contrib.auth import logout 8 | 9 | from profiles.models import OCDActionUser 10 | from challenges.views import delete_challenges 11 | 12 | 13 | class RegistrationComplete(TemplateView): 14 | """ 15 | The Registration Complete view. 16 | """ 17 | 18 | template_name = "registration/registration_complete.html" 19 | 20 | 21 | class ActivationComplete(TemplateView): 22 | """ 23 | The Registration Complete view. 24 | """ 25 | 26 | template_name = "registration/activation_complete.html" 27 | 28 | 29 | class RegistrationView(registration_views.RegistrationView): 30 | """ 31 | The Registration view. 32 | """ 33 | 34 | template_name = 'registration/register.html' 35 | form_class = OCDActionUserRegistrationForm 36 | 37 | @login_required 38 | def my_account(request): 39 | return render(request, 'profiles/my_account.html') 40 | 41 | @login_required 42 | def my_account_delete(request): 43 | return render(request, 'profiles/my_account_delete.html') 44 | 45 | @login_required 46 | def delete_user(request): 47 | ''' 48 | Delete all a user's challenges and their account 49 | ''' 50 | 51 | delete_challenges(request.user) 52 | 53 | user = OCDActionUser.objects.get(id=request.user.id) 54 | logout(request) 55 | user.delete() 56 | 57 | return render(request, 'profiles/my_account_confirm.html', {'deleted_user': True}) 58 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/challenge/challenge_edit.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load static widget_tweaks %} 3 | 4 | {% block title %}Edit challenge{% endblock %} 5 | 6 | {% load static %} 7 | 8 | {% block main %} 9 |
10 |
11 |
12 | 13 | 14 | Back 15 | 16 | 17 |
18 |

{{ challenge.challenge_name }}

19 |
20 |
21 | {% csrf_token %} 22 |
23 | {% for field in challenge_form %} 24 | {% if field.errors %} 25 | {% for error in field.errors %} 26 |
{{ error }}
27 | {% endfor %} 28 | {% endif %} 29 |

{{ field.label }}
30 | {% render_field field class+="challenge-form__textarea" %}

31 | {% endfor %} 32 |
33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 | {% endblock %} -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/registration/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Password Reset Confirm{% endblock %} 4 | 5 | {% load static widget_tweaks %} 6 | 7 | {% block main %} 8 |
9 |
10 |

Set your new password

11 |
12 | {% csrf_token %} 13 | {% if form.non_field_errors %} 14 |
15 | {% for error in form.non_field_errors %} 16 |

{{ error }}

17 | {% endfor %} 18 |
19 | {% endif %} 20 | {% for field in form %} 21 |
22 | {% if field.errors %} 23 | {% for error in field.errors %} 24 |

{{ error }}

25 | {% endfor %} 26 | {% endif %} 27 | {% render_field field placeholder=field.label class+="form-control" %} 28 |
29 | {% endfor %} 30 |

Please choose a strong password or passphrase. Your password must be at least 10 characters long and consist of letters, numbers and other special characters

31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /frontend/src/sass/partials/_normalize.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.1.3 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}a{background:0 0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0} -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/registration/password_reset.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% load static widget_tweaks %} 3 | 4 | {% block title %}Password Reset{% endblock %} 5 | 6 | {% block main %} 7 | 8 |
9 |
10 |

Password Reset

11 |

Please enter your email and we will send you password reset instructions.

12 |
13 | 14 |
15 | {% csrf_token %} 16 | 17 | {% if form.non_field_errors %} 18 |
19 | {% for error in form.non_field_errors %} 20 |

{{ error }}

21 | {% endfor %} 22 |
23 | {% endif %} 24 | {% for field in form %} 25 |
26 | {% if field.errors %} 27 | {% for error in field.errors %} 28 |

{{ error }}

29 | {% endfor %} 30 | {% endif %} 31 | {% render_field field placeholder=field.label class+="form-control" %} 32 |
33 | {% endfor %} 34 |
35 |
36 | 37 |
38 |
39 |
40 |
41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_metismenu.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * metismenu - v2.0.2 3 | * A jQuery menu plugin 4 | * https://github.com/onokumus/metisMenu 5 | * 6 | * Made by Osman Nuri Okumus 7 | * Under MIT License 8 | */ 9 | 10 | .metismenu .plus-minus, .metismenu .plus-times { 11 | float: right 12 | } 13 | 14 | .metismenu .arrow { 15 | float: right; 16 | line-height: 1.42857 17 | } 18 | 19 | .metismenu .glyphicon.arrow:before { 20 | content: "\e079" 21 | } 22 | 23 | .metismenu .active > a > .glyphicon.arrow:before { 24 | content: "\e114" 25 | } 26 | 27 | .metismenu .fa.arrow:before { 28 | content: "\f104" 29 | } 30 | 31 | .metismenu .active > a > .fa.arrow:before { 32 | content: "\f107" 33 | } 34 | 35 | .metismenu .ion.arrow:before { 36 | content: "\f3d2" 37 | } 38 | 39 | .metismenu .active > a > .ion.arrow:before { 40 | content: "\f3d0" 41 | } 42 | 43 | .metismenu .fa.plus-minus:before, .metismenu .fa.plus-times:before { 44 | content: "\f067" 45 | } 46 | 47 | .metismenu .active > a > .fa.plus-times { 48 | -webkit-transform: rotate(45deg); 49 | -ms-transform: rotate(45deg); 50 | transform: rotate(45deg) 51 | } 52 | 53 | .metismenu .active > a > .fa.plus-minus:before { 54 | content: "\f068" 55 | } 56 | 57 | .metismenu .collapse { 58 | display: none 59 | } 60 | 61 | .metismenu .collapse.in { 62 | display: block 63 | } 64 | 65 | .metismenu .collapsing { 66 | position: relative; 67 | height: 0; 68 | overflow: hidden; 69 | -webkit-transition-timing-function: ease; 70 | transition-timing-function: ease; 71 | -webkit-transition-duration: .35s; 72 | transition-duration: .35s; 73 | -webkit-transition-property: height, visibility; 74 | transition-property: height, visibility 75 | } 76 | 77 | .mini-navbar { 78 | .metismenu .collapse { opacity: 0; } 79 | .metismenu .collapse.in { opacity: 1; } 80 | .metismenu .collapse a { display: none } 81 | .metismenu .collapse.in a { display: block } 82 | } 83 | -------------------------------------------------------------------------------- /frontend/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 3 | grunt.loadNpmTasks('grunt-contrib-sass'); 4 | grunt.loadNpmTasks('grunt-contrib-watch'); 5 | grunt.loadNpmTasks('grunt-contrib-uglify'); 6 | grunt.loadNpmTasks('grunt-contrib-copy'); 7 | 8 | grunt.initConfig({ 9 | pkg: grunt.file.readJSON('package.json'), 10 | 11 | //------- CSS Minify -------// 12 | cssmin: { 13 | combine: { 14 | files: { 15 | '../ocdaction/static/styles/styles.css': ['css/styles.css'] 16 | } 17 | } 18 | }, 19 | 20 | //------- SASS -------// 21 | sass: { 22 | dist: { 23 | files: { 24 | '../ocdaction/static/styles/styles.css': 'src/sass/styles.scss' 25 | } 26 | } 27 | }, 28 | 29 | //------- Watch SASS -> CSS -------// 30 | watch: { 31 | sass: { 32 | files: 'src/sass/**/**.scss', 33 | tasks: ['sass'] 34 | } 35 | }, 36 | 37 | jspaths: { 38 | src: { 39 | js: 'src/**/**.js' 40 | }, 41 | dest: { 42 | jsMin: '../ocdaction/static/scripts/ocdaction.min.js' 43 | } 44 | }, 45 | 46 | //------- JS Minify ------// 47 | uglify: { 48 | options: { 49 | compress: true, 50 | mangle: true, 51 | sourceMap: true 52 | }, 53 | target: { 54 | src: '<%= jspaths.src.js %>', 55 | dest: '<%= jspaths.dest.jsMin %>' 56 | } 57 | }, 58 | 59 | //------- copy remaining static files ------// 60 | copy: { 61 | img: { 62 | files: [{ 63 | expand: true, 64 | nonull: true, 65 | cwd: 'src/img', 66 | src: ['*.{png,jpg,jpeg,svg,gif}'], 67 | dest: '../ocdaction/static/img/', 68 | filter: 'isFile' 69 | }] 70 | } 71 | } 72 | 73 | }); 74 | 75 | grunt.registerTask('default', ['sass', 'uglify', 'copy', 'cssmin']); 76 | 77 | }; 78 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | {% block title %} {% endblock %}| OCD Action 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {% block layout %} {% endblock %} 24 | 25 | 26 | 27 | 28 | {% if not debug and not request.user.is_staff %} 29 | 37 | {% endif %} 38 | 39 | 40 | -------------------------------------------------------------------------------- /ocdaction/challenges/urls.py: -------------------------------------------------------------------------------- 1 | """ocdaction URL Configuration 2 | Challenges specific urls for challenges workflows 3 | """ 4 | from django.conf.urls import url 5 | 6 | from .views import * 7 | 8 | urlpatterns = [ 9 | url( 10 | r'^$', 11 | challenge_list, 12 | name="challenge-list" 13 | ), 14 | url( 15 | r'^archived/$', 16 | challenge_list_archived, 17 | name="challenge-list-archived" 18 | ), 19 | url( 20 | r'^new/$', 21 | challenge_add, 22 | name="challenge-add" 23 | ), 24 | url( 25 | r'^(?P[0-9a-f-]+)/$', 26 | challenge_view, 27 | name="challenge" 28 | ), 29 | url( 30 | r'^(?P[0-9a-f-]+)/edit/$', 31 | challenge_edit, 32 | name="challenge-edit" 33 | ), 34 | url( 35 | r'^(?P[0-9a-f-]+)/archive/$', 36 | challenge_archive, 37 | name="challenge-archive" 38 | ), 39 | url( 40 | r'^(?P[0-9a-f-]+)/exposure/$', 41 | challenge_score_form_new, 42 | name="challenge-score-form-new" 43 | ), 44 | url( 45 | r'^(?P[0-9a-f-]+)/exposure/(?P[0-9a-f-]+)/$', 46 | challenge_score_form, 47 | name="challenge-score-form" 48 | ), 49 | url( 50 | r'^(?P[0-9a-f-]+)/summary/$', 51 | challenge_summary, 52 | name="challenge-summary" 53 | ), 54 | url( 55 | r'^(?P[0-9a-f-]+)/results/$', 56 | challenge_results, 57 | name="challenge-results" 58 | ), 59 | url( 60 | r'^erase-my-record/$', 61 | challenge_erase_my_record, 62 | name="challenge-erase-my-record" 63 | ), 64 | url( 65 | r'^delete-users-challenges/$', 66 | delete_users_challenges, 67 | name="delete-users-challenges" 68 | ), 69 | url( 70 | r'^export-challenges-for-user/$', 71 | export_challenges_for_user, 72 | name="export-challenges-for-user" 73 | ), 74 | ] 75 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/profiles/my_account.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% load static %} 4 | {% block title %}My Account{% endblock %} 5 | 6 | {% block main %} 7 |
8 |

My Account

9 |
10 |
11 |
Export your challenges record
12 |

This allows you to export your challenges record in CSV format. You can view this with your device's CSV editor. 13 |

14 |
15 | Export my record 16 |
17 |
18 |
19 |
Erase your record from this platform
20 |

You can erase all your challenges record on OCD Youth while keeping your account. 21 | You can log in with the same email and password. 22 |

23 |
24 | Erase my record 25 |
26 |
27 |
28 |
Delete your account
29 |

This erases your data and deletes your account. Once clicked you will not be able to log in again. 30 |

31 | 34 |
35 | 36 |
37 |
38 | 39 | 44 | {% endblock %} 45 | -------------------------------------------------------------------------------- /ocdaction/profiles/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | ocdaction URL Configuration 3 | profile/user specific urls for auth workflows 4 | """ 5 | from django.conf.urls import include, url 6 | from django.contrib.auth import views as auth_views 7 | 8 | from .views import * 9 | from .forms import OCDActionUserRegistrationForm 10 | 11 | 12 | urlpatterns = [ 13 | url( 14 | r'^register/$', 15 | RegistrationView.as_view(form_class=OCDActionUserRegistrationForm), 16 | name='register', 17 | ), 18 | url( 19 | r'^register/complete/$', 20 | RegistrationComplete.as_view(), name='registration_complete', 21 | ), 22 | url( 23 | r'^register/activation-complete', 24 | ActivationComplete.as_view(), name='activation_complete', 25 | ), 26 | url( 27 | r'^login/$', 28 | auth_views.LoginView.as_view(template_name='profiles/login.html'), 29 | name='login', 30 | ), 31 | url( 32 | r'^logout/$', 33 | auth_views.LogoutView.as_view(template_name='profiles/logout.html'), 34 | name='logout', 35 | ), 36 | url( 37 | r'^password-reset/$', 38 | auth_views.PasswordResetView.as_view(template_name='registration/password_reset.html'), 39 | name='password_reset', 40 | ), 41 | url( 42 | r'^password-reset/done/$', 43 | auth_views.PasswordResetDoneView.as_view(template_name='registration/password_reset_done.html'), 44 | name='password_reset_done', 45 | ), 46 | url( 47 | r'^password-reset/complete/$', 48 | auth_views.PasswordResetCompleteView.as_view(template_name='registration/password_reset_complete.html'), 49 | name='password_reset_complete', 50 | ), 51 | url(r'^accounts/', include('registration.backends.default.urls')), 52 | 53 | url( 54 | r'^my-account/$', 55 | my_account, 56 | name="my-account" 57 | ), 58 | 59 | url( 60 | r'^my-account-delete/$', 61 | my_account_delete, 62 | name="my-account-delete" 63 | ), 64 | 65 | url( 66 | r'^delete-user/$', 67 | delete_user, 68 | name="delete-user" 69 | ), 70 | ] 71 | -------------------------------------------------------------------------------- /ocdaction/challenges/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from challenges.models import Challenge, AnxietyScoreCard 4 | 5 | 6 | class ChallengeForm(forms.ModelForm): 7 | class Meta: 8 | model = Challenge 9 | fields = ('challenge_name', 'obsession', 'compulsion', 'exposure') 10 | widgets = { 11 | 'challenge_name': forms.TextInput(), 12 | 'obsession': forms.Textarea(attrs={'rows': '3'}), 13 | 'compulsion': forms.Textarea(attrs={'rows': '3'}), 14 | 'exposure': forms.Textarea(attrs={'rows': '3'}) 15 | } 16 | 17 | def __init__(self, *args, **kwargs): 18 | kwargs.setdefault('label_suffix', '') 19 | super(ChallengeForm, self).__init__(*args, **kwargs) 20 | self.fields['challenge_name'].label = 'Your Fear' 21 | self.fields['challenge_name'].help_text = 'This will be the name of your challenge and will appear in your hierarchy list.' 22 | self.fields['obsession'].label = 'Your Obsession' 23 | self.fields['obsession'].help_text = 'What\'s the worry or thought that makes you anxious?' 24 | self.fields['compulsion'].label = 'Your Compulsion' 25 | self.fields['compulsion'].help_text = 'What\'s the thing that you do to try to get rid of anxiety?' 26 | self.fields['exposure'].label = 'Your Exposure' 27 | self.fields['exposure'].help_text = 'What is your homework set by your therapist?' 28 | 29 | class AnxietyScoreCardForm(forms.ModelForm): 30 | 31 | class Meta: 32 | model = AnxietyScoreCard 33 | exclude = ['challenge'] 34 | widgets = { 35 | 'anxiety_at_0_min': forms.RadioSelect(), 36 | 'anxiety_at_5_min': forms.RadioSelect(), 37 | 'anxiety_at_10_min': forms.RadioSelect(), 38 | 'anxiety_at_15_min': forms.RadioSelect(), 39 | 'anxiety_at_30_min': forms.RadioSelect(), 40 | 'anxiety_at_60_min': forms.RadioSelect(), 41 | 'anxiety_at_120_min': forms.RadioSelect() 42 | } 43 | 44 | def __init__(self, *args, **kwargs): 45 | super(AnxietyScoreCardForm, self).__init__(*args, **kwargs) 46 | self.fields['anxiety_at_120_min'].required = False 47 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/challenge/challenge_results.html: -------------------------------------------------------------------------------- 1 | {% extends "challenge/challenge_chart.html" %} 2 | 3 | {% block title %}Challenge Results{% endblock %} 4 | 5 | {% load static %} 6 | {% load challenges_tags %} 7 | 8 | {% block charttitle %} 9 |
10 |

{{ challenge.challenge_name }}

11 |
12 | {% endblock %} 13 | 14 | {% block results %} 15 |
16 |
17 |

Today's anxiety levels:

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
Initial anxiety level:{% render_anxiety_level latest_scores.0 %}
After 5mins:{% render_anxiety_level latest_scores.1 %}
After 10mins:{% render_anxiety_level latest_scores.2 %}
After 15mins:{% render_anxiety_level latest_scores.3 %}
After 30mins:{% render_anxiety_level latest_scores.4 %}
After 60mins:{% render_anxiety_level latest_scores.5 %}
After 120mins:{% render_anxiety_level latest_scores.6 %}
50 |
51 |
52 |
53 |
54 | Back to challenges 55 |
56 |
57 |
58 |
59 | {% endblock %} 60 | -------------------------------------------------------------------------------- /ocdaction/profiles/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2016-08-04 19:24 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('auth', '0007_alter_validators_add_error_messages'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='OCDActionUser', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('password', models.CharField(max_length=128, verbose_name='password')), 23 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 24 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 25 | ('email', models.EmailField(db_index=True, max_length=255, unique=True, verbose_name='email address')), 26 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 27 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 28 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 29 | ('username', models.CharField(blank=True, max_length=24)), 30 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 31 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 32 | ], 33 | options={ 34 | 'abstract': False, 35 | 'verbose_name': 'user', 36 | 'verbose_name_plural': 'users', 37 | }, 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_chat.scss: -------------------------------------------------------------------------------- 1 | #small-chat { 2 | position: fixed; 3 | bottom: 20px; 4 | right: 20px; 5 | z-index: 100; 6 | } 7 | 8 | #small-chat .badge { 9 | position: absolute; 10 | top: -3px; 11 | right: -4px; 12 | } 13 | 14 | .open-small-chat { 15 | height: 38px; 16 | width: 38px; 17 | display: block; 18 | background: #1ab394; 19 | padding: 9px 8px; 20 | text-align: center; 21 | color: #fff; 22 | border-radius: 50%; 23 | } 24 | 25 | .open-small-chat:hover { 26 | color: white; 27 | background: #1ab394; 28 | } 29 | 30 | .small-chat-box { 31 | display: none; 32 | position: fixed; 33 | bottom: 20px; 34 | right: 75px; 35 | background: #fff; 36 | border: 1px solid $border-color; 37 | width: 230px; 38 | height: 320px; 39 | border-radius: 4px; 40 | } 41 | 42 | .small-chat-box.ng-small-chat { 43 | display: block; 44 | } 45 | 46 | .body-small { 47 | .small-chat-box { 48 | bottom: 70px; 49 | right: 20px; 50 | } 51 | } 52 | 53 | .small-chat-box.active { 54 | display: block; 55 | } 56 | 57 | .small-chat-box { 58 | 59 | .heading { 60 | background: $nav-bg; 61 | padding: 8px 15px; 62 | font-weight: bold; 63 | color: #fff; 64 | } 65 | 66 | .chat-date { 67 | opacity: 0.6; 68 | font-size: 10px; 69 | font-weight: normal; 70 | } 71 | 72 | .content { 73 | padding: 15px 15px; 74 | 75 | .author-name { 76 | font-weight: bold; 77 | margin-bottom: 3px; 78 | font-size: 11px; 79 | } 80 | 81 | > div { 82 | padding-bottom: 20px; 83 | } 84 | 85 | .chat-message { 86 | padding: 5px 10px; 87 | border-radius: 6px; 88 | font-size: 11px; 89 | line-height: 14px; 90 | max-width: 80%; 91 | background: #f3f3f4; 92 | margin-bottom: 10px; 93 | } 94 | 95 | .chat-message.active { 96 | background: #1ab394; 97 | color: #fff; 98 | } 99 | 100 | .left { 101 | text-align: left; 102 | clear: both; 103 | 104 | .chat-message { 105 | float: left; 106 | } 107 | } 108 | 109 | .right { 110 | text-align: right; 111 | clear: both; 112 | 113 | .chat-message { 114 | float: right; 115 | } 116 | 117 | } 118 | 119 | } 120 | 121 | .form-chat { 122 | padding: 10px 10px; 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /frontend/src/sass/components/_navigation.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | background-color: $white-colour; 3 | border-bottom: 1px solid $grey; 4 | padding: 15px 0; 5 | position: fixed; 6 | right: 0; 7 | left: 0; 8 | z-index: 1030; 9 | 10 | &__logo { 11 | flex: 0 1 10em; 12 | } 13 | 14 | &__wrapper { 15 | display: flex; 16 | flex-direction: row; 17 | align-items: center; 18 | align-content: center; 19 | flex-wrap: wrap; 20 | } 21 | 22 | &__nav { 23 | @media (min-width: 990px) { 24 | min-height: initial; 25 | flex: auto; 26 | text-align: right; 27 | position: initial; 28 | width: auto; 29 | display: inline; 30 | background-image: none; 31 | } 32 | } 33 | 34 | &__list { 35 | margin: 0; 36 | display: none; 37 | 38 | @media (min-width: 990px) { 39 | display: inline; 40 | } 41 | } 42 | 43 | &__list--mobile { 44 | display: block; 45 | min-height: 100vh; 46 | width: 100%; 47 | position: fixed; 48 | top: 0; 49 | transition: top 0.2s; 50 | bottom: 0; 51 | right: 0; 52 | margin: 0; 53 | padding: 0; 54 | z-index: 1000; 55 | list-style: none; 56 | margin: 0; 57 | background: $white-colour; 58 | padding-top: 6em; 59 | } 60 | 61 | 62 | &__nav-toggle { 63 | height: 32px; 64 | width: 40px; 65 | display: block; 66 | background: transparent; 67 | border: 0; 68 | margin: 1rem; 69 | position: fixed; 70 | top: 9px; 71 | right: 15px; 72 | z-index: 2000; 73 | background: url(../img/icon-hamburger-dark.svg) no-repeat center center; 74 | background-size: cover; 75 | color: transparent; 76 | 77 | @media (min-width: 990px) { 78 | display: none; 79 | } 80 | } 81 | 82 | 83 | &__item { 84 | list-style: none; 85 | display: block; 86 | text-align: center; 87 | margin: 0; 88 | 89 | &:hover { 90 | background-color: $ocd-dark-colour; 91 | } 92 | 93 | @media (min-width: 990px) { 94 | display: inline-block; 95 | padding: 0 .5em; 96 | text-align: left; 97 | background-image: none; 98 | 99 | &:hover { 100 | background-color: transparent; 101 | } 102 | } 103 | } 104 | 105 | &__link { 106 | text-transform: uppercase; 107 | padding: 25px; 108 | display: block; 109 | box-sizing: border-box; 110 | text-decoration: none; 111 | 112 | &:hover { 113 | text-decoration: underline; 114 | } 115 | 116 | @media (min-width: 990px) { 117 | padding: 0; 118 | } 119 | } 120 | } 121 | 122 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0017_auto_20170812_1624.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-08-12 16:24 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0016_updated-names-of-the-score-choices'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='anxietyscorecard', 17 | name='anxiety_at_0_min', 18 | field=models.CharField(choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 19 | ), 20 | migrations.AlterField( 21 | model_name='anxietyscorecard', 22 | name='anxiety_at_10_min', 23 | field=models.CharField(choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 24 | ), 25 | migrations.AlterField( 26 | model_name='anxietyscorecard', 27 | name='anxiety_at_15_min', 28 | field=models.CharField(choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 29 | ), 30 | migrations.AlterField( 31 | model_name='anxietyscorecard', 32 | name='anxiety_at_30_min', 33 | field=models.CharField(choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 34 | ), 35 | migrations.AlterField( 36 | model_name='anxietyscorecard', 37 | name='anxiety_at_5_min', 38 | field=models.CharField(choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 39 | ), 40 | migrations.AlterField( 41 | model_name='anxietyscorecard', 42 | name='anxiety_at_60_min', 43 | field=models.CharField(choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 44 | ), 45 | ] 46 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0016_updated-names-of-the-score-choices.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-07-09 12:04 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0015_removed-anxiety-score-model'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='anxietyscorecard', 17 | name='anxiety_at_0_min', 18 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 19 | ), 20 | migrations.AlterField( 21 | model_name='anxietyscorecard', 22 | name='anxiety_at_10_min', 23 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 24 | ), 25 | migrations.AlterField( 26 | model_name='anxietyscorecard', 27 | name='anxiety_at_15_min', 28 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 29 | ), 30 | migrations.AlterField( 31 | model_name='anxietyscorecard', 32 | name='anxiety_at_30_min', 33 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 34 | ), 35 | migrations.AlterField( 36 | model_name='anxietyscorecard', 37 | name='anxiety_at_5_min', 38 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 39 | ), 40 | migrations.AlterField( 41 | model_name='anxietyscorecard', 42 | name='anxiety_at_60_min', 43 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 44 | ), 45 | ] 46 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0018_updated_anxiety_score_card_model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-11-11 18:24 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0017_auto_20170812_1624'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='anxietyscorecard', 17 | name='anxiety_at_120_min', 18 | field=models.CharField(choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], default=1, max_length=2), 19 | preserve_default=False, 20 | ), 21 | migrations.AlterField( 22 | model_name='anxietyscorecard', 23 | name='anxiety_at_10_min', 24 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 25 | ), 26 | migrations.AlterField( 27 | model_name='anxietyscorecard', 28 | name='anxiety_at_15_min', 29 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 30 | ), 31 | migrations.AlterField( 32 | model_name='anxietyscorecard', 33 | name='anxiety_at_30_min', 34 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 35 | ), 36 | migrations.AlterField( 37 | model_name='anxietyscorecard', 38 | name='anxiety_at_5_min', 39 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 40 | ), 41 | migrations.AlterField( 42 | model_name='anxietyscorecard', 43 | name='anxiety_at_60_min', 44 | field=models.CharField(blank=True, choices=[('0', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three'), ('4', 'four'), ('5', 'five'), ('6', 'six'), ('7', 'seven'), ('8', 'eight'), ('9', 'nine'), ('10', 'ten')], max_length=2), 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0021_auto_20180204_1956.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-02-04 19:56 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0020_merge'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='anxietyscorecard', 17 | name='anxiety_at_0_min', 18 | field=models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], default=None, max_length=2), 19 | ), 20 | migrations.AlterField( 21 | model_name='anxietyscorecard', 22 | name='anxiety_at_10_min', 23 | field=models.CharField(blank=True, choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], max_length=2), 24 | ), 25 | migrations.AlterField( 26 | model_name='anxietyscorecard', 27 | name='anxiety_at_120_min', 28 | field=models.CharField(choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], max_length=2), 29 | ), 30 | migrations.AlterField( 31 | model_name='anxietyscorecard', 32 | name='anxiety_at_15_min', 33 | field=models.CharField(blank=True, choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], max_length=2), 34 | ), 35 | migrations.AlterField( 36 | model_name='anxietyscorecard', 37 | name='anxiety_at_30_min', 38 | field=models.CharField(blank=True, choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], max_length=2), 39 | ), 40 | migrations.AlterField( 41 | model_name='anxietyscorecard', 42 | name='anxiety_at_5_min', 43 | field=models.CharField(blank=True, choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], max_length=2), 44 | ), 45 | migrations.AlterField( 46 | model_name='anxietyscorecard', 47 | name='anxiety_at_60_min', 48 | field=models.CharField(blank=True, choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], max_length=2), 49 | ), 50 | ] 51 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_theme-config.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This is style for skin config 4 | * Use only in demo theme 5 | * 6 | */ 7 | 8 | .theme-config { 9 | position: absolute; 10 | top: 90px; 11 | right: 0; 12 | overflow: hidden; 13 | } 14 | 15 | .theme-config-box { 16 | margin-right: -220px; 17 | position: relative; 18 | z-index: 2000; 19 | transition-duration: 0.8s; 20 | } 21 | 22 | .theme-config-box.show { 23 | margin-right: 0; 24 | } 25 | 26 | .spin-icon { 27 | background: $navy; 28 | position: absolute; 29 | padding: 7px 10px 7px 13px; 30 | border-radius: 20px 0 0 20px; 31 | font-size: 16px; 32 | top: 0; 33 | left: 0; 34 | width: 40px; 35 | color: #fff; 36 | cursor: pointer; 37 | } 38 | 39 | .skin-settings { 40 | width: 220px; 41 | margin-left: 40px; 42 | background: $gray; 43 | } 44 | 45 | .skin-settings .title { 46 | background: #efefef; 47 | text-align: center; 48 | text-transform: uppercase; 49 | font-weight: 600; 50 | display: block; 51 | padding: 10px 15px; 52 | font-size: 12px; 53 | } 54 | 55 | .setings-item { 56 | padding: 10px 30px; 57 | } 58 | 59 | .setings-item.skin { 60 | text-align: center; 61 | } 62 | 63 | .setings-item .switch { 64 | float: right; 65 | } 66 | 67 | .skin-name a { 68 | text-transform: uppercase; 69 | } 70 | 71 | .setings-item a { 72 | color: #fff; 73 | } 74 | 75 | .default-skin, .blue-skin, .ultra-skin, .yellow-skin { 76 | text-align: center; 77 | } 78 | 79 | .default-skin { 80 | font-weight: 600; 81 | background: #283A49; 82 | } 83 | 84 | .default-skin:hover { 85 | background: #1e2e3d; 86 | } 87 | 88 | .blue-skin { 89 | font-weight: 600; 90 | background: url("http://res.cloudinary.com/ocd-action-app/image/upload//v1489950737/Screen_Shot_2017-03-19_at_19.11.15_qolzax.png") repeat scroll 0 0; 91 | } 92 | 93 | .blue-skin:hover { 94 | background: #0d8ddb; 95 | } 96 | 97 | .yellow-skin { 98 | font-weight: 600; 99 | background: url("http://res.cloudinary.com/ocd-action-app/image/upload//v1489950737/Screen_Shot_2017-03-19_at_19.11.15_qolzax.png") repeat scroll 0 100%; 100 | } 101 | 102 | .yellow-skin:hover { 103 | background: #ce8735; 104 | } 105 | 106 | .ultra-skin { 107 | padding: 20px 10px; 108 | font-weight: 600; 109 | background: url("http://res.cloudinary.com/ocd-action-app/image/upload//v1489950737/Screen_Shot_2017-03-19_at_19.11.15_qolzax.png") repeat scroll 0 0; 110 | } 111 | 112 | .ultra-skin:hover { 113 | background: url("http://res.cloudinary.com/ocd-action-app/image/upload//v1489950737/Screen_Shot_2017-03-19_at_19.11.15_qolzax.png") repeat scroll 0 0; 114 | } -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_media.scss: -------------------------------------------------------------------------------- 1 | @media (min-width: 768px) { 2 | #page-wrapper { 3 | position: inherit; 4 | margin: 0 0 0 $sidebar-width; 5 | min-height: 1200px; 6 | } 7 | 8 | .navbar-static-side { 9 | z-index: 2001; 10 | position: absolute; 11 | width: $sidebar-width; 12 | } 13 | 14 | .navbar-top-links .dropdown-messages, 15 | .navbar-top-links .dropdown-tasks, 16 | .navbar-top-links .dropdown-alerts { 17 | margin-left: auto; 18 | } 19 | } 20 | 21 | @media (max-width: 768px) { 22 | 23 | #page-wrapper { 24 | position: inherit; 25 | margin: 0 0 0 0; 26 | min-height: 1000px; 27 | } 28 | 29 | .body-small .navbar-static-side { 30 | display: none; 31 | z-index: 2001; 32 | position: absolute; 33 | width: 70px; 34 | } 35 | 36 | .body-small.mini-navbar .navbar-static-side { 37 | display: block; 38 | } 39 | 40 | .lock-word { 41 | display: none; 42 | } 43 | 44 | .navbar-form-custom { 45 | display: none; 46 | } 47 | 48 | .navbar-header { 49 | display: inline; 50 | float: left; 51 | } 52 | 53 | .sidebar-panel { 54 | z-index: 2; 55 | position: relative; 56 | width: auto; 57 | min-height: 100% !important; 58 | } 59 | 60 | .sidebar-content .wrapper { 61 | padding-right: 0; 62 | z-index: 1; 63 | } 64 | 65 | .fixed-sidebar.body-small .navbar-static-side { 66 | display: none; 67 | z-index: 2001; 68 | position: fixed; 69 | width: $sidebar-width; 70 | } 71 | 72 | .fixed-sidebar.body-small.mini-navbar .navbar-static-side { 73 | display: block; 74 | } 75 | 76 | .ibox-tools { 77 | float: none; 78 | text-align: right; 79 | display: block; 80 | } 81 | 82 | .navbar-static-side { display: none; } 83 | 84 | } 85 | 86 | @media (max-width: 350px) { 87 | 88 | .timeline-item .date { 89 | text-align: left; 90 | width: 110px; 91 | position: relative; 92 | padding-top: 30px; 93 | } 94 | 95 | .timeline-item .date i { 96 | position: absolute; 97 | top: 0; 98 | left: 15px; 99 | padding: 5px; 100 | width: 30px; 101 | text-align: center; 102 | border: 1px solid $border-color; 103 | background: #f8f8f8; 104 | } 105 | 106 | .timeline-item .content { 107 | border-left: none; 108 | border-top: 1px solid $border-color; 109 | padding-top: 10px; 110 | min-height: 100px; 111 | } 112 | 113 | .nav.navbar-top-links li.dropdown { 114 | display: none; 115 | } 116 | 117 | .ibox-tools { 118 | float: none; 119 | text-align: left; 120 | display: inline-block; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /ocdaction/challenges/migrations/0014_moved-score-choices-to-anxiety-score-card-model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2017-07-08 17:25 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('challenges', '0013_added-created-and-updated-fields-to-task-model'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='anxietyscorecard', 17 | name='anxiety_at_0_min', 18 | field=models.CharField(blank=True, choices=[('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], default='0', max_length=2), 19 | preserve_default=False, 20 | ), 21 | migrations.AlterField( 22 | model_name='anxietyscorecard', 23 | name='anxiety_at_10_min', 24 | field=models.CharField(blank=True, choices=[('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], default='0', max_length=2), 25 | preserve_default=False, 26 | ), 27 | migrations.AlterField( 28 | model_name='anxietyscorecard', 29 | name='anxiety_at_15_min', 30 | field=models.CharField(blank=True, choices=[('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], default='0', max_length=2), 31 | preserve_default=False, 32 | ), 33 | migrations.AlterField( 34 | model_name='anxietyscorecard', 35 | name='anxiety_at_30_min', 36 | field=models.CharField(blank=True, choices=[('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], default='0', max_length=2), 37 | preserve_default=False, 38 | ), 39 | migrations.AlterField( 40 | model_name='anxietyscorecard', 41 | name='anxiety_at_5_min', 42 | field=models.CharField(blank=True, choices=[('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], default='0', max_length=2), 43 | preserve_default=False, 44 | ), 45 | migrations.AlterField( 46 | model_name='anxietyscorecard', 47 | name='anxiety_at_60_min', 48 | field=models.CharField(blank=True, choices=[('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], default='0', max_length=2), 49 | preserve_default=False, 50 | ), 51 | ] 52 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/challenge/challenge_list_archived.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}My Challenge Archive{% endblock %} 4 | 5 | {% load static %} 6 | {% load challenges_tags %} 7 | 8 | {% block main %} 9 |
10 |
11 |
12 |

My Archive

13 |
14 |
15 | 16 | {% if challenges_archived %} 17 |
18 |
19 |

Archived

20 |
21 |
22 |
23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {% for challenge in challenges_archived %} 34 | 35 | 38 | 43 | 44 | {% endfor %} 45 | 46 |
LevelChallenge
36 | {% get_anxiety_level challenge %} 37 | 39 | 40 | {{ challenge.challenge_name }} 41 | 42 |
47 |
48 |
49 |
50 |
51 |
52 | {% else %} 53 | 54 |
55 |

No archived Challenges

56 |
57 | 58 | {% endif %} 59 | 60 |
61 | {% endblock %} 62 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/challenge/challenge_view.html: -------------------------------------------------------------------------------- 1 | {% extends "challenge/challenge_chart.html" %} 2 | 3 | {% block title %}Challenge: {{ challenge.challenge_name }}{% endblock %} 4 | 5 | {% load static %} 6 | 7 | {% block main %} 8 |
9 |
10 |
11 |
12 | 13 | {% if challenge.is_archived %} 14 | 15 | {% else %} 16 | 17 | {% endif %} 18 | Back 19 | 20 | 21 |
22 |

{{ challenge.challenge_name }}

23 |
24 | 25 |
26 |
Your Obsession:
27 |
{{ challenge.obsession }}
28 |
Your Compulsion:
29 |
{{ challenge.compulsion }}
30 |
Your Exposure:
31 |
{{ challenge.exposure }}
32 |
33 | 34 | {% if not challenge.is_archived %} 35 | 41 | 47 | {% endif %} 48 |
49 |
50 | 51 | {% if data_sets %} 52 |
53 |

54 |
55 |

Your exposures for this challenge

56 |
57 |
58 | {% endif %} 59 | 60 | 69 | {% endblock %} 70 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/challenge/challenge_chart.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% load static %} 4 | {% load challenges_tags %} 5 | 6 | {% block extrahead %} 7 | 8 | {% if data_sets %} 9 | 10 |
11 |
13 | 14 |
15 |
16 | 17 | 90 | 91 | {% endif %} 92 | 93 | {% endblock %} -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_top_navigation.scss: -------------------------------------------------------------------------------- 1 | .top-navigation #page-wrapper { 2 | margin-left: 0; 3 | } 4 | 5 | .top-navigation .navbar-nav .dropdown-menu > .active > a { 6 | background: white; 7 | color: $navy; 8 | font-weight: bold; 9 | } 10 | 11 | .white-bg .navbar-fixed-top, .white-bg .navbar-static-top { 12 | background: #fff; 13 | } 14 | 15 | .top-navigation .navbar { 16 | margin-bottom: 0; 17 | } 18 | 19 | .top-navigation .nav > li > a { 20 | padding: 15px 20px; 21 | color: #676a6c; 22 | } 23 | 24 | .top-navigation .nav > li a:hover, .top-navigation .nav > li a:focus { 25 | background: #fff; 26 | color: $navy; 27 | } 28 | 29 | .top-navigation .nav > li.active { 30 | background: #fff; 31 | border: none; 32 | } 33 | 34 | .top-navigation .nav > li.active > a { 35 | color: $navy; 36 | } 37 | 38 | .top-navigation .navbar-right { 39 | margin-right: 10px; 40 | } 41 | 42 | .top-navigation .navbar-nav .dropdown-menu { 43 | box-shadow: none; 44 | border: 1px solid #e7eaec; 45 | } 46 | 47 | .top-navigation .dropdown-menu > li > a { 48 | margin: 0; 49 | padding: 7px 20px; 50 | } 51 | 52 | .navbar .dropdown-menu { 53 | margin-top: 0; 54 | } 55 | 56 | .top-navigation .navbar-brand { 57 | background: $navy; 58 | color: #fff; 59 | padding: 15px 25px; 60 | } 61 | 62 | .top-navigation .navbar-top-links li:last-child { 63 | margin-right: 0; 64 | } 65 | 66 | .top-navigation.mini-navbar #page-wrapper, 67 | .top-navigation.body-small.fixed-sidebar.mini-navbar #page-wrapper, 68 | .mini-navbar .top-navigation #page-wrapper, 69 | .body-small.fixed-sidebar.mini-navbar .top-navigation #page-wrapper, 70 | .canvas-menu #page-wrapper { 71 | margin: 0; 72 | } 73 | 74 | .top-navigation.fixed-nav #wrapper, .fixed-nav #wrapper.top-navigation { 75 | margin-top: 50px; 76 | } 77 | 78 | .top-navigation .footer.fixed { 79 | margin-left: 0 !important; 80 | } 81 | 82 | .top-navigation .wrapper.wrapper-content { 83 | padding: 40px; 84 | } 85 | 86 | .top-navigation.body-small .wrapper.wrapper-content, .body-small .top-navigation .wrapper.wrapper-content { 87 | padding: 40px 0 40px 0; 88 | } 89 | 90 | .navbar-toggle { 91 | background-color: $navy; 92 | color: #fff; 93 | padding: 6px 12px; 94 | font-size: 14px; 95 | } 96 | 97 | .top-navigation .navbar-nav .open .dropdown-menu > li > a, .top-navigation .navbar-nav .open .dropdown-menu .dropdown-header { 98 | padding: 10px 15px 10px 20px; 99 | } 100 | 101 | @media (max-width: 768px) { 102 | .top-navigation .navbar-header { 103 | display: block; 104 | float: none; 105 | } 106 | } 107 | 108 | .menu-visible-lg, .menu-visible-md { 109 | display: none !important; 110 | } 111 | 112 | @media (min-width: 1200px) { 113 | .menu-visible-lg { 114 | display: block !important; 115 | } 116 | } 117 | 118 | @media (min-width: 992px) { 119 | .menu-visible-md { 120 | display: block !important; 121 | } 122 | } 123 | 124 | @media (max-width: 767px) { 125 | .menu-visible-md { 126 | display: block !important; 127 | } 128 | 129 | .menu-visible-lg { 130 | display: block !important; 131 | } 132 | } -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Homepage{% endblock %} 4 | 5 | {% load static %} 6 | 7 | {% block main %} 8 |
9 |
10 |
11 |
12 |
The OCD Youth app has been designed with young people and clinicians to help you tackle obsessions 13 | and compulsions as part of your therapy.
14 |
15 | Register 16 |
or
17 | Login 18 |
19 |
20 |
21 |

The OCD Youth App

22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |

About Us

37 |

OCD Youth is a peer-led project supporting under 25s affected by Obsessive Compulsive Disorder. 38 | It is facilitated by OCD Action, the national charity for people affected by OCD in the UK, and is run by us - a group 39 | of young volunteers known as the Youth Advisory Panel (YAP).

40 | 41 |

Tackling OCD can be hard work, and sometimes you just need a helping hand to stay on track 42 | and keep your eyes on the prize. Facing your fears is always going to be difficult - no matter how gradual - but 43 | if you've had enough of OCD interfering in your life and have taken steps to seek support and treatment, this web 44 | app is for you.

45 | 46 |

Here at OCD Youth we've teamed up with specialist clinicians at South London and 47 | Maudsley Child and Adolescent OCD team, and an expert development team at Women Hack for Non-Profits to bring 48 | you a free web app that, alongside therapy, will help you manage your OCD. No more messing around with pieces of 49 | paper - do your exposure tasks on the go and keep track of your progress all in one place. You can do it!

50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |

With thanks to:

59 |
60 |
61 | 62 |
63 |
64 | 65 |
66 |
67 |
68 |
69 | 70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/core/contact.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Contact Us{% endblock %} 4 | 5 | {% load static widget_tweaks %} 6 | 7 | {% block main %} 8 |
9 |
10 |
11 |

Useful Contacts

12 |

If you have any questions about symptoms, diagnosis, or treatment of Obsessive Compulsive Disorder, or you would like to learn how to support someone suffering from the disorder, we are here to help. Below are organisations which you can contact.

13 |
14 |
15 |
16 |
17 |

OCD Youth

18 | youth@ocdaction.org.uk 19 | ocdyouth.org 20 |
21 |
22 |

OCD Youth Helpline

23 | youthhelpline@ocdaction.org.uk 24 | ocdyouth.org/helpline 25 |
26 |
27 |
28 |
29 |

OCD Action

30 |

Suite 506-507 Davina House,
31 | 137-149 Goswell Road
32 | London,
33 | EC1V 7ET

34 | info@ocdaction.org.uk 35 |

020 7253 5272
36 | Registered charity No: 1154202

37 |
38 |
39 |
40 |
41 |
42 |
43 |

Contact Us Now

44 |

If you have any questions or concerns, please contact us below. We will get back to you as soon as we can.

45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 |

We'll never share your email with anyone else.

55 | 56 |
57 |
58 | 59 |

Your message will be kept confidential with us.

60 | 61 |
62 |
63 |
64 |
65 |
66 | 67 | 68 |
69 |
70 |
71 |
72 |
73 |
74 | {% endblock %} 75 | 76 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_sidebar.scss: -------------------------------------------------------------------------------- 1 | .sidebar-panel { 2 | width: 220px; 3 | background: darken($gray, 3%); 4 | padding: 10px 20px; 5 | position: absolute; 6 | right: 0; 7 | } 8 | 9 | .sidebar-panel .feed-element img.img-circle { 10 | width: 32px; 11 | height: 32px; 12 | } 13 | 14 | .sidebar-panel .feed-element, .media-body, .sidebar-panel p { 15 | font-size: 12px; 16 | } 17 | 18 | .sidebar-panel .feed-element { 19 | margin-top: 20px; 20 | padding-bottom: 0; 21 | } 22 | 23 | .sidebar-panel .list-group { 24 | margin-bottom: 10px; 25 | } 26 | 27 | .sidebar-panel .list-group .list-group-item { 28 | padding: 5px 0; 29 | font-size: 12px; 30 | border: 0; 31 | } 32 | 33 | .sidebar-content .wrapper, .wrapper.sidebar-content { 34 | padding-right: 230px !important; 35 | } 36 | 37 | .body-small .sidebar-content .wrapper, .body-small .wrapper.sidebar-content { 38 | padding-right: 20px !important; 39 | } 40 | 41 | // Right sidebar 42 | 43 | #right-sidebar { 44 | background-color: #fff; 45 | border-left: 1px solid #e7eaec; 46 | border-top: 1px solid #e7eaec; 47 | overflow: hidden; 48 | position: fixed; 49 | top: 60px; 50 | width: 260px !important; 51 | z-index: 1009; 52 | bottom: 0; 53 | right: -260px; 54 | } 55 | 56 | #right-sidebar.sidebar-open { 57 | right: 0; 58 | } 59 | 60 | #right-sidebar.sidebar-open.sidebar-top { 61 | top: 0; 62 | border-top: none; 63 | } 64 | 65 | .sidebar-container { 66 | 67 | ul.nav-tabs { 68 | border: none; 69 | } 70 | 71 | ul.nav-tabs.navs-4 li { 72 | width: 25%; 73 | } 74 | ul.nav-tabs.navs-3 li { 75 | width: 33.3333%; 76 | } 77 | ul.nav-tabs.navs-2 li { 78 | width: 50%; 79 | } 80 | 81 | ul.nav-tabs li { 82 | border: none; 83 | } 84 | 85 | ul.nav-tabs li a { 86 | border: none; 87 | padding: 12px 10px; 88 | margin: 0; 89 | border-radius: 0; 90 | background: $nav-bg; 91 | color: #fff; 92 | text-align: center; 93 | 94 | border-right: 1px solid lighten($nav-bg, 2%); 95 | } 96 | 97 | ul.nav-tabs li.active a { 98 | border: none; 99 | background: #f9f9f9; 100 | color: $text-color; 101 | font-weight: bold; 102 | 103 | } 104 | 105 | .nav-tabs > li.active > a:hover, 106 | .nav-tabs > li.active > a:focus { 107 | 108 | border: none; 109 | 110 | } 111 | 112 | ul.sidebar-list { 113 | margin: 0; 114 | padding: 0; 115 | } 116 | 117 | ul.sidebar-list li { 118 | border-bottom: 1px solid $border-color; 119 | padding: 15px 20px; 120 | list-style: none; 121 | 122 | font-size: 12px; 123 | } 124 | 125 | ul.sidebar-list li:nth-child(2n+2) { 126 | // background: #f9f9f9; 127 | } 128 | 129 | .sidebar-message:nth-child(2n+2) { 130 | background: #f9f9f9; 131 | } 132 | 133 | ul.sidebar-list li a { 134 | text-decoration: none; 135 | color: inherit; 136 | } 137 | 138 | .sidebar-content { 139 | padding: 15px 20px; 140 | font-size: 12px; 141 | } 142 | 143 | .date-item { 144 | 145 | } 146 | 147 | .sidebar-title { 148 | background: #f9f9f9; 149 | padding: 20px; 150 | border-bottom: 1px solid $border-color; 151 | 152 | h3 { 153 | margin-bottom: 3px; 154 | padding-left: 2px; 155 | } 156 | } 157 | 158 | .tab-content { 159 | 160 | h4 { 161 | margin-bottom: 5px; 162 | } 163 | 164 | } 165 | 166 | .sidebar-message > a > .pull-left { 167 | margin-right: 10px; 168 | } 169 | 170 | .sidebar-message > a { 171 | text-decoration: none; 172 | color: inherit; 173 | } 174 | 175 | .sidebar-message { 176 | padding: 15px 20px; 177 | } 178 | 179 | .sidebar-message:hover { 180 | // background: #f9f9f9; 181 | } 182 | 183 | .sidebar-message .message-avatar { 184 | height: 38px; 185 | width: 38px; 186 | border-radius: 50%; 187 | } 188 | 189 | .setings-item { 190 | padding: 15px 20px; 191 | border-bottom: 1px solid $border-color; 192 | } 193 | 194 | } 195 | 196 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/challenge/challenge_summary.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Challenge Summary{% endblock %} 4 | 5 | {% load static %} 6 | {% load challenges_tags %} 7 | 8 | {% block main %} 9 |
10 |
11 |
12 |
13 | 14 | 15 | Back 16 | 17 | 18 |
19 |

{{ challenge.challenge_name }}

20 |
21 |
22 |
23 |

Congratulations!

24 |

You have tackled this exposure

25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {% for card in anxiety_score_cards %} 38 | 39 | {% endfor %} 40 | 41 | 42 | 43 | {% for card in anxiety_score_cards %} 44 | 45 | {% endfor %} 46 | 47 | 48 | 49 | {% for card in anxiety_score_cards %} 50 | 51 | {% endfor %} 52 | 53 | 54 | 55 | {% for card in anxiety_score_cards %} 56 | 57 | {% endfor %} 58 | 59 | 60 | 61 | {% for card in anxiety_score_cards %} 62 | 63 | {% endfor %} 64 | 65 | 66 | 67 | {% for card in anxiety_score_cards %} 68 | 69 | {% endfor %} 70 | 71 | 72 | 73 | {% for card in anxiety_score_cards %} 74 | 75 | {% endfor %} 76 | 77 | 78 |
Most
recent
Initial anxiety level:{% render_anxiety_level card.anxiety_at_0_min %}
After 5mins:{% render_anxiety_level card.anxiety_at_5_min %}
After 10mins:{% render_anxiety_level card.anxiety_at_10_min %}
After 15mins:{% render_anxiety_level card.anxiety_at_15_min %}
After 30mins:{% render_anxiety_level card.anxiety_at_30_min %}
After 60mins:{% render_anxiety_level card.anxiety_at_60_min %}
After 120mins:{% render_anxiety_level card.anxiety_at_120_min %}
79 |
80 |
81 |
82 |
83 | See how I did 84 |
85 |
86 |
87 |
88 | {% endblock %} 89 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # This configuration is based on that automatically generated from a CircleCI 1.0 config. 2 | # Some of the auto generated comments have been kept in the file for future reference/changes to build config 3 | version: 2 4 | jobs: 5 | build: 6 | working_directory: ~/womenhackfornonprofits/ocdaction 7 | parallelism: 1 8 | shell: /bin/bash --login 9 | environment: 10 | CIRCLE_ARTIFACTS: /tmp/circleci-artifacts 11 | CIRCLE_TEST_REPORTS: /tmp/circleci-test-results 12 | DATABASE_URL: 'sqlite://:memory:' 13 | DJANGO_SETTINGS_MODULE: ocdaction.settings.test 14 | DEBUG: 0 15 | PYTHONPATH: ${HOME}/ocdaction/ocdaction 16 | # In CircleCI 1.0 we used a pre-configured image with a large number of languages and other packages. 17 | # In CircleCI 2.0 you can now specify your own image, or use one of our pre-configured images. 18 | # The following configuration line tells CircleCI to use the specified docker image as the runtime environment for your job. 19 | # We have selected a pre-built image that mirrors the build environment we use on 20 | # the 1.0 platform, but we recommend you choose an image more tailored to the needs 21 | # of each job. For more information on choosing an image (or alternatively using a 22 | # VM instead of a container) see https://circleci.com/docs/2.0/executor-types/ 23 | # To see the list of pre-built images that CircleCI provides for most common languages see 24 | # https://circleci.com/docs/2.0/circleci-images/ 25 | docker: 26 | - image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37 27 | command: /sbin/init 28 | steps: 29 | # Machine Setup 30 | # If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each 31 | # The following `checkout` command checks out your code to your working directory. In 1.0 we did this implicitly. In 2.0 you can choose where in the course of a job your code should be checked out. 32 | - checkout 33 | # Prepare for artifact and test results collection equivalent to how it was done on 1.0. 34 | # In many cases you can simplify this from what is generated here. 35 | # 'See docs on artifact collection here https://circleci.com/docs/2.0/artifacts/' 36 | - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS 37 | # Dependencies 38 | # This would typically go in either a build or a build-and-test job when using workflows 39 | # Restore the dependency cache 40 | - restore_cache: 41 | keys: 42 | # This branch if available 43 | - v1-dep-{{ .Branch }}- 44 | # Default branch if not 45 | - v1-dep-master- 46 | # Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly 47 | - v1-dep- 48 | - run: # install and activate virtual environment with pip before installing requirements 49 | name: Install requirements 50 | command: | 51 | python -m virtualenv venv 52 | . venv/bin/activate 53 | pip install -r requirements/testing.txt 54 | # Save dependency cache 55 | - save_cache: 56 | key: v1-dep-{{ .Branch }}-{{ epoch }} 57 | paths: 58 | # This is a broad list of cache paths to include many possible development environments 59 | # You can probably delete some of these entries 60 | - vendor/bundle 61 | - ~/virtualenvs 62 | - ~/.m2 63 | - ~/.ivy2 64 | - ~/.bundle 65 | - ~/.go_workspace 66 | - ~/.gradle 67 | - ~/.cache/bower 68 | # Test 69 | # This would typically be a build job when using workflows, possibly combined with build 70 | - run: 71 | name: Run tests 72 | command: | 73 | . venv/bin/activate 74 | cd /home/ubuntu/womenhackfornonprofits/ocdaction/ocdaction 75 | pytest 76 | # Teardown 77 | # If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each 78 | # Save test results 79 | - store_test_results: 80 | path: /tmp/circleci-test-results 81 | # Save artifacts 82 | - store_artifacts: 83 | path: /tmp/circleci-artifacts 84 | - store_artifacts: 85 | path: /tmp/circleci-test-results 86 | -------------------------------------------------------------------------------- /ocdaction/challenges/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.urls import reverse 3 | 4 | from django.db import models, transaction 5 | import uuid 6 | 7 | 8 | class Challenge(models.Model): 9 | """a challenge is a user created challenge that can be completed 10 | by a user to track anxiety 11 | """ 12 | challenge_name = models.CharField(max_length=100) 13 | is_archived = models.BooleanField(default=False) 14 | in_progress = models.BooleanField(default=False) 15 | obsession = models.CharField(max_length=300, blank=True) 16 | compulsion = models.CharField(max_length=300, blank=True) 17 | exposure = models.CharField(max_length=300, blank=True) 18 | user = models.ForeignKey('profiles.OCDActionUser', on_delete=models.CASCADE) 19 | created_at = models.DateTimeField(auto_now_add=True) 20 | updated_at = models.DateTimeField(auto_now=True) 21 | uuid = models.UUIDField(primary_key=False, default=uuid.uuid4, editable=False, unique=True) 22 | 23 | def __str__(self): 24 | return self.challenge_name 25 | 26 | @transaction.atomic 27 | def save(self, *args, **kwargs): 28 | user = self.user 29 | if self.in_progress: 30 | Challenge.objects.filter(user=user, in_progress=True).update(in_progress=False) 31 | super(Challenge, self).save(*args, **kwargs) 32 | 33 | def get_absolute_url(self): 34 | return reverse('challenge-edit', 35 | kwargs={'challenge_uuid': self.uuid}) 36 | 37 | def archive(self): 38 | self.is_archived = True 39 | self.save() 40 | 41 | def get_latest_initial_anxiety_level(self): 42 | try: 43 | anxiety_score_card = AnxietyScoreCard.objects.filter(challenge=self).last() 44 | latest_initial_anxiety_level = anxiety_score_card.anxiety_at_0_min 45 | except: 46 | AnxietyScoreCard.DoesNotExist 47 | latest_initial_anxiety_level = -1 48 | return latest_initial_anxiety_level 49 | 50 | class AnxietyScoreCard(models.Model): 51 | """ 52 | Anxiety score card is a collection of scores for the challenge 53 | """ 54 | SCORE_ONE = '1' 55 | SCORE_TWO = '2' 56 | SCORE_THREE = '3' 57 | SCORE_FOUR = '4' 58 | SCORE_FIVE = '5' 59 | SCORE_SIX = '6' 60 | SCORE_SEVEN = '7' 61 | SCORE_EIGHT = '8' 62 | SCORE_NINE = '9' 63 | SCORE_TEN = '10' 64 | 65 | ANXIETY_SCORE_CHOICES = ( 66 | (SCORE_ONE, '1'), 67 | (SCORE_TWO, '2'), 68 | (SCORE_THREE, '3'), 69 | (SCORE_FOUR, '4'), 70 | (SCORE_FIVE, '5'), 71 | (SCORE_SIX, '6'), 72 | (SCORE_SEVEN, '7'), 73 | (SCORE_EIGHT, '8'), 74 | (SCORE_NINE, '9'), 75 | (SCORE_TEN, '10'), 76 | ) 77 | 78 | anxiety_at_0_min = models.CharField( 79 | max_length=2, 80 | choices=ANXIETY_SCORE_CHOICES, 81 | blank=False, 82 | default=None 83 | ) 84 | anxiety_at_5_min = models.CharField( 85 | max_length=2, 86 | choices=ANXIETY_SCORE_CHOICES, 87 | blank=True 88 | ) 89 | anxiety_at_10_min = models.CharField( 90 | max_length=2, 91 | choices=ANXIETY_SCORE_CHOICES, 92 | blank=True 93 | ) 94 | anxiety_at_15_min = models.CharField( 95 | max_length=2, 96 | choices=ANXIETY_SCORE_CHOICES, 97 | blank=True 98 | ) 99 | anxiety_at_30_min = models.CharField( 100 | max_length=2, 101 | choices=ANXIETY_SCORE_CHOICES, 102 | blank=True 103 | ) 104 | anxiety_at_60_min = models.CharField( 105 | max_length=2, 106 | choices=ANXIETY_SCORE_CHOICES, 107 | blank=True 108 | ) 109 | anxiety_at_120_min = models.CharField( 110 | max_length=2, 111 | choices=ANXIETY_SCORE_CHOICES, 112 | blank=False 113 | ) 114 | challenge = models.ForeignKey('Challenge', on_delete=models.CASCADE) 115 | uuid = models.UUIDField(primary_key=False, default=uuid.uuid4, editable=False, unique=True) 116 | created_at = models.DateTimeField(auto_now_add=True) 117 | updated_at = models.DateTimeField(auto_now=True) 118 | 119 | def user_name(self): 120 | return self.challenge.user 121 | 122 | def get_absolute_url(self): 123 | return reverse( 124 | 'challenge-score-form', 125 | kwargs={'challenge_uuid': self.challenge.uuid}) 126 | 127 | def get_score_url(self): 128 | return reverse( 129 | 'challenge-summary', 130 | kwargs={'challenge_uuid': self.challenge.uuid, 131 | 'score_uuid': self.uuid}) 132 | -------------------------------------------------------------------------------- /frontend/src/sass/components/_forms.scss: -------------------------------------------------------------------------------- 1 | // ================================= 2 | // LAYOUT / FORMS 3 | // ================================= 4 | 5 | 6 | $input-txt-height: rem-calc(52); 7 | 8 | input[type="text"], 9 | input[type="email"], 10 | input[type="password"], 11 | input[type="tel"], 12 | input[type="url"], 13 | select { 14 | width: 100%; 15 | height: $input-txt-height; 16 | margin: .5em 0; 17 | padding: 6px 8px; 18 | font-size: 1em; 19 | font-weight: 100; 20 | border-top: 1px solid #ccc; 21 | border-left: 1px solid #ccc; 22 | border-right: 1px solid #eee; 23 | border-bottom: 1px solid #eee; 24 | } 25 | 26 | input[type="Submit"] { 27 | padding: 6px 8px; 28 | } 29 | 30 | textarea { 31 | width: 100%; 32 | margin: .5em 0; 33 | padding: .5em; 34 | font-size: 1em; 35 | font-weight: 100; 36 | text-align: left; 37 | border-top: 1px solid #ccc; 38 | border-left: 1px solid #ccc; 39 | border-right: 1px solid #eee; 40 | border-bottom: 1px solid #eee; 41 | } 42 | 43 | label { 44 | display: block; 45 | } 46 | 47 | select { 48 | -webkit-appearance: none; 49 | background: url("http://garyjeffress.com/wp-content/themes/lms/framework/plugins/designthemes-core-features/page-builder/images/select-box-arrow.png") no-repeat right center; 50 | background-color: $white-colour; 51 | border-radius: 0; 52 | display: inline; 53 | width: auto; 54 | padding-right: 2rem; 55 | } 56 | 57 | .errorlist { 58 | padding: 0; 59 | margin: 0; 60 | display: block; 61 | li { 62 | color: red; 63 | line-height: 1em; 64 | line-height: .6em; 65 | font-size: .7em; 66 | list-style: none; 67 | } 68 | } 69 | 70 | .errorlist + p > input { 71 | border: 1px red solid; 72 | } 73 | 74 | form { 75 | p { 76 | margin-top: 0; 77 | } 78 | } 79 | 80 | .challenge-form { 81 | margin-top: 40px; 82 | 83 | &__label, 84 | label { 85 | font-size: 18px; 86 | font-weight: 700; 87 | color: $black-colour; 88 | } 89 | 90 | textarea, input[type="text"], { 91 | width: 100%; 92 | margin: .5em 0; 93 | padding: .5em; 94 | font-size: 1em; 95 | font-weight: 100; 96 | text-align: left; 97 | border: 1px solid $black-colour; 98 | } 99 | 100 | textarea.challenge-form__textarea, input.challenge-form__textarea{ 101 | color: $black-colour; 102 | font-weight: 400; 103 | } 104 | 105 | p { 106 | margin-bottom: 23px; 107 | 108 | &:last-of-type { 109 | margin-bottom: 27px; 110 | } 111 | } 112 | 113 | ::-webkit-input-placeholder { 114 | color: $light-grey; 115 | } 116 | 117 | :-moz-placeholder { 118 | color: $light-grey; 119 | } 120 | } 121 | 122 | .anxiety-tracker { 123 | margin-top: 25px; 124 | margin-bottom: 90px; 125 | 126 | form { 127 | padding-top: 8px; 128 | padding-bottom: 30px; 129 | 130 | p { 131 | margin-top: 20px; 132 | } 133 | } 134 | 135 | label { 136 | display: inline-block; 137 | } 138 | 139 | p { 140 | span { 141 | font-weight: 600; 142 | padding-left: 30px; 143 | } 144 | } 145 | 146 | .btn-group { 147 | padding-top: 5px; 148 | } 149 | 150 | .btn-group>.btn:not(:first-child) { 151 | margin-left: -1px; 152 | } 153 | 154 | .btn-group>.btn { 155 | padding: 9px 11px; 156 | 157 | @media (min-width: 990px) { 158 | padding: 10px 12px; 159 | } 160 | } 161 | 162 | .btn-group>.btn:last-child { 163 | padding: 9px 7px; 164 | 165 | @media (min-width: 990px) { 166 | padding: 10px 8px; 167 | } 168 | } 169 | 170 | .btn-group>.btn:not(:first-child):not(:last-child) { 171 | border-radius: 2px; 172 | } 173 | 174 | fieldset { 175 | border: none; 176 | padding: 1.2em 0 .95em; 177 | } 178 | 179 | button[disabled] { 180 | cursor: not-allowed; 181 | background-color: $btn-disabled-color; 182 | color: $white-colour; 183 | box-shadow: none; 184 | } 185 | 186 | input[type=radio]:checked + label { 187 | background-color: $light-grey; 188 | color: $white-colour; 189 | } 190 | 191 | input[type=radio] { 192 | display: none 193 | } 194 | 195 | .anxiety-tracker--complete { 196 | padding-top: 25px; 197 | 198 | a { 199 | color: $white-colour; 200 | } 201 | 202 | .button--primary { 203 | padding: 8px 30px; 204 | } 205 | } 206 | } -------------------------------------------------------------------------------- /frontend/src/sass/partials/_typography.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Lato:400,400i,700,900|Open+Sans:600,700,300'); 2 | 3 | $title-font-name: "Lato", sans-serif; 4 | $base-font-name: "Open Sans", sans-serif; 5 | $base-font-size: 16px; 6 | 7 | $title-font-family: $title-font-name; 8 | $base-font-family: $base-font-name; 9 | 10 | $header-margin: 1.25em; 11 | 12 | /*------------------------------------*\ 13 | # Type 14 | \*------------------------------------*/ 15 | 16 | // Make type normal across browsers 17 | *, html, body { 18 | font-size: $base-font-size; 19 | font-family: $base-font-family; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | -webkit-text-size-adjust: 100%; 23 | -ms-text-size-adjust: 100%; 24 | } 25 | 26 | 27 | // Headers 28 | h1, h2, h3, h4, h5, h6 { 29 | color: $black-colour; 30 | font-family: $title-font-family; 31 | text-align: center; 32 | } 33 | 34 | .h1, 35 | h1 { 36 | font-size: 1.7em; 37 | font-weight: 400; 38 | margin-bottom: $header-margin / 2; 39 | 40 | @media (min-width: 990px) { 41 | font-size: 1.95em; 42 | } 43 | } 44 | 45 | .h2, 46 | h2 { 47 | font-size: 1.8em; 48 | margin: $header-margin * 1.3 0 $header-margin 0; 49 | font-weight: 700; 50 | } 51 | 52 | .h3, 53 | h3 { 54 | font-size: 1.5em; 55 | margin: $header-margin * 1.5 0 $header-margin 0; 56 | font-weight: 400; 57 | } 58 | 59 | .h4, 60 | h4 { 61 | font-size: 1.3em; 62 | margin: $header-margin * 1.7 0 $header-margin 0; 63 | font-weight: 400; 64 | } 65 | 66 | .h5, 67 | h5 { 68 | font-size: 1.2em; 69 | margin: $header-margin * 1.8 0 $header-margin 0; 70 | font-weight: 400; 71 | } 72 | 73 | 74 | // Paragraphs 75 | .body-text, 76 | p { 77 | 78 | margin: 30px 0 15px 0; 79 | font-size: 1em; 80 | line-height: 1.3em; 81 | color: $body-text-colour; 82 | font-family: $base-font-family; 83 | text-align: left; 84 | font-weight: 300; 85 | 86 | &:first-of-type { 87 | margin-top: 0; 88 | } 89 | } 90 | 91 | 92 | 93 | // Links 94 | .a, 95 | .link, 96 | a { 97 | text-decoration: none; 98 | font-weight: 600; 99 | color: $link-colour; 100 | 101 | &:hover, &:focus { 102 | color: $link-active-colour; 103 | text-decoration: none; 104 | } 105 | 106 | &--disabled { 107 | color: $link-disabled-colour; 108 | pointer-events: all !important; 109 | cursor: not-allowed; 110 | } 111 | } 112 | 113 | .helptext { 114 | font-style: italic; 115 | font-size: .8rem; 116 | font-color: $lighter-black-colour; 117 | } 118 | 119 | .backlink { 120 | text-decoration: none; 121 | font-size: 20px; 122 | color: $lighter-black-colour; 123 | position: absolute; 124 | top: 20px; 125 | 126 | i { 127 | color: $lighter-black-colour; 128 | } 129 | 130 | @media (min-width: 990px) { 131 | font-size: 15px; 132 | position: absolute; 133 | top: 20px; 134 | } 135 | 136 | &__text { 137 | display: none; 138 | 139 | @media (min-width: 990px) { 140 | display: inline-block; 141 | color: $lighter-black-colour; 142 | text-decoration: none; 143 | font-size: 20px; 144 | } 145 | } 146 | } 147 | 148 | // Homepage introduction text 149 | .intro { 150 | padding-bottom: 30px; 151 | 152 | &--text { 153 | line-height: 1.3em; 154 | padding-bottom: 5px; 155 | margin-top: 0; 156 | 157 | @media (min-width: 740px) { 158 | line-height: 1.7em; 159 | padding-bottom: 18px; 160 | } 161 | } 162 | 163 | &--span { 164 | font-weight: 600; 165 | font-size: 20px; 166 | color: $black-colour; 167 | display: inline-block; 168 | } 169 | } 170 | 171 | // Lists 172 | ul, ol { 173 | margin: 30px 0 0 20px; 174 | 175 | li { 176 | margin-top: 10px; 177 | color: $body-text-colour; 178 | list-style: none; 179 | } 180 | 181 | ul, ol { 182 | margin-top: 0; 183 | } 184 | } 185 | 186 | dl { 187 | margin: 30px 0 0; 188 | 189 | dd, dt { 190 | margin-top: 10px; 191 | font-size: 18px; 192 | color: $body-text-colour; 193 | list-style: none; 194 | } 195 | 196 | dd { 197 | margin-bottom: 25px; 198 | 199 | &:last-child { 200 | margin-bottom: 50px; 201 | } 202 | } 203 | } 204 | 205 | strong, 206 | b, 207 | .bold { 208 | font-weight: 800; 209 | } 210 | 211 | .small { 212 | font-weight: 300; 213 | font-size: .9em; 214 | } 215 | 216 | .large { 217 | font-size: 1.1em; 218 | 219 | @media (min-width: 740px) { 220 | font-size: 1.2em; 221 | } 222 | } 223 | 224 | *:focus { 225 | outline-color: #1ab394; 226 | outline-width: 2px; 227 | } 228 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OCD Action 2 | 3 | # Content 4 | 5 | 1. [Description](#description) 6 | 2. [Setup](#setup) 7 | 3. [Contributing](#contributing) 8 | 9 | 10 | # Description 11 | A collaborative, youth­led project aiming to make use of digital technology to provide mental health support to children & adolescents suffering with anxiety. 12 | 13 | [More Info](https://github.com/womenhackfornonprofits/whfnp-wiki/wiki/Current-Projects#ocd-action) 14 | 15 | 16 | ## Tech 17 | - Python + Django 18 | - JavaScript for some interactions/ lazy loading images etc 19 | - Chart.js 20 | - Sass + HTML 21 | - Github 22 | - Slack for group chats/standups etc 23 | 24 | # Setup 25 | ## Tools 26 | 1. **Terminal**: [iTerm2](https://www.iterm2.com/) (MacOSX), [Terminator](http://gnometerminator.blogspot.co.uk/p/introduction.html) (Linux) or use your preffered one. 27 | 2. **Text Editor**: [Sublime Text](http://www.sublimetext.com/) or you preffered one. 28 | 29 | ## Dev Enviroment Setup 30 | 1. [Install Git](http://git-scm.com/download/mac) 31 | 32 | 1. Install [virtualenv](https://virtualenv.pypa.io/en/stable/): 33 | 34 | ```sudo pip install virtualenv``` 35 | 36 | 2. Install [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/install.html): ```sudo pip install virtualenvwrapper``` 37 | 38 | If you are on OS X El Capitan, you might get the following error: 39 | 40 | ``` 41 | Uninstalling six-1.4.1: 42 | Exception: 43 | Traceback (most recent call last): 44 | ... 45 | OSError: [Errno 1] Operation not permitted: '/var/folders/7x/h9sy5ff95bl45569k5km9g5m0000gn/T/pip-aWCOxi-uninstall/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/six-1.4.1-py2.7.egg-info' 46 | ``` 47 | 48 | If that happens, re-run the command with the flag `--ignore-installed six` 49 | 50 | 3. Source the `virtualenvwrapper`: 51 | ```source /usr/local/bin/virtualenvwrapper.sh``` 52 | 53 | **NOTE**: To help do this automatically on every new shell you open add the line above to your `.bash_profile` or `.bashrc` 54 | 55 | 4. Create a new env for the project: 56 | 57 | ```mkvirtualenv ocdaction``` 58 | 6. Get the code: 59 | 60 | ```git clone git@github.com:womenhackfornonprofits/ocdaction.git``` 61 | 62 | 6. Go inside the `ocdaction` directory: 63 | 64 | ```cd ocdaction``` 65 | 66 | 7. Activate the virtual enviroment: 67 | 68 | ```workon ocdaction``` 69 | 70 | This will now ensure anything you install is within this enviroment. 71 | 72 | 8. You will need to have [Postgres installed](https://www.postgresql.org/download/) and up and running. You can onstall it via: 73 | - Homebrew ```brew install postgresql``` 74 | - OR Download the [Postgress App](http://postgresapp.com/) 75 | 76 | 9. Make sure the Postgres Server is up and running: 77 | - If using the App simply start the server from there 78 | - If using command line: ``brew services start postgresql`` 79 | 80 | 9. Install the requirements: 81 | 82 | ```pip install -r requirements/base.txt``` 83 | 84 | This will get all the dependencies. 85 | 86 | 9. Create a database locally for the project to run: 87 | 88 | ```createdb ocdaction``` 89 | 90 | 10. Go inside frontend folder: 91 | 92 | ```cd frontend``` 93 | 94 | 11. Install all the dependencies: 95 | 96 | ```npm install``` 97 | 98 | This will get all the dependencies 99 | 100 | ## Running the project locally 101 | 1. Go inside the django app directory: 102 | 103 | ```cd ocdaction``` 104 | 2. Run django server: 105 | 106 | ```python manage.py runserver``` 107 | 108 | 3. The project is now running on `http://127.0.0.1:8000/`, go to that address in your browser. 109 | 4. You may see a message that you have unapplied migrations, when you see this simply run the command below which will create any tables and fields in the database: 110 | 111 | ```python manage.py migrate``` 112 | 113 | 5. To run tests, run ```pytest``` from within the app directory. This will run all tests within that directory and subfolders. 114 | 115 | 116 | ## Front End changes 117 | 1. Make css and javascript changes in the ```frontend``` folder 118 | 2. Make any HTML changes in the Django templates located in `ocdaction/templates` 119 | 3. Use `grunt watch` in the frontend folder to build, watch and copy all the required files automatically into the Django static folder. 120 | 121 | ## Deploying to Heroku 122 | 1. Create a Heroku Account 123 | 2. Get added to the app in the Heroku Dashboard 124 | 3. Download [Heroku toolbelt](https://devcenter.heroku.com/articles/heroku-command-line): `wget -qO- https://toolbelt.heroku.com/install.sh | sh` 125 | 3. In the terminal `heroku login` 126 | 4. Within you project directory `heroku git:remote -a staging-ocdaction` 127 | 5. Once you are ready to deploy, from master branch you can run `git push heroku master` make sure you have commited all the changes before running this and the `git status` is clean. 128 | 6. Go to https://staging-ocdaction.herokuapp.com/ to view the live site. 129 | 130 | # Contributing 131 | Please follow a few guidelines in order to contribute to the project set out in the [Contributing file](https://github.com/womenhackfornonprofits/ocdaction/blob/master/CONTRIBUTING.md) 132 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/challenge/challenge_score_form.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Anxiety Tracker{% endblock %} 4 | 5 | {% load static %} 6 | 7 | {% block main %} 8 |
9 |
10 |
11 |
12 | 13 | 14 | Back 15 | 16 | 17 |
18 |

{{ challenge.challenge_name }}

19 |
20 |
21 |
22 |
Enter your anxiety level at each time
23 |
24 | {% csrf_token %} 25 | {% for field in anxiety_score_form %} 26 | {% if anxiety_score_form.anxiety_at_0_min.value %} 27 | {% if field.value %} 28 | {{ field.as_hidden }} 29 |

{{ field.label_tag }}{{ field.value }}

30 | {% else %} 31 |
32 | {{ field.errors }} 33 | {{ field.label_tag }} 34 |
35 | {% for radio in field|slice:"1:" %} 36 | {{ radio.tag }} 37 | 40 | {% endfor %} 41 |
42 |
43 |
44 |
45 | 46 |
47 |
48 | {% endif %} 49 | {% else %} 50 | {% if field.name == 'anxiety_at_0_min' %} 51 |
52 | {{ field.errors }} 53 | {{ field.label_tag }} 54 |
55 | {% for radio in field %} 56 | {{ radio.tag }} 57 | 60 | {% endfor %} 61 |
62 |
63 |
64 |
65 | 66 |
67 |
68 | {% else %} 69 |
70 | {{ field.errors }} 71 | {{ field.label_tag }} 72 |
73 | {% for radio in field|slice:"1:" %} 74 | {{ radio.tag }} 75 | 78 | {% endfor %} 79 |
80 |
81 |
82 |
83 | 84 |
85 |
86 | {% endif %} 87 | {% endif %} 88 | {% endfor %} 89 | 90 | {% if anxiety_score_form.anxiety_at_120_min.value %} 91 | 96 | {% endif %} 97 |
98 |
99 |
100 | {% endblock %} 101 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load static %} 3 | 4 | {% block layout %} 5 |
6 |
7 |
8 | 11 | 47 |
48 |
49 | {% block main %} 50 | {% endblock %} 51 | {% block charttitle %} 52 | {% endblock %} 53 | {% block extrahead %} 54 | {% endblock %} 55 | {% block results %} 56 | {% endblock %} 57 |
58 | 59 | 98 | {% endblock %} 99 | 100 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/settings/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for ocdaction project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.9.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.9/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.9/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 17 | 18 | # Quick-start development settings - unsuitable for production 19 | # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ 20 | 21 | ALLOWED_HOSTS = [] 22 | 23 | 24 | # Application definition 25 | 26 | INSTALLED_APPS = [ 27 | 'registration', 28 | 'storages', 29 | 'django.contrib.admin', 30 | 'django.contrib.auth', 31 | 'django.contrib.contenttypes', 32 | 'django.contrib.sessions', 33 | 'django.contrib.messages', 34 | 'django.contrib.staticfiles', 35 | 'widget_tweaks', 36 | 'custom_user', 37 | 'profiles', 38 | 'core', 39 | 'challenges', 40 | 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | 'django.middleware.security.SecurityMiddleware', 45 | 'whitenoise.middleware.WhiteNoiseMiddleware', 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'django.middleware.common.CommonMiddleware', 48 | 'django.middleware.csrf.CsrfViewMiddleware', 49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 | 'django.contrib.messages.middleware.MessageMiddleware', 51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 | 53 | ] 54 | 55 | ROOT_URLCONF = 'ocdaction.urls' 56 | 57 | LOGIN_URL = '/users/login/' 58 | 59 | LOGIN_REDIRECT_URL = 'challenge-list' 60 | 61 | TEMPLATES = [ 62 | { 63 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 64 | 'DIRS': ( 65 | os.path.join(BASE_DIR, 'ocdaction/templates'), 66 | ), 67 | 'APP_DIRS': True, 68 | 'OPTIONS': { 69 | 'context_processors': [ 70 | 'django.template.context_processors.debug', 71 | 'django.template.context_processors.request', 72 | 'django.contrib.auth.context_processors.auth', 73 | 'django.contrib.messages.context_processors.messages', 74 | 'django.template.context_processors.media' 75 | ], 76 | }, 77 | }, 78 | ] 79 | 80 | 81 | WSGI_APPLICATION = 'ocdaction.wsgi.application' 82 | 83 | # Database 84 | # https://docs.djangoproject.com/en/1.9/ref/settings/#databases 85 | 86 | DATABASES = { 87 | 'default': { 88 | 'ENGINE': 'django.db.backends.postgresql_psycopg2',# Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 89 | 'NAME': 'ocdaction', # Or path to database file if using sqlite3. 90 | 'USER': '', # Not used with sqlite3 91 | 'PASSWORD': '', # Not used with sqlite3. 92 | 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 93 | 'PORT': '', # Set to empty string for default. Not used with sqlite3. 94 | } 95 | } 96 | 97 | 98 | # Password validation 99 | # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators 100 | 101 | AUTH_PASSWORD_VALIDATORS = [ 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 104 | }, 105 | { 106 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 107 | 'OPTIONS': { 108 | 'min_length': 10, 109 | } 110 | }, 111 | { 112 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 113 | }, 114 | { 115 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 116 | }, 117 | ] 118 | 119 | 120 | # Internationalization 121 | # https://docs.djangoproject.com/en/1.9/topics/i18n/ 122 | 123 | LANGUAGE_CODE = 'en-us' 124 | 125 | TIME_ZONE = 'UTC' 126 | 127 | USE_I18N = True 128 | 129 | USE_L10N = True 130 | 131 | USE_TZ = True 132 | 133 | AUTH_USER_MODEL = 'profiles.OCDActionUser' 134 | 135 | CONTEXT_PROCESSORS = [ 136 | "django.contrib.auth.context_processors.auth", 137 | "django.template.context_processors.debug", 138 | "django.template.context_processors.i18n", 139 | "django.template.context_processors.media", 140 | "django.template.context_processors.static", 141 | "django.template.context_processors.tz", 142 | "django.contrib.messages.context_processors.messages", 143 | "django.template.context_processors.request", 144 | ] 145 | 146 | 147 | 148 | # Static files (CSS, JavaScript, Images) 149 | # https://docs.djangoproject.com/en/1.6/howto/static-files/ 150 | # 151 | 152 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 153 | 154 | 155 | STATICFILES_DIRS = [ 156 | os.path.join(BASE_DIR, "static"), 157 | ] 158 | 159 | # Static and media file storage with AWS S3 160 | 161 | AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME'] 162 | AWS_S3_REGION_NAME = 'eu-west-2' 163 | AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID'] 164 | AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY'] 165 | 166 | AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME 167 | 168 | STATICFILES_LOCATION = 'static' 169 | STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' 170 | STATIC_URL = AWS_S3_CUSTOM_DOMAIN + '/static/' 171 | 172 | MEDIAFILES_LOCATION = 'media' 173 | DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' 174 | MEDIA_URL = AWS_S3_CUSTOM_DOMAIN + '/media/' 175 | 176 | # Registration settings 177 | 178 | ACCOUNT_ACTIVATION_DAYS = 7 179 | REGISTRATION_EMAIL_HTML = True 180 | REGISTRATION_AUTO_LOGIN = True 181 | 182 | EMAIL_BACKEND = 'django_ses.SESBackend' 183 | REGISTRATION_DEFAULT_FROM_EMAIL = 'youth@ocdaction.org.uk' 184 | DEFAULT_FROM_EMAIL = 'youth@ocdaction.org.uk' 185 | AWS_SES_REGION_NAME = 'us-east-1' 186 | AWS_SES_REGION_ENDPOINT = 'email.us-east-1.amazonaws.com' 187 | -------------------------------------------------------------------------------- /frontend/src/sass/inspinia/_rtl.scss: -------------------------------------------------------------------------------- 1 | //RTL Support 2 | body.rtls { 3 | 4 | #page-wrapper { 5 | margin: 0 220px 0 0; 6 | } 7 | 8 | .nav-second-level li a { 9 | padding: 7px 35px 7px 10px; 10 | } 11 | 12 | .ibox-title h5 { 13 | float: right; 14 | } 15 | 16 | .pull-right { 17 | float: left !important; 18 | } 19 | 20 | .pull-left { 21 | float: right !important; 22 | } 23 | 24 | .ibox-tools { 25 | float: left; 26 | } 27 | 28 | .stat-percent { 29 | float: left; 30 | } 31 | 32 | .navbar-right { 33 | float: left !important; 34 | } 35 | 36 | .navbar-top-links li:last-child { 37 | margin-left: 40px; 38 | margin-right: 0; 39 | } 40 | 41 | .minimalize-styl-2 { 42 | float: right; 43 | margin: 14px 20px 5px 5px; 44 | } 45 | 46 | .feed-element > .pull-left { 47 | margin-left: 10px; 48 | margin-right: 0; 49 | } 50 | 51 | .timeline-item .date { 52 | text-align: left; 53 | } 54 | 55 | .timeline-item .date i { 56 | left: 0; 57 | right: auto; 58 | } 59 | 60 | .timeline-item .content { 61 | border-right: 1px solid #e7eaec; 62 | border-left: none; 63 | } 64 | 65 | .theme-config { 66 | left: 0; 67 | right: auto; 68 | } 69 | 70 | .spin-icon { 71 | border-radius: 0 20px 20px 0; 72 | } 73 | 74 | .toast-close-button { 75 | float: left; 76 | } 77 | 78 | #toast-container > .toast:before { 79 | margin: auto -1.5em auto 0.5em; 80 | } 81 | 82 | #toast-container > div { 83 | padding: 15px 50px 15px 15px; 84 | } 85 | 86 | .center-orientation .vertical-timeline-icon i { 87 | margin-left: 0; 88 | margin-right: -12px; 89 | } 90 | 91 | .vertical-timeline-icon i { 92 | right: 50%; 93 | left: auto; 94 | margin-left: auto; 95 | margin-right: -12px; 96 | } 97 | 98 | .file-box { 99 | float: right; 100 | } 101 | 102 | ul.notes li { 103 | float: right; 104 | } 105 | 106 | .chat-users, .chat-statistic { 107 | margin-right: -30px; 108 | margin-left: auto; 109 | } 110 | 111 | .dropdown-menu > li > a { 112 | text-align: right; 113 | } 114 | 115 | .b-r { 116 | border-left: 1px solid #e7eaec; 117 | border-right: none; 118 | } 119 | 120 | .dd-list .dd-list { 121 | padding-right: 30px; 122 | padding-left: 0; 123 | } 124 | 125 | .dd-item > button { 126 | float: right 127 | } 128 | 129 | /* Theme config */ 130 | .theme-config-box { 131 | margin-left: -220px; 132 | margin-right: 0; 133 | } 134 | 135 | .theme-config-box.show { 136 | margin-left: 0; 137 | margin-right: 0; 138 | } 139 | 140 | .spin-icon { 141 | right: 0; 142 | left: auto; 143 | } 144 | 145 | .skin-settings { 146 | margin-right: 40px; 147 | margin-left: 0; 148 | } 149 | 150 | .skin-settings { 151 | direction: ltr; 152 | } 153 | 154 | .footer.fixed { 155 | margin-right: 220px; 156 | margin-left: 0; 157 | } 158 | 159 | } 160 | 161 | @media (max-width: 992px) { 162 | body.rtls { 163 | .chat-users, .chat-statistic { 164 | margin-right: 0; 165 | } 166 | } 167 | } 168 | 169 | body.rtls.mini-navbar .footer.fixed, body.body-small.mini-navbar .footer.fixed { 170 | margin: 0 70px 0 0; 171 | } 172 | 173 | body.rtls.mini-navbar.fixed-sidebar .footer.fixed, body.body-small.mini-navbar .footer.fixed { 174 | margin: 0 0 0 0; 175 | } 176 | 177 | body.rtls.top-navigation .navbar-toggle { 178 | float: right; 179 | margin-left: 15px; 180 | margin-right: 15px; 181 | } 182 | 183 | .body-small.rtls.top-navigation .navbar-header { 184 | float: none; 185 | } 186 | 187 | body.rtls.top-navigation #page-wrapper { 188 | margin: 0; 189 | } 190 | 191 | body.rtls.mini-navbar #page-wrapper { 192 | margin: 0 70px 0 0; 193 | } 194 | 195 | body.rtls.mini-navbar.fixed-sidebar #page-wrapper { 196 | margin: 0 0 0 0; 197 | } 198 | 199 | body.rtls.body-small.fixed-sidebar.mini-navbar #page-wrapper { 200 | margin: 0 $sidebar-width 0 0; 201 | } 202 | 203 | body.rtls.body-small.fixed-sidebar.mini-navbar .navbar-static-side { 204 | width: $sidebar-width; 205 | } 206 | 207 | .body-small.rtls .navbar-fixed-top { 208 | margin-right: 0; 209 | } 210 | 211 | .body-small.rtls .navbar-header { 212 | float: right; 213 | } 214 | 215 | body.rtls .navbar-top-links li:last-child { 216 | margin-left: 20px; 217 | } 218 | 219 | body.rtls .top-navigation #page-wrapper, body.rtls.mini-navbar .top-navigation #page-wrapper, body.rtls.mini-navbar.top-navigation #page-wrapper { 220 | margin: 0; 221 | } 222 | 223 | body.rtls .top-navigation .footer.fixed, body.rtls.top-navigation .footer.fixed { 224 | margin: 0; 225 | } 226 | 227 | @media (max-width: 768px) { 228 | 229 | body.rtls .navbar-top-links li:last-child { 230 | margin-left: 20px; 231 | } 232 | 233 | .body-small.rtls #page-wrapper { 234 | position: inherit; 235 | margin: 0 0 0 0; 236 | min-height: 1000px; 237 | } 238 | 239 | .body-small.rtls .navbar-static-side { 240 | display: none; 241 | z-index: 2001; 242 | position: absolute; 243 | width: 70px; 244 | } 245 | 246 | .body-small.rtls.mini-navbar .navbar-static-side { 247 | display: block; 248 | } 249 | 250 | .rtls.fixed-sidebar.body-small .navbar-static-side { 251 | display: none; 252 | z-index: 2001; 253 | position: fixed; 254 | width: $sidebar-width; 255 | } 256 | 257 | .rtls.fixed-sidebar.body-small.mini-navbar .navbar-static-side { 258 | display: block; 259 | } 260 | 261 | } 262 | 263 | // For special ltr supporting plugin 264 | .rtls .ltr-support { 265 | direction: ltr; 266 | } 267 | 268 | .rtls.mini-navbar .nav-second-level, .rtls.mini-navbar li.active .nav-second-level { 269 | left: auto; 270 | right: 70px; 271 | } 272 | 273 | .rtls #right-sidebar { 274 | left: -260px; 275 | right: auto; 276 | } 277 | 278 | .rtls #right-sidebar.sidebar-open { 279 | left: 0; 280 | } 281 | -------------------------------------------------------------------------------- /ocdaction/ocdaction/templates/core/learn.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block title %}Learn: What is OCD?{% endblock %} 4 | 5 | {% load static %} 6 | 7 | {% block main %} 8 |
9 |
10 |
11 |

What is OCD?

12 |
13 |

OCD stands for Obsessive Compulsive Disorder.

14 | 15 |

Obsessions are thoughts, worries, images, impulses, doubts and urges that pop into your mind and it makes you feel 16 | anxious. They are not very nice and they are not wanted.

17 | 18 |

Compulsions are actions or things we do; they can be things people can see you do or things you do in your head. 19 | They are usually done to make obsessions or anxiety go away.

20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 |

Everyone has unwanted thoughts that pop into their head or habits they like to do, but to have OCD the obsessions 34 | and compulsions take up more than an hour of your day and they cause distress or anxiety and they get in the way 35 | of the things that you would like to do or things your friends are doing.

36 | 37 |

OCD is really common - in young people between 1-2% have it.

38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 | 48 |
49 |
50 |

Why do we get anxious?

51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 | 59 |
60 |

Anxiety is a normal feeling that everyone has from time to time. When we feel anxious we usually get changes 61 | in our body to help us understand how we feel e.g. racing heart, shaking, feeling sick.

62 | 63 |

Anxiety is a helpful emotion. Its helps us face up to real dangers. This response is known as the fight or 64 | flight response.

65 | 66 |

Different kinds of situations and things cause us to feel anxious or worried. Some things cause us to feel 67 | more anxious that others. This app will help you rate the things that make you anxious.

68 |
69 |
70 |
71 | 72 |
73 |
74 |

How does CBT work?

75 |
76 |

CBT stands for Cognitive Behaviour Therapy. You should see a therapist to help you with CBT.

77 | 78 |

To beat OCD, you need to a) understand anxiety and how it works, b) develop a list of the things that 79 | OCD makes you do and put them in order of what would make you least to most anxious (hierarchy) and 80 | c) do exposure with response prevention (ERP) tasks to fight OCD.

81 | 82 |

This app is designed to help you do just that.

83 | 84 |

By following those steps, you should then have a good relapse prevention plan when you have beaten OCD.

85 |
86 | 87 |
88 |
89 |
90 | 91 |
92 |
93 |
94 | 95 |
96 |
97 |
98 |
99 | 100 |
101 |
102 |

What is ERP and why should I do it?

103 |
104 |
105 |
106 | 107 |
108 |
109 |
110 |
111 |

Exposure with response prevention (ERP) is the main tool to fight OCD.

112 | 113 |

OCD makes us feel scared or uncomfortable and ‘tricks’ us into worrying or doing things we know may be silly or 114 | that we do not really want to do. To fight OCD, we have to face the situation that triggers the obsession 115 | (exposure) and then we resist doing the ritual (response prevention). We learn that when we expose ourselves 116 | to the trigger and do not do a ritual, at first we feel anxious, but the anxiety reduces over time 117 | (anxiety habituation).

118 |
119 |
120 | 121 |
122 |
123 |
124 |
125 | 126 |
127 |
128 |
129 | 130 |
131 |

By doing ERP regularly - so by practicing facing your fears and resisting doing the rituals consistently - you will 132 | notice it will get easier to resist each time and anxiety will reduce each time you do it.

133 | 134 |

You will also learn that OCD is playing tricks by saying the obsession will come true if you don’t do the ritual... the 135 | only way to find this out is by not doing what OCD wants!

136 |
137 |
138 |
139 | 140 |
141 |
142 |

Fighting Back

143 |
144 |

OCD is a severe but treatable condition and it is possible to get better. CBT with exposure and response prevention 145 | can help us get to a place where OCD is manageable and doesn’t stop us doing what we want to do.

146 | 147 |

This app is designed to help with that process, by putting into practice all those things we learn with our CBT 148 | therapist. Let’s use this app to practice facing our fears and together we can beat OCD.

149 |
150 | 151 |
152 |
153 | 154 |
155 |
156 |
157 |
158 |
159 | {% endblock %} 160 | --------------------------------------------------------------------------------