├── base
├── __init__.py
├── migrations
│ ├── __init__.py
│ └── 0001_initial.py
├── tests.py
├── static
│ ├── images
│ │ ├── fav.ico
│ │ ├── background.jpg
│ │ └── sanabil-logo.png
│ ├── fonts
│ │ ├── arabeyes-qr.ttf
│ │ └── ScheherazadeRegOT.ttf
│ ├── css
│ │ └── scss
│ │ │ ├── _mixins.scss
│ │ │ ├── main.scss
│ │ │ ├── pages
│ │ │ ├── _contact.scss
│ │ │ ├── _other_pages.scss
│ │ │ └── _home.scss
│ │ │ ├── _base.scss
│ │ │ ├── _variables.scss
│ │ │ ├── _layout.scss
│ │ │ ├── _component.scss
│ │ │ └── _vendor.scss
│ └── js
│ │ └── dataTable.js
├── apps.py
├── forms.py
├── admin.py
├── views.py
├── models.py
└── fixtures
│ └── wilayas.json
├── staff
├── __init__.py
├── migrations
│ ├── __init__.py
│ ├── 0003_merge_20190210_0945.py
│ ├── 0003_merge_20191028_0604.py
│ ├── 0004_merge_20191028_0626.py
│ ├── 0002_auto_20181108_1944.py
│ ├── 0002_auto_20181109_0341.py
│ └── 0001_initial.py
├── tests.py
├── constants.py
├── apps.py
├── views.py
├── admin.py
└── models.py
├── Sanabil
├── __init__.py
├── wsgi.py
├── urls.py
└── settings.py
├── charity
├── __init__.py
├── migrations
│ ├── __init__.py
│ ├── 0011_merge_20191028_0626.py
│ ├── 0008_besoin_expire_le.py
│ ├── 0014_auto_20191028_0637.py
│ ├── 0015_necessiteux_address.py
│ ├── 0009_necessiteux_jeune_fille.py
│ ├── 0004_necessiteux_appartient_centre.py
│ ├── 0006_auto_20190216_1028.py
│ ├── 0005_auto_20190209_1833.py
│ ├── 0012_auto_20191028_0630.py
│ ├── 0007_auto_20190331_1050.py
│ ├── 0013_auto_20191028_0635.py
│ ├── 0002_auto_20190209_1741.py
│ ├── 0003_auto_20190209_1807.py
│ ├── 0010_auto_20190402_1823.py
│ ├── 0002_auto_20191028_0621.py
│ └── 0001_initial.py
├── tests.py
├── apps.py
├── admin.py
├── views.py
└── models.py
├── Procfile
├── 0.specs
└── Plateforme association.pdf
├── templates
├── rest_framework
│ ├── login.html
│ └── base.html
├── contact.html
├── charity
│ ├── statistiques.html
│ ├── necessiteux_list.html
│ └── besoin_list.html
├── footer.html
├── header.html
├── help.html
├── admin
│ └── base_site.html
├── staff
│ └── association_list.html
├── base.html
└── home.html
├── manage.py
├── scripts
└── reset_db.sh
├── requirements.txt
├── README.md
├── .gitignore
└── LICENSE
/base/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/staff/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Sanabil/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/charity/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/base/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/staff/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/charity/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | release: python manage.py migrate --no-input
2 | web: gunicorn Sanabil.wsgi
--------------------------------------------------------------------------------
/base/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/charity/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/staff/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/staff/constants.py:
--------------------------------------------------------------------------------
1 | PROFILE_TYPES = [(1, 'Sanabil Admin'), (2, 'Association Responsible')]
2 |
3 |
4 |
--------------------------------------------------------------------------------
/base/static/images/fav.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dzNLPCommunity/sanabil/HEAD/base/static/images/fav.ico
--------------------------------------------------------------------------------
/base/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class BaseConfig(AppConfig):
5 | name = 'base'
6 |
--------------------------------------------------------------------------------
/staff/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class StaffConfig(AppConfig):
5 | name = 'staff'
6 |
--------------------------------------------------------------------------------
/0.specs/Plateforme association.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dzNLPCommunity/sanabil/HEAD/0.specs/Plateforme association.pdf
--------------------------------------------------------------------------------
/base/static/fonts/arabeyes-qr.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dzNLPCommunity/sanabil/HEAD/base/static/fonts/arabeyes-qr.ttf
--------------------------------------------------------------------------------
/base/static/images/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dzNLPCommunity/sanabil/HEAD/base/static/images/background.jpg
--------------------------------------------------------------------------------
/base/static/images/sanabil-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dzNLPCommunity/sanabil/HEAD/base/static/images/sanabil-logo.png
--------------------------------------------------------------------------------
/charity/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class DonationConfig(AppConfig):
5 | name = 'charity'
6 |
--------------------------------------------------------------------------------
/base/static/fonts/ScheherazadeRegOT.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dzNLPCommunity/sanabil/HEAD/base/static/fonts/ScheherazadeRegOT.ttf
--------------------------------------------------------------------------------
/templates/rest_framework/login.html:
--------------------------------------------------------------------------------
1 | {% extends "rest_framework/login_base.html" %}
2 |
3 | {% block branding %}
4 |
Sanabil API
5 | {% endblock %}
--------------------------------------------------------------------------------
/base/static/css/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin user-select($color_select, $color_select_background) {
2 | user-select: none;
3 | color: $color_select;
4 | background: $color_select_background;
5 | }
--------------------------------------------------------------------------------
/Sanabil/wsgi.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from django.core.wsgi import get_wsgi_application
4 |
5 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Sanabil.settings")
6 |
7 | application = get_wsgi_application()
8 |
--------------------------------------------------------------------------------
/staff/views.py:
--------------------------------------------------------------------------------
1 | from django.views.generic import ListView
2 |
3 | from staff.models import Association
4 |
5 |
6 | class AssociationListView(ListView):
7 | model = Association
8 | ordering = ['-nom']
9 | paginate_by = 10
--------------------------------------------------------------------------------
/base/forms.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | from django import forms
4 |
5 | class ContactForm(forms.Form):
6 | Votre_email = forms.EmailField(required=True)
7 | Sujet = forms.CharField(required=True)
8 | Votre_message = forms.CharField(widget=forms.Textarea, required=True)
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.6
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Sanabil.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/scripts/reset_db.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | find . -path "*/migrations/*.py" -not -name "__init__.py" -delete
3 | find . -path "*/migrations/*.pyc" -delete
4 |
5 | rm db.sqlite3
6 |
7 | ./manage.py makemigrations && ./manage.py migrate && ./manage.py createsuperuser --username admin --email assem.ch@gmail.com && ./manage.py loaddata */fixtures/*.json
8 |
9 |
10 |
--------------------------------------------------------------------------------
/staff/migrations/0003_merge_20190210_0945.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-02-10 08:45
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('staff', '0002_auto_20181109_0341'),
10 | ('staff', '0002_auto_20181108_1944'),
11 | ]
12 |
13 | operations = [
14 | ]
15 |
--------------------------------------------------------------------------------
/staff/migrations/0003_merge_20191028_0604.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-10-28 06:04
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('staff', '0002_auto_20181108_1944'),
10 | ('staff', '0002_auto_20181109_0341'),
11 | ]
12 |
13 | operations = [
14 | ]
15 |
--------------------------------------------------------------------------------
/staff/migrations/0004_merge_20191028_0626.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-10-28 06:26
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('staff', '0003_merge_20191028_0604'),
10 | ('staff', '0003_merge_20190210_0945'),
11 | ]
12 |
13 | operations = [
14 | ]
15 |
--------------------------------------------------------------------------------
/charity/migrations/0011_merge_20191028_0626.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-10-28 06:26
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0010_auto_20190402_1823'),
10 | ('charity', '0002_auto_20191028_0621'),
11 | ]
12 |
13 | operations = [
14 | ]
15 |
--------------------------------------------------------------------------------
/base/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.apps import apps
3 |
4 | baseapp = apps.get_app_config('base')
5 |
6 | for model in baseapp.get_models():
7 | admin.site.register(model)
8 |
9 | from django.contrib.auth.models import Permission
10 |
11 | admin.site.register(Permission)
12 |
13 |
14 | admin.site.site_header = "Sanabil"
15 | admin.site.site_title = "Sanabil"
16 | admin.site.admin_name ='Unchained'
17 |
18 |
--------------------------------------------------------------------------------
/charity/migrations/0008_besoin_expire_le.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-03-31 14:03
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0007_auto_20190331_1050'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='besoin',
15 | name='expire_le',
16 | field=models.DateField(blank=True, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/charity/migrations/0014_auto_20191028_0637.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-10-28 06:37
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0013_auto_20191028_0635'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='besoin',
15 | name='délivré_par',
16 | field=models.CharField(blank=True, max_length=100, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/charity/migrations/0015_necessiteux_address.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-10-28 18:39
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0014_auto_20191028_0637'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='necessiteux',
15 | name='address',
16 | field=models.TextField(blank=True, null=True, verbose_name='adresse postale'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/base/static/css/scss/main.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Amiri');
2 |
3 | @font-face {
4 | font-family: 'Scheherazade';
5 | src: url('../../fonts/ScheherazadeRegOT.ttf');
6 | }
7 |
8 | @font-face {
9 | font-family: 'ArabeyesQr';
10 | src: url('../../fonts/arabeyes-qr.ttf');
11 | }
12 |
13 | @import "variables";
14 | @import "mixins";
15 | @import "component";
16 | @import "layout";
17 | @import "base";
18 | @import "pages/contact";
19 | @import "pages/other_pages";
20 | @import "pages/home";
21 | @import "vendor";
22 |
--------------------------------------------------------------------------------
/base/static/css/scss/pages/_contact.scss:
--------------------------------------------------------------------------------
1 | .contact-form {
2 | padding-bottom: 10px !important;
3 | }
4 | .contact-infos {
5 | margin-top: 3%;
6 | .row {
7 | margin-bottom: 10px;
8 | }
9 | button {
10 | float: right;
11 | }
12 | }
13 |
14 | @media only screen and (max-width: 768px) {
15 | .navigator > .item:nth-child(n+2), .contact-button {
16 | display: none !important;
17 | }
18 | .ui.grid > [class*="seven wide"].column {
19 | width: 70% !important;
20 | }
21 | .contact-infos {
22 | display: none !important;
23 | }
24 | }
--------------------------------------------------------------------------------
/base/static/css/scss/_base.scss:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 16px;
3 | }
4 |
5 | .clr {
6 | clear: both;
7 | }
8 |
9 | .hidden {
10 | display: none !important;
11 | }
12 |
13 | body::-webkit-scrollbar {
14 | width: 10px;
15 | background: $color_light_grey;
16 | }
17 |
18 | body::-webkit-scrollbar-track {
19 | background: $color_light_grey;
20 | }
21 |
22 | body::-webkit-scrollbar-thumb {
23 | background: rgba(0, 0, 0, .87);
24 | outline: 1px solid rgba(0, 0, 0, .87);
25 | }
26 |
27 | body.pushable {
28 | background: transparent !important;
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/charity/migrations/0009_necessiteux_jeune_fille.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-03-31 15:16
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0008_besoin_expire_le'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='necessiteux',
15 | name='jeune_fille',
16 | field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Nom de jeune fille'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/charity/migrations/0004_necessiteux_appartient_centre.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-02-09 17:29
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('charity', '0003_auto_20190209_1807'),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name='necessiteux',
16 | name='appartient_centre',
17 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='charity.Centre'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | django>=2.0.11
2 | django-debug-toolbar
3 | qrcode==5.1
4 | django-location-field
5 | pillow
6 | djangorestframework
7 | dynamic-rest
8 | django-rest-swagger
9 | markdown
10 | django-filter
11 | dj-database-url
12 | whitenoise
13 | django-annoying
14 | pyfcm
15 | easy-thumbnails
16 | unicodecsv
17 | django-import-export
18 | django-widget-tweaks
19 | fcm-django
20 | django-storages
21 | #boto3
22 | django-haystack
23 | drf-haystack
24 | whoosh
25 | mysqlclient
26 | django-mysql
27 | dj_database_url
28 | gunicorn
29 | django_extensions
30 | psycopg2-binary
31 | libsass
32 | django-compressor
33 | django-sass-processor
34 | django-jet
--------------------------------------------------------------------------------
/charity/migrations/0006_auto_20190216_1028.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-02-16 09:28
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0005_auto_20190209_1833'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='besoin',
15 | name='necessiteux',
16 | ),
17 | migrations.AddField(
18 | model_name='besoin',
19 | name='necessiteux',
20 | field=models.ManyToManyField(blank=True, null=True, to='charity.Necessiteux'),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/charity/migrations/0005_auto_20190209_1833.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-02-09 17:33
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('charity', '0004_necessiteux_appartient_centre'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='necessiteux',
16 | name='pointure',
17 | field=models.IntegerField(blank=True, default=36, null=True, validators=[django.core.validators.MaxValueValidator(50), django.core.validators.MinValueValidator(20)]),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/charity/migrations/0012_auto_20191028_0630.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-10-28 06:30
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0011_merge_20191028_0626'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='necessiteux',
15 | name='appartient_centre',
16 | ),
17 | migrations.AlterField(
18 | model_name='necessiteux',
19 | name='tel',
20 | field=models.CharField(blank=True, max_length=20, null=True, verbose_name='tél'),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/charity/migrations/0007_auto_20190331_1050.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-03-31 09:50
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0006_auto_20190216_1028'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='necessiteux',
15 | name='est_permanent',
16 | ),
17 | migrations.AddField(
18 | model_name='necessiteux',
19 | name='type',
20 | field=models.SmallIntegerField(choices=[(0, 'Permanent'), (1, 'Temporaire'), (2, 'Voyageur en détresse')], default=0),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/templates/rest_framework/base.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends "rest_framework/base.html" %}
3 | {% load staticfiles %}
4 |
5 | {% block title %}{% if name %}{{ name }} – {% endif %}Sanabil API{% endblock %}
6 |
7 | {% block branding %}
8 |
9 | Sanabil API
10 |
11 | {% endblock %}
12 |
13 | {#{% block bootstrap_theme %}#}
14 | {# #}
15 | {# #}
16 | {#{% endblock %}#}
17 |
18 | {#{% block bootstrap_navbar_variant %}{% endblock %}#}
--------------------------------------------------------------------------------
/staff/migrations/0002_auto_20181108_1944.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.5 on 2018-11-08 18:44
2 |
3 | import django.contrib.auth.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('staff', '0001_initial'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='membre',
16 | name='username',
17 | field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/charity/migrations/0013_auto_20191028_0635.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-10-28 06:35
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0012_auto_20191028_0630'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='necessiteux',
15 | name='nin',
16 | field=models.CharField(blank=True, max_length=9, null=True, verbose_name='Numéro pièce d’identité'),
17 | ),
18 | migrations.AlterField(
19 | model_name='necessiteux',
20 | name='tel',
21 | field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Téléphone 1'),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/base/static/css/scss/_variables.scss:
--------------------------------------------------------------------------------
1 | $color_sanabil_accent: #F28230;
2 | $color_sanabil: #5DBCD2;
3 | $color_light_sanabil: #90dcf8;
4 | $color_ghost_white: #f8f8ff;
5 | $color_light_grey: lightgrey;
6 | $color_shark_15_approx: rgba(34, 36, 38, 0.15);
7 | $color_shark_30_approx: rgba(40, 40, 40, 0.3);
8 | $color_black_5: rgba(0, 0, 0, 0.05);
9 | $color_black_12: rgba(0, 0, 0, 0.12);
10 | $color_black_24: rgba(0, 0, 0, 0.24);
11 | $color_black_80: rgba(0, 0, 0, 0.8);
12 | $color_black_87: rgba(0, 0, 0, 0.87);
13 | $color_black_95: rgba(0, 0, 0, 0.95);
14 | $color_white: #fff;
15 |
16 | $color_font_0: Icons;
17 | $color_font_aya: 'Scheherazade';
18 | $color_font_quran_decoration: 'ArabeyesQr', 'Scheherazade';
19 |
20 | $aya_font_size: 43px;
21 | $home_header_height: calc(100vh - 10em);
22 | $footer-height: 250px;
23 |
--------------------------------------------------------------------------------
/staff/migrations/0002_auto_20181109_0341.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2018-11-09 03:41
2 |
3 | import django.contrib.auth.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('staff', '0001_initial'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='membre',
16 | name='username',
17 | field=models.CharField(default=1, error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'),
18 | preserve_default=False,
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/staff/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from staff.models import Association, Membre, Login
4 |
5 |
6 | @admin.register(Membre)
7 | class MembreAdmin(admin.ModelAdmin):
8 | list_display = ["nom", "prénom", "email", "tel"]
9 | search_fields = ["nom", "prénom", "email", "tel"]
10 | list_filter = ["profil"]
11 |
12 | @admin.register(Association)
13 | class AssociationAdmin(admin.ModelAdmin):
14 | list_display = ['nom', 'surnom', 'telephone', 'commune', 'website', 'responsable']
15 | list_display_links = ['nom','surnom']
16 | search_fields = ['nom', 'surnom', 'telephone']
17 | list_filter = ['commune__wilaya']
18 |
19 |
20 | @admin.register(Login)
21 | class LoginAdmin(admin.ModelAdmin):
22 | def has_add_permission(self, request):
23 | return False
24 |
25 | def has_delete_permission(self, request, obj=None):
26 | return False
27 |
--------------------------------------------------------------------------------
/charity/migrations/0002_auto_20190209_1741.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-02-09 16:41
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='necessiteux',
15 | name='date_de_naissance',
16 | field=models.DateField(blank=True, null=True),
17 | ),
18 | migrations.AlterField(
19 | model_name='necessiteux',
20 | name='pointure',
21 | field=models.IntegerField(blank=True, null=True),
22 | ),
23 | migrations.AlterField(
24 | model_name='necessiteux',
25 | name='taille',
26 | field=models.CharField(blank=True, max_length=100, null=True),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/charity/migrations/0003_auto_20190209_1807.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-02-09 17:07
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('charity', '0002_auto_20190209_1741'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='necessiteux',
15 | name='est_permanent',
16 | field=models.BooleanField(default=False),
17 | ),
18 | migrations.AlterField(
19 | model_name='necessiteux',
20 | name='prenom',
21 | field=models.CharField(max_length=100, verbose_name='prénom'),
22 | ),
23 | migrations.AlterField(
24 | model_name='necessiteux',
25 | name='tel',
26 | field=models.CharField(blank=True, max_length=20, null=True, verbose_name='tél'),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/charity/migrations/0010_auto_20190402_1823.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-04-02 17:23
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('charity', '0009_necessiteux_jeune_fille'),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name='besoin',
16 | name='délivré_par',
17 | field=models.TextField(blank=True, max_length=100, null=True),
18 | ),
19 | migrations.AddField(
20 | model_name='necessiteux',
21 | name='nin',
22 | field=models.CharField(blank=True, max_length=9, null=True, validators=[django.core.validators.RegexValidator(message='Champ invalide', regex='\\d{9}$')], verbose_name='Numéro pièce d’identité'),
23 | ),
24 | migrations.AddField(
25 | model_name='necessiteux',
26 | name='part_de',
27 | field=models.CharField(blank=True, max_length=100, null=True, verbose_name='De la part de'),
28 | ),
29 | ]
30 |
--------------------------------------------------------------------------------
/templates/contact.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block title %} Contactez-nous {% endblock %}
4 |
5 | {% block content %}
6 |
41 | {% endblock %}
--------------------------------------------------------------------------------
/base/views.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.contrib import messages
3 | from django.core.mail import send_mail, BadHeaderError
4 | from django.http import HttpResponse, HttpResponseRedirect
5 | from django.shortcuts import render, redirect
6 | from .forms import ContactForm
7 |
8 | class ContactForm(forms.Form):
9 | Votre_email = forms.EmailField(required=True)
10 | Sujet = forms.CharField(required=True)
11 | Votre_message = forms.CharField(widget=forms.Textarea, required=True)
12 |
13 | def contact_view(request):
14 | if request.method == 'GET':
15 | form = ContactForm()
16 | else:
17 | form = ContactForm(request.POST)
18 | if form.is_valid():
19 | subject = form.cleaned_data['Sujet']
20 | from_email = form.cleaned_data['Votre_email']
21 | message = form.cleaned_data['Votre_message']
22 | try:
23 | send_mail(subject, message, from_email, ['dz.sanabil@gmail.com'])
24 | except BadHeaderError:
25 | messages.error(request, 'Une erreur s\'est produite veuillez réessayer ultérieurement!')
26 | messages.success(request, 'Votre email a été envoyé avec succès!')
27 | return render(request, "contact.html", {'form': form})
28 |
29 | def contact_success_view(request):
30 | return HttpResponse('Success! Thank you for your message.')
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Sanabil
3 | A platform to organize the work of charity in Algiers
4 |
5 | ## Built with
6 |
7 | Sanabil was built with:
8 |
9 | * [Django](https://www.djangoproject.com/)- Python-based free and open-source web framework
10 | * [Semantic UI](https://semantic-ui.com/) - UI component framework for modern web apps
11 |
12 | ## Installation
13 |
14 | Sanabil requires [Python](https://www.python.org/download/releases/3.7/) version 3+ to run.
15 | ### Packages
16 | Install the required packages using pip:
17 |
18 | ```
19 | pip install -r requirements.txt
20 | ```
21 | ### Database
22 |
23 | For database and super-admin creation:
24 | ```
25 | ./manage.py makemigrations
26 | ./manage.py migrate
27 | ./manage.py createsuperuser
28 | ```
29 | And then fill fixtures by running:
30 | ```
31 | ./manage.py loaddata */fixtures/*.json
32 | ```
33 | ### Collecting static files
34 | Excute in terminal:
35 | ```
36 | ./manage.py collectstatic --noinput
37 |
38 | ```
39 |
40 | ### Running server:
41 |
42 | To run server locally run in terminal :
43 | `./manage.py runserver`
44 |
45 | And open in browser: [http://127.0.0.1:8000/](http://127.0.0.1:8000/) to check the plateforme
46 |
47 | ## Get involved!
48 | We are happy to receive bug reports, fixes, or any other enhancements.
49 | Please if you find any report bugs via the [github issue tracker](https://github.com/assem-ch/sanabil/issues).
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/base/static/css/scss/pages/_other_pages.scss:
--------------------------------------------------------------------------------
1 | .ui.statistics {
2 | margin-left: 15%;
3 | }
4 |
5 | .ui.modal .ui.horizontal.list {
6 | display: flex !important;
7 | }
8 | .needy-stats .ui.large.button {
9 | margin-bottom: 20px;
10 | }
11 |
12 | td.detail-click, td.actions {
13 | text-align: center!important;
14 | }
15 |
16 | .about {
17 | margin-bottom: 8% !important;
18 | }
19 |
20 | table#needy-table {
21 | th, td {
22 | text-align: center;
23 | }
24 | }
25 |
26 | @media only screen and (max-width: 768px) {
27 | #associations-table_filter label {
28 | text-align: left;
29 | }
30 | .contact-form {
31 | .ui.grid>[class*="seven wide"].column {
32 | width: 60% !important;
33 | }
34 | }
35 | .logo-sanabil {
36 | width: 65% !important;
37 | margin-left: 15%;
38 | }
39 | .about {
40 | .ui.horizontal.divider {
41 | display: inline-grid !important;
42 | }
43 | }
44 | }
45 |
46 | @media only screen and (min-width: 768px) {
47 | table#needy-table {
48 | th, td {
49 | text-align: left !important;
50 | }
51 | }
52 | .dataTables_filter {
53 | label {
54 | float: right;
55 | }
56 | }
57 | }
58 | @media only screen and (max-width: 400px) {
59 | #associations, #needs, #needy {
60 | &-table_wrapper .left.aligned.eight.wide.column {
61 | display: none !important;
62 | }
63 | td.actions {
64 | text-align: left !important;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/templates/charity/statistiques.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Nécessiteux
13 |
14 |
15 |
16 |
17 |
18 |
19 | Familles
20 |
21 |
22 |
23 |
24 |
25 |
26 | Enfants
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | La distribution des nécessiteux par sexe ( % )
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | La distribution des nécessiteux par tranche d'âge
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Sanabil/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url, include
2 | from django.contrib import admin
3 | from django.views.static import serve
4 |
5 | from base.views import contact_view, contact_success_view
6 | from charity import views as charity_views
7 | from Sanabil import settings
8 | from charity.views import NecessiteuxListView, NecessiteuxData, AideRecuListView, BesoinListView
9 | from staff.views import AssociationListView
10 |
11 |
12 | admin.site.site_header = 'Sanabil Administration'
13 | admin.site.site_title = 'Sanabil'
14 |
15 |
16 | urlpatterns = [
17 | url(r'^jet/', include('jet.urls', 'jet')),
18 | url(r'^admin/', admin.site.urls),
19 | url(r'^media/(?P.*)$', serve, { 'document_root': settings.MEDIA_ROOT}),
20 | url(r'^associations/?', AssociationListView.as_view(), name='association-list'),
21 | url(r'^necessiteux/data/$', NecessiteuxData.as_view()),
22 | url(r'^necessiteux/?', NecessiteuxListView.as_view(), name='necessiteux-list'),
23 | url(r'^besoins/?', BesoinListView.as_view(), name='besoin-list'),
24 | url(r'^aides/?', AideRecuListView.as_view(), name='aiderecu-list'),
25 | url(r'^about/?', charity_views.helppage, name='help-page'),
26 | url(r'^contact/', contact_view, name='contact-page'),
27 | url(r'^contact-success/', contact_success_view, name='contact-success-page'),
28 | url(r'^$', charity_views.homepage, name='home-page'),
29 | ]
30 |
31 | if settings.DEBUG:
32 | import debug_toolbar
33 | urlpatterns = [
34 | url(r'^__debug__/', include(debug_toolbar.urls)),
35 | ] + urlpatterns
--------------------------------------------------------------------------------
/base/static/css/scss/pages/_home.scss:
--------------------------------------------------------------------------------
1 | .masthead {
2 | position: relative !important;
3 | padding: 1em 0 !important;
4 | background-color: $color_black_80 !important;
5 | &:after {
6 | content: "";
7 | position: absolute;
8 | background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(11, 1, 1, 0.5)), url("../../images/background.jpg") !important;
9 | filter: blur(5px);
10 | top: 0;
11 | left: 0;
12 | right: 0;
13 | bottom: 0;
14 | z-index: -1;
15 | opacity: 0.5;
16 | }
17 | .ui.sanabil-color.button.inverted {
18 | background-color: transparent;
19 | box-shadow: 0 0 0 2px $color_ghost_white inset !important;
20 | &.home:hover {
21 | transition: all 0.6s linear;
22 | opacity: 0.9;
23 | border: transparent !important;
24 | background-color: $color_ghost_white !important;
25 | color: $color_black_80 !important;
26 | }
27 | }
28 | }
29 |
30 | #aya {
31 | font-family: 'Scheherazade';
32 | font-size: 45px;
33 | padding: 45px;
34 | font-weight: 400;
35 | color: $color_ghost_white !important;
36 | .quran_decoration {
37 | direction: rtl;
38 | font-family: 'ArabeyesQr', 'Scheherazade';
39 | }
40 | .aya-surat {
41 | font-family: 'Amiri', serif;
42 | font-size: 14px;
43 | color: $color_sanabil_accent;
44 | }
45 | }
46 |
47 | .activities {
48 | max-width: 70em;
49 | .card {
50 | text-align: center;
51 | padding: 1em 1px;
52 | h2 {
53 | font-size: 1.4em
54 | }
55 | }
56 |
57 | }
58 |
59 | @media only screen and (max-width: 768px) {
60 | #aya {
61 | font-size: 28px;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | local_settings.py
56 |
57 | # Flask stuff:
58 | instance/
59 | .webassets-cache
60 |
61 | # Scrapy stuff:
62 | .scrapy
63 |
64 | # Sphinx documentation
65 | docs/_build/
66 |
67 | # PyBuilder
68 | target/
69 |
70 | # Jupyter Notebook
71 | .ipynb_checkpoints
72 |
73 | # pyenv
74 | .python-version
75 |
76 | # celery beat schedule file
77 | celerybeat-schedule
78 |
79 | # SageMath parsed files
80 | *.sage.py
81 |
82 | # dotenv
83 | .env
84 |
85 | # virtualenv
86 | .venv
87 | venv/
88 | ENV/
89 |
90 | # Spyder project settings
91 | .spyderproject
92 | .spyproject
93 |
94 | # Rope project settings
95 | .ropeproject
96 |
97 | # mkdocs documentation
98 | /site
99 |
100 | # mypy
101 | .mypy_cache/
102 |
103 |
104 | #idea
105 | .idea/
106 |
107 | *.sqlite*
108 |
109 | **/indexes/
110 |
111 | **/static_files/**
--------------------------------------------------------------------------------
/templates/charity/necessiteux_list.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block title %} Nécessiteux {% endblock %}
4 |
5 | {% block content %}
6 |
7 |
8 |
9 |
10 |
11 |
12 | Code
13 | Sexe
14 | Age
15 | Taille
16 | Besoins
17 | Association à laquelle il appartient
18 |
19 |
20 |
21 | {% for needy in object_list %}
22 |
23 |
24 | {{ needy.id }}
25 |
26 |
27 | {% if needy.sexe == 'F' %}
28 | Féminin
29 | {% else %}
30 | Masculin
31 | {% endif %}
32 |
33 |
34 | {% if needy.get_age == None %}
35 | Âge inconnu
36 | {% else %}
37 | {{ needy.get_age }}
38 | {% endif %}
39 |
40 |
41 | {% if needy.taille == None %}
42 | Taille inconnue
43 | {% else %}
44 | {{ needy.taille }}
45 | {% endif %}
46 |
47 |
48 | {% if needy.get_need %}
49 | {% for need in needy.get_need %}
50 | {{ need|safe }},
51 | {% endfor %}
52 | {% else %}
53 | /
54 | {% endif %}
55 |
56 |
57 |
58 |
59 | {{ needy.association.nom }}
60 |
61 |
62 | {% empty %}
63 | Aucun nécessiteux est ajouté pour le moment
64 |
65 |
66 |
67 |
68 |
69 | {% endfor %}
70 |
71 |
72 |
73 |
74 | {% endblock %}
75 |
--------------------------------------------------------------------------------
/base/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 |
4 | class Wilaya(models.Model):
5 | id = models.IntegerField(primary_key=True)
6 | name = models.CharField(max_length=30)
7 | arabic_name = models.CharField(max_length=30, blank="")
8 |
9 | def __str__(self):
10 | return "{}. {}".format(self.id, self.name)
11 |
12 | class Commune(models.Model):
13 | id = models.IntegerField(primary_key=True)
14 | wilaya = models.ForeignKey(Wilaya, on_delete=models.CASCADE)
15 | name = models.CharField(max_length=50)
16 | postcode = models.IntegerField()
17 |
18 | def __str__(self):
19 | return "{}. {}".format(self.id, self.name)
20 |
21 |
22 | class Parameter(models.Model):
23 | name = models.CharField(max_length=40, unique=True)
24 | value = models.CharField(max_length=100)
25 | type = models.CharField(max_length=10,choices=[('int','Integer'),('bool','Boolean')])
26 | created_at = models.DateTimeField(auto_now_add=True)
27 | updated_at = models.DateTimeField(auto_now=True)
28 | def __str__(self):
29 | return "{} ({})".format(self.name, self.type)
30 |
31 |
32 | class NiveauScolaire(models.Model):
33 | label = models.CharField(max_length=100)
34 | order = models.IntegerField(unique=True)
35 |
36 | def __str__(self):
37 | return self.label
38 |
39 |
40 | class SituationFamiliale(models.Model):
41 | label = models.CharField(max_length=100)
42 |
43 | def __str__(self):
44 | return self.label
45 |
46 |
47 | class SituationProfessionelle(models.Model):
48 | label = models.CharField(max_length=100)
49 |
50 | def __str__(self):
51 | return self.label
52 |
53 |
54 |
55 | class CentreType(models.Model):
56 | label = models.CharField(max_length=100, unique=True)
57 |
58 | def __str__(self):
59 | return self.label
60 |
61 |
62 | class DonType(models.Model):
63 | label = models.CharField(max_length=100, unique=True)
64 |
65 | def __str__(self):
66 | return self.label
67 |
68 |
69 | class DonneurType(models.Model):
70 | label = models.CharField(max_length=100, unique=True)
71 |
72 | def __str__(self):
73 | return self.label
--------------------------------------------------------------------------------
/base/static/css/scss/_layout.scss:
--------------------------------------------------------------------------------
1 | .body-content {
2 | padding-bottom: $footer-height;
3 | }
4 | .ui {
5 | &.vertical {
6 | &.stripe {
7 | padding: 0;
8 | .horizontal.divider {
9 | margin: 48px 0;
10 | }
11 | }
12 | &.segment {
13 | border: none;
14 | padding-bottom: 0;
15 | }
16 | }
17 | .grid {
18 | margin: 0 !important;
19 | }
20 | .raised.segment {
21 | margin-bottom: 5%;
22 | text-align: center;
23 | }
24 | }
25 |
26 | .masthead.segment {
27 | min-height: $home_header_height;
28 | padding: 16px 0;
29 | opacity: 0.9;
30 | position: relative;
31 | h1.ui.header {
32 | margin-top: 32px;
33 | margin-bottom: 0;
34 | }
35 | }
36 |
37 | .quote.stripe.segment {
38 | padding: 0;
39 | .grid .column {
40 | padding-top: 80px;
41 | padding-bottom: 80px;
42 | }
43 | }
44 |
45 | .footer.segment {
46 | margin-top: 10px!important;
47 | position: absolute;
48 | bottom: 0;
49 | width: 100%;
50 | height: $footer-height;
51 | i.icon {
52 | margin-bottom: 10%;
53 | }
54 | button {
55 | margin-top: 10px;
56 | }
57 | .ui.inverted.header {
58 | margin-bottom: 30px;
59 | }
60 | }
61 |
62 | @media only screen and (max-width: 768px) {
63 | .needs-charts canvas {
64 | margin-left: 20% !important;
65 | height: 60% !important;
66 | width: 60% !important;
67 | }
68 | table#needy-table th, table#needy-table td {
69 | text-align: left !important;
70 | }
71 | .body-content {
72 | padding-bottom: 720px !important;
73 | }
74 | .masthead.segment {
75 | min-height: 350px;
76 | .menu {
77 | border: none;
78 | }
79 | h1.ui.header {
80 | margin-top: 1.5em;
81 | }
82 | }
83 | .footer {
84 | height: 700px !important;
85 | button.contact {
86 | margin-left: 25%;
87 | }
88 | }
89 |
90 | }
91 |
92 | @media only screen and (min-width: 768px) {
93 | .footer.segment {
94 | h4::after {
95 | content: "";
96 | width: 50px;
97 | height: 2px;
98 | background-color: $color_sanabil;
99 | position: absolute;
100 | bottom: 80%;
101 | left: 6%;
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/charity/migrations/0002_auto_20191028_0621.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.9 on 2019-10-28 06:21
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('charity', '0001_initial'),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name='necessiteux',
16 | name='health_state',
17 | field=models.SmallIntegerField(choices=[(0, 'En bonne santé'), (1, 'Malade'), (2, 'Maladie chronique')], default=0, verbose_name='état de santé'),
18 | ),
19 | migrations.AddField(
20 | model_name='necessiteux',
21 | name='investigated',
22 | field=models.BooleanField(default=False, verbose_name='investigation effectuée?'),
23 | ),
24 | migrations.AddField(
25 | model_name='necessiteux',
26 | name='other_infos',
27 | field=models.TextField(blank=True, null=True, verbose_name='informations supplémentaires'),
28 | ),
29 | migrations.AddField(
30 | model_name='necessiteux',
31 | name='tel_2',
32 | field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Téléphone 2'),
33 | ),
34 | migrations.AlterField(
35 | model_name='necessiteux',
36 | name='appartient_famille',
37 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='charity.Famille', verbose_name='appartient-il à une famille?'),
38 | ),
39 | migrations.AlterField(
40 | model_name='necessiteux',
41 | name='prenom',
42 | field=models.CharField(max_length=100, verbose_name='prénom'),
43 | ),
44 | migrations.AlterField(
45 | model_name='necessiteux',
46 | name='represent_famille',
47 | field=models.BooleanField(default=False, verbose_name='représente-il une famille?'),
48 | ),
49 | migrations.AlterField(
50 | model_name='necessiteux',
51 | name='tel',
52 | field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Téléphone 1'),
53 | ),
54 | ]
55 |
--------------------------------------------------------------------------------
/templates/footer.html:
--------------------------------------------------------------------------------
1 |
53 |
--------------------------------------------------------------------------------
/templates/header.html:
--------------------------------------------------------------------------------
1 |
32 |
33 |
--------------------------------------------------------------------------------
/templates/help.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block title %} Qui sommes-nous? {% endblock %}
3 | {% block content %}
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 | Sanabil est la seule plateforme en Algérie qui s’adresse à la fois aux
15 | associations qui souhaitent gérer et satisfaire les besoins des nécessiteux qui leurs
16 | sont affectés et aux personnes qui souhaitent faire un don et ce, peu importe leur
17 | localité.
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
30 |
31 | L’équipe Sanabil est une équipe motivée et entièrement bénévole, elle est composée de plusieurs
32 | membres rassemblés par l’envie d’aider ceux qui en ont besoin.
33 |
34 |
37 |
38 | Sanabil est une plateforme en ligne qui permet aux associations d’avoir une traçabilité des
39 | nécessiteux qui leur sont affectés , des dons reçus et des besoins de chaque famille. Elle permet
40 | également aux donateurs de consulter la liste des besoins reliés à chaque association et pouvoir ainsi
41 | les satisfaire de manière très rapide.
42 |
43 |
44 |
45 |
46 |
47 | {% endblock %}
--------------------------------------------------------------------------------
/base/static/css/scss/_component.scss:
--------------------------------------------------------------------------------
1 | .sanabil-color {
2 | color: $color_sanabil;
3 | &-accent {
4 | color: $color_sanabil_accent !important;
5 | }
6 | }
7 |
8 | .ui.secondary.pointing.menu{
9 |
10 | border-color: transparent!important;
11 | .item {
12 | &.active, &:hover {
13 | border-color: $color_sanabil_accent !important;
14 | color: $color_sanabil_accent !important;
15 | }
16 | &:hover {
17 | transition: all 0.5s ease-out;
18 | }
19 | &.logo, &.logo:hover {
20 | transform: scale(1.3);
21 | padding-bottom: 5px;
22 | padding-right: 20px;
23 | padding-left: 10px;
24 |
25 | border-color: transparent !important;
26 | color: transparent !important;
27 | }
28 | }
29 | }
30 |
31 | .text.color_sanabil, h1.header, .header a {
32 | color: $color_sanabil !important;
33 | }
34 |
35 | .text.sanabil-color, h1.header, .header a {
36 | color: $color_sanabil !important;
37 | }
38 |
39 | .contact-form .sanabil-color.button,
40 | .ui.footer .sanabil-color.button,
41 | .actions .sanabil-color.button,
42 | .ui.navigator .sanabil-color.button,
43 | .ui.sanabil-color.button:hover,
44 | .sanabil-color.button a,
45 | .ui.sanabil-color.button a:hover {
46 | color: $color_ghost_white !important;
47 | background-color: $color_sanabil !important;
48 | }
49 |
50 |
51 |
52 | .ui.navigator .right.menu .item:hover {
53 | border-bottom-color: transparent !important;
54 | }
55 | .masthead .logo.item img {
56 | margin-right: 16px;
57 | }
58 |
59 | .masthead .ui.menu .ui.button {
60 | margin-left: 8px;
61 | }
62 |
63 | .ui.vertical.stripe {
64 | .floated.image {
65 | clear: both;
66 |
67 | }
68 | .button, p {
69 | + h3 {
70 | margin-top: 48px;
71 | }
72 | }
73 | }
74 |
75 | .ui.cards {
76 | margin-left: 15%;
77 | }
78 |
79 | .secondary.menu .toc.item, .hidden.menu, .silder-icon {
80 | display: none !important;
81 | }
82 |
83 | @media only screen and (max-width: 768px) {
84 | .ui.fixed.menu, .ui.secondary.inverted.menu a.item {
85 | display: none !important;
86 | }
87 | a.item.logo img{
88 | width: 30% !important;
89 | }
90 | .secondary.menu .toc.item, .ui.secondary.inverted.menu .toc.item, .silder-icon {
91 | display: block !important;
92 | }
93 | .masthead {
94 | h2 {
95 | margin-top: 8px;
96 | }
97 | .ui.sanabil-color.button.inverted {
98 | font-size: 90%;
99 | margin-bottom: 10%;
100 | }
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/templates/admin/base_site.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/base.html" %}
2 | {% load admin_static %}
3 | {% block extrahead %}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {% block title %} Administration {% endblock %}
18 | {% endblock %}
19 |
20 | {# Additional CSS includes #}
21 | {#{% block extrastyle %}#}
22 | {# #}
23 | {#{% endblock %}#}
24 |
25 |
26 | {# Additional JS files in footer, right before
70 | {% include 'header.html' %}
71 |
72 |
73 |
74 | {% block content %}
75 | {% endblock %}
76 |
77 |
78 | {% include 'footer.html' %}
79 |
80 |
81 |
82 |
83 |
84 |
87 |
88 |
89 |
97 | #}
27 | {#{% block extrajs %}#}
28 | {# #}
29 | {#{% endblock %}#}
30 |
31 |
32 | {# Footer links (left side) #}
33 | {% block footer_links %}
34 | {# C #}
35 | {% endblock %}
36 |
37 | {# Additional header content like notifications or language switcher #}
38 | {#{% block header_content %}#}
39 | {# {{ block.super }}#}
40 | {#
#}
58 | {#{% endblock %}#}
59 |
60 |
61 |
62 | {# Footer copyright (right side) #}
63 | {% block copyright %}
64 | Copyright © 2018 Developed by Unchained
65 | {% endblock %}
66 |
--------------------------------------------------------------------------------
/base/static/css/scss/_vendor.scss:
--------------------------------------------------------------------------------
1 | .ui.table.dataTable {
2 | &_wrapper .dataTables {
3 | &_filter {
4 | text-align: right;
5 | color: $color_black_87;
6 | display: inline-flex;
7 | position: relative;
8 | input {
9 | margin-left: 8px;
10 | }
11 | }
12 | &_info {
13 | clear: both;
14 | padding-top: 12px;
15 | }
16 | }
17 | thead {
18 | th {
19 | cursor: pointer;
20 | white-space: nowrap;
21 | border-left: 1px solid $color_shark_15_approx;
22 | color: $color_black_87;
23 | &:first-child {
24 | border-left: none;
25 | }
26 | &:after {
27 | display: none;
28 | content: '';
29 | height: 1em;
30 | width: auto;
31 | opacity: 0.8;
32 | margin: 0 0 0 8px;
33 | font-family: $color_font_0;
34 | }
35 | &:hover {
36 | background: $color_black_5;
37 | color: $color_black_80;
38 | }
39 | &.sorting_asc:after {
40 | content: '\f160';
41 | }
42 | &.sorting_desc:after {
43 | content: '\f161';
44 | }
45 | &.sorting:after {
46 | content: '\f0dc';
47 | opacity: 0.2;
48 | }
49 | }
50 | .sorting {
51 | user-select: none;
52 | &:hover {
53 | user-select: none;
54 | }
55 | &:after {
56 | display: inline-block;
57 | }
58 | }
59 | .sorting_asc, .sorting_desc, .sorting_asc_disabled, .sorting_desc_disabled {
60 | @include user-select($color_black_95, $color_black_5);
61 | &:hover {
62 | @include user-select($color_black_95, $color_black_5);
63 | }
64 | &:after {
65 | display: inline-block;
66 | }
67 | }
68 |
69 | }
70 | th.disabled:hover {
71 | cursor: auto;
72 | color: $color_shark_30_approx;
73 | }
74 | }
75 |
76 | .dataTables_length select {
77 | background: $color_white none repeat scroll 0 0;
78 | border: 1px solid $color_shark_15_approx;
79 | border-radius: 4px;
80 | box-shadow: none;
81 | color: $color_black_87;
82 | cursor: pointer;
83 | display: inline-block;
84 | line-height: 19px;
85 | min-height: 11px;
86 | outline: 0 none;
87 | padding: 4px;
88 | transform: rotateZ(0deg);
89 | transition: box-shadow 0.1s ease 0s, width 0.1s ease 0s;
90 | white-space: normal;
91 | word-wrap: break-word;
92 | }
93 |
94 | .dataTables_filter input {
95 | background: $color_white none repeat scroll 0 0;
96 | border: 1px dotted $color_sanabil;
97 | border-radius: 8000px;
98 | box-shadow: none;
99 | color: $color_black_87;
100 | flex: 1 0 auto;
101 | height: 2em;
102 | margin: 0;
103 | max-width: 100%;
104 | outline: 0 none;
105 | padding: .4em;
106 | text-align: left;
107 | transition: background-color 0.1s ease 0s, box-shadow 0.1s ease 0s, border-color 0.1s ease 0s;
108 | }
109 |
110 | @media only screen and (max-width: 768px) {
111 | .dt-buttons {
112 | display: none!important;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/templates/staff/association_list.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block title %} Associations {% endblock %}
4 |
5 |
6 | {% block content %}
7 |
8 |
9 |
10 |
11 |
12 |
13 | Nom
14 | Surnom
15 | Téléphone
16 | Commune
17 | Adresse
18 | Réseaux sociaux
19 |
20 |
21 |
22 | {% for association in object_list %}
23 |
24 | {{ association.nom }}
25 |
26 | {{ association.surnom |default:"/" }}
27 |
28 | {{ association.telephone }}
29 | {% if association.telephone_2 != None %}
30 |
31 | {{ association.telephone_2 }}
32 | {% endif %}
33 |
34 | {{ association.commune }}
35 | {{ association.address }}
36 |
37 |
58 |
59 |
60 | {% empty %}
61 | Aucune association est ajoutée pour le moment
62 |
63 |
64 |
65 |
66 |
67 | {% endfor %}
68 |
69 |
70 |
71 |
72 | {% endblock %}
73 |
74 |
--------------------------------------------------------------------------------
/charity/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from import_export import resources
3 | from import_export.admin import ExportActionModelAdmin
4 | from import_export.formats import base_formats
5 | from charity.models import Famille, Necessiteux, Centre, Besoin, Donneur, AideRecu
6 |
7 |
8 | @admin.register(Famille)
9 | class FamilleAdmin(admin.ModelAdmin):
10 | list_display = ["nom", "nombre_enfant"]
11 | list_display_links = ["nom"]
12 | search_fields = ["nom"]
13 | list_filter = ["nombre_enfant"]
14 |
15 |
16 | class NecessiteuxResource(resources.ModelResource):
17 | class Meta:
18 | model = Necessiteux
19 |
20 |
21 | @admin.register(Necessiteux)
22 | class NecessiteuxAdmin(ExportActionModelAdmin):
23 | list_display = ["nom", "prenom", "tel", "association"]
24 | list_display_links = ["nom", "prenom"]
25 | search_fields = ["nom", "prenom", "tel"]
26 | list_filter = ['sexe', 'niveau_scolaire', 'pointure', 'taille', 'situation_familiale', 'situation_professionelle',
27 | 'est_orphelin', 'represent_famille', 'degré_nécessite', 'archivé', 'type']
28 | radio_fields = {'sexe': admin.HORIZONTAL, 'degré_nécessite': admin.HORIZONTAL}
29 | resource_class = NecessiteuxResource
30 |
31 | def get_export_formats(self):
32 | formats = (
33 | base_formats.CSV,
34 | base_formats.XLS,
35 | )
36 | return [f for f in formats if f().can_export()]
37 |
38 |
39 | @admin.register(Centre)
40 | class CentreAdmin(admin.ModelAdmin):
41 | list_display = ["nom", "address"]
42 | list_display_links = ["nom"]
43 | search_fields = ["nom", "address"]
44 | list_filter = ["type"]
45 |
46 |
47 | def satisfy_need(modeladmin, request, queryset):
48 | for need in queryset:
49 | need.est_satisfait = True
50 | need.save()
51 |
52 |
53 | satisfy_need.short_description = 'Satisfaire les besoins sélectionnés'
54 |
55 |
56 | class BesoinResource(resources.ModelResource):
57 | class Meta:
58 | model = Besoin
59 |
60 |
61 | @admin.register(Besoin)
62 | class BesoinAdmin(ExportActionModelAdmin):
63 | list_display = ["nom", "type", "est_urgent", "association"]
64 | date_hierarchy = "date_planification"
65 | search_fields = ["nom", "description"]
66 | list_filter = ["type", "est_urgent", "est_satisfait", "association", "type"]
67 | actions = [satisfy_need, ]
68 |
69 | def formfield_for_manytomany(self, db_field, request, **kwargs):
70 | if db_field.name == "necessiteux":
71 | kwargs["queryset"] = Necessiteux.objects.filter(archivé=False)
72 | return super().formfield_for_manytomany(db_field, request, **kwargs)
73 |
74 | resource_class = BesoinResource
75 |
76 | def get_export_formats(self):
77 | formats = (
78 | base_formats.CSV,
79 | base_formats.XLS,
80 | )
81 | return [f for f in formats if f().can_export()]
82 |
83 |
84 | @admin.register(Donneur)
85 | class DonneurAdmin(admin.ModelAdmin):
86 | list_display = ["nom", "type", "active", "tel"]
87 | list_display_links = ["nom"]
88 | search_fields = ["nom", "tel"]
89 | list_filter = ["type", "active", "anonyme"]
90 |
91 |
92 | @admin.register(AideRecu)
93 | class AideRecuAdmin(admin.ModelAdmin):
94 | list_display = ["__str__", "donneur", "association"]
95 | date_hierarchy = "date_reception"
96 | list_filter = ["type", "association"]
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/staff/models.py:
--------------------------------------------------------------------------------
1 | import six
2 | from django.contrib.auth.models import AbstractUser, User
3 | from django.contrib.auth.validators import UnicodeUsernameValidator, ASCIIUsernameValidator
4 | from django.db import models
5 |
6 | from base.models import Commune
7 | from staff.constants import PROFILE_TYPES
8 |
9 |
10 | class Membre(models.Model):
11 | user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True, editable=False)
12 | prénom = models.CharField(max_length=30)
13 | nom = models.CharField( max_length=50)
14 | email = models.EmailField()
15 | tel = models.CharField(max_length=50)
16 | profil = models.IntegerField(choices=PROFILE_TYPES, default=1)
17 | username_validator = UnicodeUsernameValidator() if six.PY3 else ASCIIUsernameValidator()
18 |
19 | username = models.CharField(
20 | 'username',
21 | null=False,
22 | blank=False,
23 | max_length=150,
24 | help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.',
25 | validators=[username_validator],
26 | error_messages={
27 | 'unique': "A user with that username already exists.",
28 | },
29 | )
30 | password = models.CharField(max_length=20, blank=True)
31 |
32 | def __str__(self):
33 | return "{}".format(self.username)
34 |
35 | class Meta:
36 | verbose_name_plural = "Membres"
37 | verbose_name = "Membre"
38 |
39 | def save(self, *args, **kwargs):
40 | if not self.id:
41 | username = self.username or "%s_%s" % (
42 | self.nom.lower().strip().replace(' ', '_'), self.prénom.lower().strip().replace(' ', '_'))
43 | password = self.password or User.objects.make_random_password(length=8,
44 | allowed_chars='abcdefghjkmnpqrstuvwxyz')
45 | new_user = User.objects.create_user(username=username,
46 | email=self.email,
47 | password=password)
48 | self.password = password
49 | self.user = new_user
50 |
51 | super(Membre, self).save() # force_update=force_update
52 |
53 |
54 | class Association(models.Model):
55 | responsable = models.ForeignKey(Membre, on_delete=models.SET_NULL, blank=True, null=True,
56 | limit_choices_to={"profil": 2})
57 | nom = models.CharField(max_length=500, unique=True)
58 | surnom = models.CharField(max_length=100, unique=True, blank=True)
59 | telephone = models.CharField(max_length=20, unique=True)
60 | telephone_2 = models.CharField(max_length=20, null=True, blank=True)
61 | address = models.TextField()
62 | commune = models.ForeignKey(Commune, on_delete=models.SET_NULL,null=True)
63 | website = models.CharField(max_length=500, null=True, blank=True)
64 | facebook = models.CharField(max_length=500, null=True, blank=True)
65 | youtube = models.CharField(max_length=500, null=True, blank=True)
66 |
67 | def __str__(self):
68 | return "{}.{}".format(self.id, self.nom)
69 |
70 |
71 | class Login(models.Model):
72 | created_at = models.DateTimeField(auto_now_add=True)
73 | updated_at = models.DateTimeField(auto_now=True)
74 | membre = models.ForeignKey(Membre, on_delete=models.CASCADE)
75 | ip = models.GenericIPAddressField()
76 |
77 |
78 | def __str__(self):
79 | return "{} {} {}".format(self.membre, self.ip, self.created_at)
80 |
81 |
82 |
--------------------------------------------------------------------------------
/staff/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.5 on 2018-11-08 18:01
2 |
3 | from django.conf import settings
4 | import django.contrib.auth.validators
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15 | ('base', '0001_initial'),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Association',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('nom', models.CharField(max_length=500, unique=True)),
24 | ('surnom', models.CharField(blank=True, max_length=100, unique=True)),
25 | ('telephone', models.CharField(max_length=20, unique=True)),
26 | ('telephone_2', models.CharField(blank=True, max_length=20, null=True)),
27 | ('address', models.TextField()),
28 | ('website', models.CharField(blank=True, max_length=500, null=True)),
29 | ('facebook', models.CharField(blank=True, max_length=500, null=True)),
30 | ('youtube', models.CharField(blank=True, max_length=500, null=True)),
31 | ('commune', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='base.Commune')),
32 | ],
33 | ),
34 | migrations.CreateModel(
35 | name='Login',
36 | fields=[
37 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
38 | ('created_at', models.DateTimeField(auto_now_add=True)),
39 | ('updated_at', models.DateTimeField(auto_now=True)),
40 | ('ip', models.GenericIPAddressField()),
41 | ],
42 | ),
43 | migrations.CreateModel(
44 | name='Membre',
45 | fields=[
46 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
47 | ('prénom', models.CharField(max_length=30)),
48 | ('nom', models.CharField(max_length=50)),
49 | ('email', models.EmailField(max_length=254)),
50 | ('tel', models.CharField(max_length=50)),
51 | ('profil', models.IntegerField(choices=[(1, 'Sanabil Admin'), (2, 'Association Responsible')], default=1)),
52 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
53 | ('password', models.CharField(blank=True, max_length=20)),
54 | ('user', models.OneToOneField(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
55 | ],
56 | options={
57 | 'verbose_name': 'Membre',
58 | 'verbose_name_plural': 'Membres',
59 | },
60 | ),
61 | migrations.AddField(
62 | model_name='login',
63 | name='membre',
64 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='staff.Membre'),
65 | ),
66 | migrations.AddField(
67 | model_name='association',
68 | name='responsable',
69 | field=models.ForeignKey(blank=True, limit_choices_to={'profil': 2}, null=True, on_delete=django.db.models.deletion.SET_NULL, to='staff.Membre'),
70 | ),
71 | ]
72 |
--------------------------------------------------------------------------------
/base/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.5 on 2018-11-08 18:01
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='CentreType',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('label', models.CharField(max_length=100, unique=True)),
20 | ],
21 | ),
22 | migrations.CreateModel(
23 | name='Commune',
24 | fields=[
25 | ('id', models.IntegerField(primary_key=True, serialize=False)),
26 | ('name', models.CharField(max_length=50)),
27 | ('postcode', models.IntegerField()),
28 | ],
29 | ),
30 | migrations.CreateModel(
31 | name='DonneurType',
32 | fields=[
33 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
34 | ('label', models.CharField(max_length=100, unique=True)),
35 | ],
36 | ),
37 | migrations.CreateModel(
38 | name='DonType',
39 | fields=[
40 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
41 | ('label', models.CharField(max_length=100, unique=True)),
42 | ],
43 | ),
44 | migrations.CreateModel(
45 | name='NiveauScolaire',
46 | fields=[
47 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
48 | ('label', models.CharField(max_length=100)),
49 | ('order', models.IntegerField(unique=True)),
50 | ],
51 | ),
52 | migrations.CreateModel(
53 | name='Parameter',
54 | fields=[
55 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
56 | ('name', models.CharField(max_length=40, unique=True)),
57 | ('value', models.CharField(max_length=100)),
58 | ('type', models.CharField(choices=[('int', 'Integer'), ('bool', 'Boolean')], max_length=10)),
59 | ('created_at', models.DateTimeField(auto_now_add=True)),
60 | ('updated_at', models.DateTimeField(auto_now=True)),
61 | ],
62 | ),
63 | migrations.CreateModel(
64 | name='SituationFamiliale',
65 | fields=[
66 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
67 | ('label', models.CharField(max_length=100)),
68 | ],
69 | ),
70 | migrations.CreateModel(
71 | name='SituationProfessionelle',
72 | fields=[
73 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
74 | ('label', models.CharField(max_length=100)),
75 | ],
76 | ),
77 | migrations.CreateModel(
78 | name='Wilaya',
79 | fields=[
80 | ('id', models.IntegerField(primary_key=True, serialize=False)),
81 | ('name', models.CharField(max_length=30)),
82 | ('arabic_name', models.CharField(blank='', max_length=30)),
83 | ],
84 | ),
85 | migrations.AddField(
86 | model_name='commune',
87 | name='wilaya',
88 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Wilaya'),
89 | ),
90 | ]
91 |
--------------------------------------------------------------------------------
/charity/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.views.generic import ListView
3 |
4 | from charity.models import Famille, Necessiteux, AideRecu, Besoin
5 | from staff.models import Association
6 |
7 | from rest_framework.views import APIView
8 | from rest_framework.response import Response
9 |
10 | from django.db.models import Sum, Count, Q
11 |
12 | from datetime import date
13 | from django.utils.timezone import now
14 |
15 |
16 | def homepage(request):
17 | return render(request,
18 | 'home.html',
19 | {"stat": {
20 | 'familles': Famille.objects.count(),
21 | 'necessiteux': Necessiteux.objects.count(),
22 | 'aides': AideRecu.objects.count(),
23 | 'association': Association.objects.count()
24 | }}
25 | )
26 |
27 |
28 | def helppage(request):
29 | return render(request,
30 | 'help.html',
31 | {}
32 | )
33 |
34 |
35 | class NecessiteuxListView(ListView):
36 | model = Necessiteux
37 | ordering = ['-id']
38 | # paginate_by = 10
39 |
40 |
41 | def age_range(min_age, max_age):
42 | current = now().date()
43 | min_date = date(current.year - min_age, current.month, current.day)
44 | max_date = date(current.year - max_age, current.month, current.day)
45 | return Count('date_de_naissance', filter=Q(date_de_naissance__gte=max_date) & Q(date_de_naissance__lte=min_date))
46 |
47 |
48 | class NecessiteuxData(APIView):
49 | authentication_classes = []
50 | permission_classes = []
51 |
52 | def get(self, request, format=None):
53 | qs = Necessiteux.objects
54 | qsFamille = Famille.objects.all()
55 | gender_counts = [0, 0]
56 | if qs.count() > 0:
57 | gender_counts[0] = round(qs.filter(sexe='F').count() * 100 / (qs.count()), 2)
58 | gender_counts[1] = 100 - gender_counts[0]
59 | gander_data = {
60 | "labels": ["Femmes", "Hommes"],
61 | "data": gender_counts,
62 | }
63 | enfants = age_range(0, 15)
64 | ados = age_range(15, 25)
65 | adults = age_range(25, 65)
66 | vieux = age_range(65, 150)
67 | age_counts = [0, 0, 0, 0]
68 | enfants_count = 0
69 | labels = ["Enfants", "Adolescents", "Adultes", "Vieux"]
70 | if qs.count() > 0:
71 | age_qs = qs.values('date_de_naissance').annotate(enfants=enfants, ados=ados, adults=adults, vieux=vieux)
72 | age_sums = age_qs.aggregate(enfants_sum=Sum('enfants'), ados_sum=Sum('ados'), adults_sum=Sum('adults'), vieux_sum=Sum('vieux'))
73 | age_counts = [age_sums['enfants_sum'], age_sums['ados_sum'], age_sums['adults_sum'], age_sums['vieux_sum']]
74 | enfants_count = age_counts[0]
75 | unknown_age_count = qs.filter(date_de_naissance__isnull=True).count()
76 | if unknown_age_count > 0:
77 | age_counts.append(unknown_age_count)
78 | labels.append('Âge inconnu')
79 | age_data = {
80 | "labels": labels,
81 | "data": age_counts,
82 | }
83 | numbers = {'totale': qs.count(), 'familles': qsFamille.count(),
84 | 'enfants': enfants_count}
85 | data = {'gander_data': gander_data, 'age_data': age_data, 'numbers': numbers}
86 | return Response(data)
87 |
88 |
89 | class AideRecuListView(ListView):
90 | model = AideRecu
91 | #paginate_by = 10
92 |
93 |
94 | class BesoinListView(ListView):
95 | model = Besoin
96 | ordering = ['-nom']
97 |
98 | def get_satisfied_needs(self):
99 | return self.model.objects.get(est_satisfait=True)
100 |
101 | #paginate_by = 10
102 |
--------------------------------------------------------------------------------
/Sanabil/settings.py:
--------------------------------------------------------------------------------
1 | import os
2 | import dj_database_url
3 |
4 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5 |
6 |
7 |
8 | DEBUG = bool(os.environ.get('DEBUG', 0))
9 |
10 | ALLOWED_HOSTS = ['.sanabil.org']
11 |
12 | ADMINS = (('Assem Chelli', 'assem.ch@gmail.com'), )
13 |
14 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
15 |
16 | EMAIL_HOST = 'smtp.sendgrid.net'
17 | EMAIL_HOST_USER = os.environ.get('SENDGRID_USERNAME', False)
18 | EMAIL_HOST_PASSWORD = os.environ.get('SENDGRID_PASSWORD', False)
19 | EMAIL_PORT = 587
20 | EMAIL_USE_TLS = True
21 |
22 | SECRET_KEY = os.environ.get('SECRET_KEY','1tx6ee0l)64l68tl*6akdesms%uz8noqcrarl-2s1_z*^bgw(r')
23 |
24 | if DEBUG:
25 | ALLOWED_HOSTS += ['localhost','127.0.0.1','sanabil.herokuapp.com']
26 |
27 |
28 |
29 | INSTALLED_APPS = [
30 | 'jet',
31 | 'django.contrib.admin',
32 | 'django.contrib.auth',
33 | 'django.contrib.contenttypes',
34 | 'django.contrib.sessions',
35 | 'django.contrib.messages',
36 | 'rest_framework',
37 | 'sass_processor',
38 | 'django.contrib.staticfiles',
39 | 'staff',
40 | 'charity',
41 | 'base'
42 | ]
43 |
44 |
45 |
46 | MIDDLEWARE = [
47 | 'django.middleware.security.SecurityMiddleware',
48 | 'django.contrib.sessions.middleware.SessionMiddleware',
49 | 'django.middleware.common.CommonMiddleware',
50 | 'django.middleware.csrf.CsrfViewMiddleware',
51 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
52 | 'django.contrib.messages.middleware.MessageMiddleware',
53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
54 | 'whitenoise.middleware.WhiteNoiseMiddleware',
55 |
56 | ]
57 |
58 |
59 | if DEBUG:
60 | INSTALLED_APPS += ['debug_toolbar']
61 | MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
62 |
63 | ROOT_URLCONF = 'Sanabil.urls'
64 |
65 | TEMPLATES = [
66 | {
67 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
68 | 'DIRS': [os.path.join(BASE_DIR, 'templates')],
69 | 'APP_DIRS': True,
70 | 'OPTIONS': {
71 | 'context_processors': [
72 | 'django.template.context_processors.debug',
73 | 'django.template.context_processors.request',
74 | 'django.contrib.auth.context_processors.auth',
75 | 'django.contrib.messages.context_processors.messages',
76 | ],
77 | },
78 | },
79 | ]
80 |
81 | WSGI_APPLICATION = 'Sanabil.wsgi.application'
82 |
83 |
84 |
85 |
86 | DATABASES = {
87 | 'default': dj_database_url.config(default="sqlite:///db.sqlite3")
88 | }
89 |
90 | # Password validation
91 | # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
92 |
93 | AUTH_PASSWORD_VALIDATORS = [
94 | {
95 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
96 | },
97 | {
98 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
99 | },
100 | {
101 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
102 | },
103 | {
104 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
105 | },
106 | ]
107 |
108 |
109 | # Internationalization
110 | # https://docs.djangoproject.com/en/1.9/topics/i18n/
111 |
112 | LANGUAGE_CODE = 'fr-FR'
113 |
114 | TIME_ZONE = 'UTC'
115 |
116 | USE_I18N = True
117 |
118 | USE_L10N = True
119 |
120 | USE_TZ = True
121 |
122 | MEDIA_ROOT = os.path.join(BASE_DIR, "media/")
123 |
124 |
125 | # Static files (CSS, JavaScript, Images)
126 | # https://docs.djangoproject.com/en/1.9/howto/static-files/
127 | STATIC_ROOT = os.path.join(BASE_DIR, "static_files")
128 | STATIC_URL = '/static/'
129 | # STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
130 | SASS_PROCESSOR_ENABLED = True
131 | SASS_PROCESSOR_INCLUDE_FILE_PATTERN = r'^.+\.scss$'
132 | SASS_PROCESSOR_INCLUDE_DIRS = [
133 | os.path.join(BASE_DIR, 'base/static/css/scss'),
134 | ]
135 |
136 | STATICFILES_FINDERS = [
137 | 'django.contrib.staticfiles.finders.FileSystemFinder',
138 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
139 | 'sass_processor.finders.CssFinder',
140 | ]
141 |
142 | def show_toolbar(request):
143 | return False
144 |
145 |
146 | if DEBUG:
147 | DEBUG_TOOLBAR_CONFIG = {
148 | "SHOW_TOOLBAR_CALLBACK" : show_toolbar,
149 | }
150 |
151 |
152 | #JET THEME CONFIG
153 | JET_SIDE_MENU_COMPACT = True
154 | SECURE_SSL_REDIRECT = not DEBUG
--------------------------------------------------------------------------------
/base/fixtures/wilayas.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | { "model": "base.wilaya", "pk": 1,"fields": { "name": "Adrar", "arabic_name":""}},
4 | { "model": "base.wilaya", "pk": 2,"fields": { "name": "Chlef", "arabic_name":""}},
5 | { "model": "base.wilaya", "pk": 3,"fields": { "name": "Laghouat", "arabic_name":""}},
6 | { "model": "base.wilaya", "pk": 4,"fields": { "name": "Oum El Bouaghi", "arabic_name":""}},
7 | { "model": "base.wilaya", "pk": 5,"fields": { "name": "Batna", "arabic_name":""}},
8 | { "model": "base.wilaya", "pk": 6,"fields": { "name": "Béjaïa", "arabic_name":""}},
9 | { "model": "base.wilaya", "pk": 7,"fields": { "name": "Biskra", "arabic_name":""}},
10 | { "model": "base.wilaya", "pk": 8,"fields": { "name": "Béchar", "arabic_name":""}},
11 | { "model": "base.wilaya", "pk": 9,"fields": { "name": "Blida", "arabic_name":""}},
12 | { "model": "base.wilaya", "pk": 10,"fields": { "name": "Bouira", "arabic_name":""}},
13 | { "model": "base.wilaya", "pk": 11,"fields": { "name": "Tamanrasset", "arabic_name":""}},
14 | { "model": "base.wilaya", "pk": 12,"fields": { "name": "Tébessa", "arabic_name":""}},
15 | { "model": "base.wilaya", "pk": 13,"fields": { "name": "Tlemcen", "arabic_name":""}},
16 | { "model": "base.wilaya", "pk": 14,"fields": { "name": "Tiaret", "arabic_name":""}},
17 | { "model": "base.wilaya", "pk": 15,"fields": { "name": "Tizi Ouzou", "arabic_name":""}},
18 | { "model": "base.wilaya", "pk": 16,"fields": { "name": "Alger", "arabic_name":""}},
19 | { "model": "base.wilaya", "pk": 17,"fields": { "name": "Djelfa", "arabic_name":""}},
20 | { "model": "base.wilaya", "pk": 18,"fields": { "name": "Jijel", "arabic_name":""}},
21 | { "model": "base.wilaya", "pk": 19,"fields": { "name": "Sétif", "arabic_name":""}},
22 | { "model": "base.wilaya", "pk": 20,"fields": { "name": "Saïda", "arabic_name":""}},
23 | { "model": "base.wilaya", "pk": 21,"fields": { "name": "Skikda", "arabic_name":""}},
24 | { "model": "base.wilaya", "pk": 22,"fields": { "name": "Sidi Bel Abbès", "arabic_name":""}},
25 | { "model": "base.wilaya", "pk": 23,"fields": { "name": "Annaba", "arabic_name":""}},
26 | { "model": "base.wilaya", "pk": 24,"fields": { "name": "Guelma", "arabic_name":""}},
27 | { "model": "base.wilaya", "pk": 25,"fields": { "name": "Constantine", "arabic_name":""}},
28 | { "model": "base.wilaya", "pk": 26,"fields": { "name": "Médéa", "arabic_name":""}},
29 | { "model": "base.wilaya", "pk": 27,"fields": { "name": "Mostaganem", "arabic_name":""}},
30 | { "model": "base.wilaya", "pk": 28,"fields": { "name": "M'Sila", "arabic_name":""}},
31 | { "model": "base.wilaya", "pk": 29,"fields": { "name": "Mascara", "arabic_name":""}},
32 | { "model": "base.wilaya", "pk": 30,"fields": { "name": "Ouargla", "arabic_name":""}},
33 | { "model": "base.wilaya", "pk": 31,"fields": { "name": "Oran", "arabic_name":""}},
34 | { "model": "base.wilaya", "pk": 32,"fields": { "name": "El Bayadh", "arabic_name":""}},
35 | { "model": "base.wilaya", "pk": 33,"fields": { "name": "Illizi", "arabic_name":""}},
36 | { "model": "base.wilaya", "pk": 34,"fields": { "name": "Bordj Bou Arreridj", "arabic_name":""}},
37 | { "model": "base.wilaya", "pk": 35,"fields": { "name": "Boumerdès", "arabic_name":""}},
38 | { "model": "base.wilaya", "pk": 36,"fields": { "name": "El Tarf", "arabic_name":""}},
39 | { "model": "base.wilaya", "pk": 37,"fields": { "name": "Tindouf", "arabic_name":""}},
40 | { "model": "base.wilaya", "pk": 38,"fields": { "name": "Tissemsilt", "arabic_name":""}},
41 | { "model": "base.wilaya", "pk": 39,"fields": { "name": "El Oued", "arabic_name":""}},
42 | { "model": "base.wilaya", "pk": 40,"fields": { "name": "Khenchela", "arabic_name":""}},
43 | { "model": "base.wilaya", "pk": 41,"fields": { "name": "Souk Ahras", "arabic_name":""}},
44 | { "model": "base.wilaya", "pk": 42,"fields": { "name": "Tipaza", "arabic_name":""}},
45 | { "model": "base.wilaya", "pk": 43,"fields": { "name": "Mila", "arabic_name":""}},
46 | { "model": "base.wilaya", "pk": 44,"fields": { "name": "Aïn Defla", "arabic_name":""}},
47 | { "model": "base.wilaya", "pk": 45,"fields": { "name": "Naâma", "arabic_name":""}},
48 | { "model": "base.wilaya", "pk": 46,"fields": { "name": "Aïn Témouchent", "arabic_name":""}},
49 | { "model": "base.wilaya", "pk": 47,"fields": { "name": "Ghardaïa", "arabic_name":""}},
50 | { "model": "base.wilaya", "pk": 48,"fields": { "name": "Relizane", "arabic_name":""}}
51 | ]
--------------------------------------------------------------------------------
/templates/charity/besoin_list.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block title %} Besoins {% endblock %}
4 |
5 |
6 | {% block content %}
7 |
8 |
9 |
10 |
11 |
12 |
13 | Nom
14 | Type de don
15 | Association
16 | Montant
17 | Urgent
18 | Date limite
19 | Autres détails
20 |
21 |
22 |
23 | {% for besoin in object_list %}
24 |
25 |
26 | {{ besoin.nom }}
27 |
28 |
29 | {{ besoin.type.label | default:"Autres" }}
30 |
31 |
32 | {{ besoin.association.nom |default:"/" }}
33 |
34 |
35 |
36 | {{ besoin.montant }} DA
37 |
38 |
39 |
40 |
42 |
43 |
44 |
45 | {{ besoin.date_limite |default:"/" }}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 | {% if besoin.description|length > 0 %}
65 |
66 |
67 |
68 |
69 | {{ besoin.description }}
70 |
71 |
72 |
73 | {% endif %}
74 |
75 |
76 |
77 |
78 |
79 | Nom: {{ besoin.association.nom | default:"/" }}
80 |
81 |
82 | Commune: {{ besoin.association.commune | default:"/" }}
83 |
84 |
85 | Adresse: {{ besoin.association.address | default:"/" }}
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
{{ besoin.association.address | default:"/" }}
95 |
96 |
97 |
98 |
99 |
100 | Fermer
101 |
102 |
103 |
104 | {% empty %}
105 | Aucun besoin est ajouté pour le moment
106 |
107 |
108 |
109 |
110 |
111 |
112 | {% endfor %}
113 |
114 |
115 |
116 |
117 | {% endblock %}
118 |
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 | {% load sass_tags %}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
{% block title %} title {% endblock %}
26 |
27 |
28 |
29 |
30 |
68 |
69 |
98 |