├── 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 |
7 |
8 | {% if messages %} 9 | {% for message in messages %} 10 |
11 |
12 | 13 |

{{ message }}

14 |
15 |
16 | {% endfor %} 17 | {% endif %} 18 | 19 |
20 | {% csrf_token %} 21 | {{ form.as_p }} 22 | 23 |
24 |
25 | 26 |
27 |
28 | dz.sanabil@gmail.com 29 |
30 |
31 | +213 540 286 003 32 |
33 |
34 |
35 | 37 |
38 |
39 |
40 |
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 |

4 | ISTIQUES 5 |

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 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% for needy in object_list %} 22 | 23 | 26 | 33 | 40 | 47 | 58 | 61 | 62 | {% empty %} 63 | 64 | 65 | 66 | 67 | 68 | 69 | {% endfor %} 70 | 71 |
CodeSexeAgeTailleBesoinsAssociation à laquelle il appartient
24 | {{ needy.id }} 25 | 27 | {% if needy.sexe == 'F' %} 28 | Féminin 29 | {% else %} 30 | Masculin 31 | {% endif %} 32 | 34 | {% if needy.get_age == None %} 35 | Âge inconnu 36 | {% else %} 37 | {{ needy.get_age }} 38 | {% endif %} 39 | 41 | {% if needy.taille == None %} 42 | Taille inconnue 43 | {% else %} 44 | {{ needy.taille }} 45 | {% endif %} 46 | 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 | 59 | {{ needy.association.nom }} 60 |
Aucun nécessiteux est ajouté pour le moment
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 |

7 | SANABIL 8 |

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 |

28 | SOMMES-NOUS ? 29 |

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 |

35 | SERT CETTE PLATEFORME ? 36 |

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 #} 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 | {#
#} 41 | {# #} 42 | {#
#} 43 | {#
#} 44 | {# #} 45 | {#
#} 46 | {#
#} 47 | {# Front-end
#} 48 | {# One more link#} 49 | {#
#} 50 | {# #} 51 | {#
#} 52 | {# #} 53 | {#
#} 54 | {#
#} 55 | {# 5 new messages#} 56 | {#
#} 57 | {#
#} 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 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% for association in object_list %} 23 | 24 | 25 | 28 | 34 | 35 | 36 | 59 | 60 | {% empty %} 61 | 62 | 63 | 64 | 65 | 66 | 67 | {% endfor %} 68 | 69 |
NomSurnomTéléphoneCommuneAdresseRéseaux sociaux
{{ association.nom }} 26 | {{ association.surnom |default:"/" }} 27 | {{ association.telephone }} 29 | {% if association.telephone_2 != None %} 30 |
31 | {{ association.telephone_2 }} 32 | {% endif %} 33 |
{{ association.commune }}{{ association.address }} 37 |
38 | 41 | 42 | 43 | 48 | 51 | 52 | 53 | {{ association.address }} 56 | 57 |
58 |
Aucune association est ajoutée pour le moment
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 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {% for besoin in object_list %} 24 | 25 | 28 | 31 | 34 | 39 | 44 | 47 | 53 | 54 | 104 | {% empty %} 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | {% endfor %} 113 | 114 |
NomType de donAssociationMontantUrgentDate limiteAutres détails
26 | {{ besoin.nom }} 27 | 29 | {{ besoin.type.label | default:"Autres" }} 30 | 32 | {{ besoin.association.nom |default:"/" }} 33 | 35 |
36 | {{ besoin.montant }} DA 37 |
38 |
40 | 42 | 43 | 45 | {{ besoin.date_limite |default:"/" }} 46 | 48 | 49 | 50 | 51 | 52 |
Aucun besoin est ajouté pour le moment
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 | 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 | 98 | 99 | -------------------------------------------------------------------------------- /charity/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator 3 | from datetime import date 4 | from base.models import Commune, NiveauScolaire, SituationFamiliale, SituationProfessionelle, CentreType, \ 5 | DonType, DonneurType 6 | from staff.models import Membre, Association 7 | 8 | 9 | class Famille(models.Model): 10 | nom = models.CharField(max_length=500) 11 | nombre_enfant = models.SmallIntegerField(default=0, blank=True, null=True) 12 | archivé = models.BooleanField(default=False) 13 | 14 | def __str__(self): 15 | return "{}".format(self.nom) 16 | 17 | 18 | class Centre(models.Model): 19 | nom = models.CharField(max_length=500) 20 | address = models.TextField() 21 | type = models.ForeignKey(CentreType, on_delete=models.CASCADE) 22 | 23 | def __str__(self): 24 | return "{} ({})".format(self.nom, self.type) 25 | 26 | 27 | class Necessiteux(models.Model): 28 | association = models.ForeignKey(Association, null=True, on_delete=models.SET_NULL, related_name="members") 29 | nom = models.CharField(max_length=500) 30 | prenom = models.CharField('prénom', max_length=100) 31 | jeune_fille = models.CharField('Nom de jeune fille', max_length=100, blank=True, null=True) 32 | part_de = models.CharField('De la part de', max_length=100, blank=True, null=True) 33 | nin = models.CharField('Numéro pièce d’identité', max_length=9, blank=True, null=True) 34 | date_de_naissance = models.DateField(blank=True, null=True) 35 | sexe = models.CharField(max_length=1, choices=[("F", "Female"), ("M", "Male")]) 36 | type = models.SmallIntegerField(default=0, 37 | choices=[(0, 'Permanent'), (1, 'Temporaire'), (2, 'Voyageur en détresse')]) 38 | niveau_scolaire = models.ForeignKey(NiveauScolaire, on_delete=models.SET_NULL, blank=True, null=True) 39 | tel = models.CharField('Téléphone 1', max_length=20, blank=True, null=True) 40 | tel_2 = models.CharField('Téléphone 2', max_length=20, blank=True, null=True) 41 | pointure = models.IntegerField(null=True) # TODO validation 20 - 50 42 | taille = models.CharField(max_length=100, null=True) 43 | pointure = models.IntegerField(blank=True, null=True, default=36, 44 | validators=[ 45 | MaxValueValidator(50), 46 | MinValueValidator(20) 47 | ]) 48 | taille = models.CharField(max_length=100, blank=True, null=True) 49 | situation_familiale = models.ForeignKey(SituationFamiliale, on_delete=models.SET_NULL, null=True) 50 | situation_professionelle = models.ForeignKey(SituationProfessionelle, on_delete=models.SET_NULL, null=True) 51 | health_state = models.SmallIntegerField('état de santé', default=0, 52 | choices=[(0, 'En bonne santé'), (1, 'Malade'), (2, 'Maladie chronique')]) 53 | est_orphelin = models.BooleanField() 54 | degré_nécessite = models.SmallIntegerField(default=0, 55 | choices=[(0, '0'), (1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5')]) 56 | appartient_famille = models.ForeignKey(Famille, verbose_name='appartient-il à une famille?', on_delete=models.SET_NULL, blank=True, null=True) 57 | represent_famille = models.BooleanField('représente-il une famille?', default=False) 58 | archivé = models.BooleanField(default=False) 59 | investigated = models.BooleanField('investigation effectuée?', default=False) 60 | address = models.TextField('adresse postale', blank=True, null=True) 61 | other_infos = models.TextField('informations supplémentaires', blank=True, null=True) 62 | 63 | def get_need(self): 64 | needs_qs = Besoin.objects.filter(necessiteux=self.id).values_list('nom', flat=True) 65 | return list(needs_qs) 66 | 67 | def get_age(self): 68 | today = date.today() 69 | date_naissance = self.date_de_naissance 70 | if date_naissance is None: 71 | return None 72 | return today.year - date_naissance.year - ( 73 | (today.month, today.day) < (date_naissance.month, date_naissance.day)) 74 | 75 | class Meta: 76 | verbose_name_plural = "Nécessiteux" 77 | verbose_name = "Nécessiteux" 78 | 79 | def __str__(self): 80 | return "{} {}".format(self.nom, self.prenom) 81 | 82 | 83 | class Besoin(models.Model): 84 | association = models.ForeignKey(Association, on_delete=models.CASCADE) 85 | necessiteux = models.ManyToManyField(Necessiteux, blank=True) 86 | centre = models.ForeignKey(Centre, on_delete=models.CASCADE, null=True, blank=True) 87 | nom = models.CharField(max_length=100) 88 | description = models.TextField(blank=True, null=True) 89 | type = models.ForeignKey(DonType, on_delete=models.CASCADE) 90 | est_urgent = models.BooleanField(default=False) 91 | date_limite = models.DateField(blank=True, null=True) 92 | expire_le = models.DateField(blank=True, null=True) 93 | montant = models.IntegerField(default=0, null=True, blank=True) 94 | date_planification = models.DateField(blank=True, null=True) 95 | date_remise_don = models.DateField(blank=True, null=True) 96 | est_satisfait = models.BooleanField(default=False) 97 | délivré_par = models.CharField(max_length=100, blank=True, null=True) 98 | 99 | def __str__(self): 100 | return "{}. {}".format(self.id, self.nom) 101 | 102 | 103 | class Donneur(models.Model): 104 | association = models.ForeignKey(Association, on_delete=models.SET_NULL, null=True) 105 | nom = models.CharField(max_length=100) 106 | tel = models.CharField(max_length=20, null=True, blank=True) 107 | type = models.ForeignKey(DonneurType, on_delete=models.SET_NULL, null=True) 108 | active = models.BooleanField(default=False) 109 | anonyme = models.BooleanField(default=True) 110 | 111 | def __str__(self): 112 | return "{} ({})".format(self.nom, self.type) 113 | 114 | 115 | class AideRecu(models.Model): 116 | association = models.ForeignKey(Association, on_delete=models.CASCADE) 117 | donneur = models.ForeignKey(Donneur, on_delete=models.SET_NULL, null=True) 118 | type = models.ForeignKey(DonType, on_delete=models.SET_NULL, null=True) 119 | date_reception = models.DateField() 120 | notes = models.TextField(blank=True, null=True) 121 | 122 | class Meta: 123 | verbose_name = "Aide reçu" 124 | verbose_name_plural = "Aides reçus" 125 | 126 | def __str__(self): 127 | return "Don #{} ({})".format(self.id, self.type) 128 | -------------------------------------------------------------------------------- /charity/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 | ('staff', '0001_initial'), 13 | ('base', '0001_initial'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='AideRecu', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('date_reception', models.DateField()), 22 | ('notes', models.TextField(blank=True, null=True)), 23 | ('association', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='staff.Association')), 24 | ], 25 | options={ 26 | 'verbose_name': 'Aide reçu', 27 | 'verbose_name_plural': 'Aides reçus', 28 | }, 29 | ), 30 | migrations.CreateModel( 31 | name='Besoin', 32 | fields=[ 33 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 34 | ('nom', models.CharField(max_length=100)), 35 | ('description', models.TextField(blank=True, null=True)), 36 | ('est_urgent', models.BooleanField(default=False)), 37 | ('date_limite', models.DateField(blank=True, null=True)), 38 | ('montant', models.IntegerField(blank=True, default=0, null=True)), 39 | ('date_planification', models.DateField(blank=True, null=True)), 40 | ('date_remise_don', models.DateField(blank=True, null=True)), 41 | ('est_satisfait', models.BooleanField(default=False)), 42 | ('association', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='staff.Association')), 43 | ], 44 | ), 45 | migrations.CreateModel( 46 | name='Centre', 47 | fields=[ 48 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 49 | ('nom', models.CharField(max_length=500)), 50 | ('address', models.TextField()), 51 | ('type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.CentreType')), 52 | ], 53 | ), 54 | migrations.CreateModel( 55 | name='Donneur', 56 | fields=[ 57 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 58 | ('nom', models.CharField(max_length=100)), 59 | ('tel', models.CharField(blank=True, max_length=20, null=True)), 60 | ('active', models.BooleanField(default=False)), 61 | ('anonyme', models.BooleanField(default=True)), 62 | ('association', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='staff.Association')), 63 | ('type', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='base.DonneurType')), 64 | ], 65 | ), 66 | migrations.CreateModel( 67 | name='Famille', 68 | fields=[ 69 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 70 | ('nom', models.CharField(max_length=500)), 71 | ('nombre_enfant', models.SmallIntegerField(blank=True, default=0, null=True)), 72 | ('archivé', models.BooleanField(default=False)), 73 | ], 74 | ), 75 | migrations.CreateModel( 76 | name='Necessiteux', 77 | fields=[ 78 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 79 | ('nom', models.CharField(max_length=500)), 80 | ('prenom', models.CharField(max_length=100)), 81 | ('date_de_naissance', models.DateField(null=True)), 82 | ('sexe', models.CharField(choices=[('F', 'Female'), ('M', 'Male')], max_length=1)), 83 | ('tel', models.CharField(blank=True, max_length=20, null=True)), 84 | ('pointure', models.IntegerField(null=True)), 85 | ('taille', models.CharField(max_length=100, null=True)), 86 | ('est_orphelin', models.BooleanField()), 87 | ('degré_nécessite', models.SmallIntegerField(choices=[(0, '0'), (1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5')], default=0)), 88 | ('represent_famille', models.BooleanField(default=False)), 89 | ('archivé', models.BooleanField(default=False)), 90 | ('appartient_famille', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='charity.Famille')), 91 | ('association', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='staff.Association')), 92 | ('niveau_scolaire', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='base.NiveauScolaire')), 93 | ('situation_familiale', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='base.SituationFamiliale')), 94 | ('situation_professionelle', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='base.SituationProfessionelle')), 95 | ], 96 | options={ 97 | 'verbose_name': 'Nécessiteux', 98 | 'verbose_name_plural': 'Nécessiteux', 99 | }, 100 | ), 101 | migrations.AddField( 102 | model_name='besoin', 103 | name='centre', 104 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='charity.Centre'), 105 | ), 106 | migrations.AddField( 107 | model_name='besoin', 108 | name='necessiteux', 109 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='charity.Necessiteux'), 110 | ), 111 | migrations.AddField( 112 | model_name='besoin', 113 | name='type', 114 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.DonType'), 115 | ), 116 | migrations.AddField( 117 | model_name='aiderecu', 118 | name='donneur', 119 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='charity.Donneur'), 120 | ), 121 | migrations.AddField( 122 | model_name='aiderecu', 123 | name='type', 124 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='base.DonType'), 125 | ), 126 | ] 127 | -------------------------------------------------------------------------------- /templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} Sanabil - سنابل {% endblock %} 4 | 5 | {% block content %} 6 |
7 | 32 |
33 |

34 | [ 35 | مَثَلُ الَّذِينَ يُنْفِقُونَ أَمْوَالَهُمْ فِي سَبِيلِ اللَّهِ كَمَثَلِ حَبَّةٍ أَنْبَتَتْ سَبْعَ 36 | سَنَابِلَ فِي كُلِّ سُنْبُلَةٍ مِائَةُ حَبَّةٍ وَاللَّهُ يُضَاعِفُ لِمَنْ يَشَاءُ وَاللَّهُ وَاسِعٌ 37 | عَلِيمٌ 38 | ] 39 | 40 | الآية 261 من سورة البقرة 41 | 42 |

43 | 44 | 45 | Contactez-nous 46 | 47 |
48 |
49 | 50 |
51 |
52 |

53 | OFFRE 54 |

55 |
56 |
57 |
58 |
59 | 60 |

61 | Simple et rapide 62 |

63 |

64 | Vous souhaitez offrir un pantalon ou un soulier de taille et de pointure bien précise, 65 | vous voulez faire don de médicaments pour des malades chroniques, vous êtes en 66 | possession d’un fauteuil roulant que vous voulez prêter ou donner à une personne atteint 67 | d’un handicap. Il vous suffit d’un simple clic sur notre plateforme. 68 | 69 |

70 |
71 |
72 |
73 |
74 | 75 |

76 | Confidentialité 77 |

78 |

79 | Les données personnelles des nécessiteux ainsi que celles des donateurs demeureront 80 | invisibles et anonymes. 81 |

82 |
83 |
84 |
85 |
86 | 87 |

88 | Traçabilité des données 89 |

90 |

91 | Cette plateforme permet d’avoir une traçabilité de toutes les actions entreprises : 92 | gestion des dons, gestion des nécessiteux, gestion des besoins… 93 |

94 |
95 |
96 | 97 |
98 |
99 | 100 |

101 | Disponibilité et accompagnement 102 |

103 |

Notre équipe reste disponible pour vous accompagner et s'engage à répondre dans les plus 104 | courts délais à toutes vos questions et préoccupations. 105 |

106 |
107 |
108 |
109 |
110 |
111 | {% include "charity/statistiques.html" %} 112 |
113 | {% endblock %} 114 | {% block extra_js %} 115 | $(document).ready(function () { 116 | const endpoint = '/necessiteux/data/'; 117 | $.ajax({ 118 | method: "GET", 119 | url: endpoint, 120 | success: function (data) { 121 | setChart(data); 122 | }, 123 | error: function (error) { 124 | } 125 | }); 126 | 127 | function setChart(data) { 128 | $('#totale').html(data.numbers.totale); 129 | $('#familles').html(data.numbers.familles); 130 | $('#enfants').html(data.numbers.enfants); 131 | let ctx = document.getElementById('needs-charts-gender').getContext('2d'); 132 | const chart = new Chart(ctx, { 133 | type: 'doughnut', 134 | // data are percentages 135 | data: { 136 | labels: data.gander_data.labels, 137 | datasets: [{ 138 | label: "Les nécessiteux par sexe", 139 | backgroundColor: ['rgb(255, 99, 132)', 'rgb(63, 173, 255)'], 140 | borderColor: ['rgb(255, 99, 132)', 'rgb(63, 173, 255)'], 141 | data: data.gander_data.data, 142 | }] 143 | }, 144 | options: {} 145 | }); 146 | let ctx_age = document.getElementById('needs-charts-age').getContext('2d'); 147 | const chart_age = new Chart(ctx_age, { 148 | type: 'bar', 149 | // data are percentages 150 | data: { 151 | labels: data.age_data.labels, 152 | datasets: [{ 153 | label: "Les nécessiteux par age", 154 | backgroundColor: ['#FFA07A', '#DB7093', '#008080', '#6495ED', '#00FFFF'], 155 | borderColor: ['#FFA07A', '#DB7093', '#008080', '#6495ED', '#00FFFF'], 156 | data: data.age_data.data, 157 | }] 158 | }, 159 | options: { 160 | scales: { 161 | yAxes: [{ 162 | ticks: { 163 | beginAtZero: true, 164 | userCallback: function(label, index, labels) { 165 | if (Math.floor(label) === label) { 166 | return label; 167 | } 168 | 169 | }, 170 | } 171 | }], 172 | }, 173 | } 174 | }); 175 | } 176 | }); 177 | {% endblock extra_js %} 178 | -------------------------------------------------------------------------------- /base/static/js/dataTable.js: -------------------------------------------------------------------------------- 1 | (function (window, document, undefined) { 2 | 3 | var factory = function ($, DataTable) { 4 | "use strict"; 5 | $.extend(true, DataTable.defaults, { 6 | dom: 7 | "<'left aligned eight wide column'l><'right aligned eight wide column'f>" + 8 | "<'sixteen wide column'tr>" + 9 | "<'left aligned four wide column'i><'right aligned twelve wide column'p>", 10 | renderer: 'semantic' 11 | }); 12 | 13 | $.extend(DataTable.ext.pager, { 14 | full_numbers_icon: DataTable.ext.pager.full_numbers 15 | }); 16 | 17 | $.extend(DataTable.ext.classes, { 18 | sWrapper: "ui grid dataTables_wrapper ", 19 | sFilterInput: "", 20 | sLengthSelect: "" 21 | }); 22 | 23 | DataTable.ext.renderer.pageButton.semantic = function (settings, host, idx, buttons, page, pages) { 24 | var api = new DataTable.Api(settings); 25 | var classes = settings.oClasses; 26 | var lang = settings.oLanguage.oPaginate; 27 | var btnDisplay, btnClass, btnIcon, counter = 0; 28 | var addIcons = ((!api.init().pagingType ? '' : api.init().pagingType.toLowerCase()).indexOf('icon') !== -1); 29 | 30 | var attach = function (container, buttons) { 31 | var i, ien, node, button; 32 | var clickHandler = function (e) { 33 | e.preventDefault(); 34 | if (!$(e.currentTarget).hasClass('disabled')) { 35 | api.page(e.data.action).draw('page'); 36 | } 37 | }; 38 | 39 | for (i = 0, ien = buttons.length; i < ien; i++) { 40 | button = buttons[i]; 41 | 42 | if ($.isArray(button)) { 43 | attach(container, button); 44 | } 45 | else { 46 | btnDisplay = ''; 47 | btnClass = ''; 48 | btnIcon = ''; 49 | switch (button) { 50 | case 'ellipsis': 51 | btnDisplay = (addIcons ? '' : '…'); 52 | btnClass = 'disabled'; 53 | break; 54 | 55 | case 'first': 56 | btnIcon = (addIcons ? '' : ''); 57 | btnDisplay = btnIcon + lang.sFirst; 58 | btnClass = button + (page > 0 ? 59 | '' : ' disabled'); 60 | break; 61 | 62 | case 'previous': 63 | btnIcon = (addIcons ? '' : ''); 64 | btnDisplay = btnIcon + lang.sPrevious; 65 | btnClass = button + (page > 0 ? 66 | '' : ' disabled'); 67 | break; 68 | 69 | case 'next': 70 | btnIcon = (addIcons ? '' : ''); 71 | btnDisplay = lang.sNext + btnIcon; 72 | btnClass = button + (page < pages - 1 ? 73 | '' : ' disabled'); 74 | break; 75 | 76 | case 'last': 77 | btnIcon = (addIcons ? '' : ''); 78 | btnDisplay = lang.sLast + btnIcon; 79 | btnClass = button + (page < pages - 1 ? 80 | '' : ' disabled'); 81 | break; 82 | 83 | default: 84 | btnDisplay = button + 1; 85 | btnClass = page === button ? 86 | 'active' : ''; 87 | break; 88 | } 89 | 90 | 91 | if (btnDisplay) { 92 | node = $('', { 93 | 'class': classes.sPageButton + ' ' + btnClass + ' item ', 94 | 'id': idx === 0 && typeof button === 'string' ? 95 | settings.sTableId + '_' + button : 96 | null 97 | }).html(btnDisplay).appendTo(container); 98 | 99 | settings.oApi._fnBindAction( 100 | node, {action: button}, clickHandler 101 | ); 102 | 103 | counter++; 104 | } 105 | } 106 | } 107 | }; 108 | var activeEl; 109 | 110 | try { 111 | activeEl = $(host).find(document.activeElement).data('dt-idx'); 112 | } 113 | catch (e) { 114 | } 115 | 116 | attach( 117 | $(host).empty().html('