├── 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 |
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 |
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 |
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 |
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 | 
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 |
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 |
17 |
Add new challenge
18 |
19 |
20 |
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 |
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 |
18 |
{{ challenge.challenge_name }}
19 |
20 |
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 |
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 |
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 |
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 |
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 | Initial anxiety level:
22 | {% render_anxiety_level latest_scores.0 %}
23 |
24 |
25 | After 5mins:
26 | {% render_anxiety_level latest_scores.1 %}
27 |
28 |
29 | After 10mins:
30 | {% render_anxiety_level latest_scores.2 %}
31 |
32 |
33 | After 15mins:
34 | {% render_anxiety_level latest_scores.3 %}
35 |
36 |
37 | After 30mins:
38 | {% render_anxiety_level latest_scores.4 %}
39 |
40 |
41 | After 60mins:
42 | {% render_anxiety_level latest_scores.5 %}
43 |
44 |
45 | After 120mins:
46 | {% render_anxiety_level latest_scores.6 %}
47 |
48 |
49 |
50 |
51 |
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 | Level
29 | Challenge
30 |
31 |
32 |
33 | {% for challenge in challenges_archived %}
34 |
35 |
36 | {% get_anxiety_level challenge %}
37 |
38 |
39 |
40 | {{ challenge.challenge_name }}
41 |
42 |
43 |
44 | {% endfor %}
45 |
46 |
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 |
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 |
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 |
27 |
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 |
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 |
19 |
{{ challenge.challenge_name }}
20 |
21 |
22 |
23 |
Congratulations!
24 |
You have tackled this exposure
25 |
26 |
27 |
28 |
29 | Most
recent
30 |
31 |
32 |
33 |
34 |
35 |
36 | Initial anxiety level:
37 | {% for card in anxiety_score_cards %}
38 | {% render_anxiety_level card.anxiety_at_0_min %}
39 | {% endfor %}
40 |
41 |
42 | After 5mins:
43 | {% for card in anxiety_score_cards %}
44 | {% render_anxiety_level card.anxiety_at_5_min %}
45 | {% endfor %}
46 |
47 |
48 | After 10mins:
49 | {% for card in anxiety_score_cards %}
50 | {% render_anxiety_level card.anxiety_at_10_min %}
51 | {% endfor %}
52 |
53 |
54 | After 15mins:
55 | {% for card in anxiety_score_cards %}
56 | {% render_anxiety_level card.anxiety_at_15_min %}
57 | {% endfor %}
58 |
59 |
60 | After 30mins:
61 | {% for card in anxiety_score_cards %}
62 | {% render_anxiety_level card.anxiety_at_30_min %}
63 | {% endfor %}
64 |
65 |
66 | After 60mins:
67 | {% for card in anxiety_score_cards %}
68 | {% render_anxiety_level card.anxiety_at_60_min %}
69 | {% endfor %}
70 |
71 |
72 | After 120mins:
73 | {% for card in anxiety_score_cards %}
74 | {% render_anxiety_level card.anxiety_at_120_min %}
75 | {% endfor %}
76 |
77 |
78 |
79 |
80 |
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, youthled 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 |
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 |
38 | {{ radio.choice_label }}
39 |
40 | {% endfor %}
41 |
42 |
43 |
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 |
58 | {{ radio.choice_label }}
59 |
60 | {% endfor %}
61 |
62 |
63 |
68 | {% else %}
69 |
70 | {{ field.errors }}
71 | {{ field.label_tag }}
72 |
73 | {% for radio in field|slice:"1:" %}
74 | {{ radio.tag }}
75 |
76 | {{ radio.choice_label }}
77 |
78 | {% endfor %}
79 |
80 |
81 |
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 |
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 | VIDEO
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 | VIDEO
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
Why do we get anxious?
51 |
52 |
53 |
54 | VIDEO
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 | VIDEO
91 |
92 |
93 |
94 | VIDEO
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
What is ERP and why should I do it?
103 |
104 |
105 |
106 | VIDEO
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 | VIDEO
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 | VIDEO
154 |
155 |
156 |
157 |
158 |
159 | {% endblock %}
160 |
--------------------------------------------------------------------------------