├── EC
├── __init__.py
├── docker_settings.py
├── wsgi.py
├── urls.py
└── settings.py
├── ecweb
├── __init__.py
├── tests
│ ├── __init__.py
│ ├── test_url.py
│ ├── test_models.py
│ ├── test_create_student_view.py
│ └── test_create_user_type_view.py
├── migrations
│ ├── __init__.py
│ ├── 0005_classroom_slug.py
│ ├── 0003_auto_20171227_0048.py
│ ├── 0008_classroom_is_active.py
│ ├── 0009_auto_20171230_1715.py
│ ├── 0004_classroom_turn.py
│ ├── 0007_auto_20171229_1943.py
│ ├── 0011_auto_20180103_0243.py
│ ├── 0006_auto_20171229_1918.py
│ ├── 0002_createsuperuser.py
│ ├── 0010_event.py
│ └── 0001_initial.py
├── templates
│ ├── registration
│ │ ├── password_reset_subject.txt
│ │ ├── logout.html
│ │ ├── forget-complete.html
│ │ ├── change-password.html
│ │ ├── forget-done.html
│ │ ├── password_reset_email.html
│ │ ├── forget-password.html
│ │ ├── forget-confirm.html
│ │ ├── create_user.html
│ │ ├── create_student.html
│ │ └── login.html
│ └── ecweb
│ │ ├── home.html
│ │ ├── base.html
│ │ ├── events.html
│ │ ├── classroom.html
│ │ ├── classes.html
│ │ ├── class_attendance.html
│ │ ├── classroom
│ │ ├── create_classroom.html
│ │ ├── update_classroom.html
│ │ ├── classroom_confirm_delete.html
│ │ ├── classroom.html
│ │ └── detail_classroom.html
│ │ ├── teacher-dashboard.html
│ │ ├── coordinator-dashboard.html
│ │ ├── student.html
│ │ ├── baseadmin.html
│ │ └── student-dashboard.html
├── static
│ ├── images
│ │ ├── face.jpg
│ │ ├── .DS_Store
│ │ ├── favicon.png
│ │ ├── flags
│ │ │ ├── AD.png
│ │ │ ├── AE.png
│ │ │ ├── AG.png
│ │ │ ├── AM.png
│ │ │ ├── AR.png
│ │ │ ├── AT.png
│ │ │ ├── AU.png
│ │ │ ├── BE.png
│ │ │ ├── BF.png
│ │ │ ├── BG.png
│ │ │ ├── BO.png
│ │ │ ├── BR.png
│ │ │ ├── CA.png
│ │ │ ├── CD.png
│ │ │ ├── CG.png
│ │ │ ├── CH.png
│ │ │ ├── CL.png
│ │ │ ├── CM.png
│ │ │ ├── CN.png
│ │ │ ├── CO.png
│ │ │ ├── CZ.png
│ │ │ ├── DE.png
│ │ │ ├── DJ.png
│ │ │ ├── DK.png
│ │ │ ├── DZ.png
│ │ │ ├── EE.png
│ │ │ ├── EG.png
│ │ │ ├── ES.png
│ │ │ ├── FI.png
│ │ │ ├── FR.png
│ │ │ ├── GA.png
│ │ │ ├── GB.png
│ │ │ ├── GM.png
│ │ │ ├── GT.png
│ │ │ ├── HN.png
│ │ │ ├── HT.png
│ │ │ ├── HU.png
│ │ │ ├── ID.png
│ │ │ ├── IE.png
│ │ │ ├── IL.png
│ │ │ ├── IN.png
│ │ │ ├── IQ.png
│ │ │ ├── IR.png
│ │ │ ├── IT.png
│ │ │ ├── JM.png
│ │ │ ├── JO.png
│ │ │ ├── JP.png
│ │ │ ├── KG.png
│ │ │ ├── KN.png
│ │ │ ├── KP.png
│ │ │ ├── KR.png
│ │ │ ├── KW.png
│ │ │ ├── KZ.png
│ │ │ ├── LA.png
│ │ │ ├── LB.png
│ │ │ ├── LC.png
│ │ │ ├── LS.png
│ │ │ ├── LU.png
│ │ │ ├── LV.png
│ │ │ ├── MG.png
│ │ │ ├── MK.png
│ │ │ ├── ML.png
│ │ │ ├── MM.png
│ │ │ ├── MT.png
│ │ │ ├── MX.png
│ │ │ ├── NA.png
│ │ │ ├── NE.png
│ │ │ ├── NG.png
│ │ │ ├── NI.png
│ │ │ ├── NL.png
│ │ │ ├── NO.png
│ │ │ ├── OM.png
│ │ │ ├── PA.png
│ │ │ ├── PE.png
│ │ │ ├── PG.png
│ │ │ ├── PK.png
│ │ │ ├── PL.png
│ │ │ ├── PT.png
│ │ │ ├── PY.png
│ │ │ ├── QA.png
│ │ │ ├── RO.png
│ │ │ ├── RU.png
│ │ │ ├── RW.png
│ │ │ ├── SA.png
│ │ │ ├── SE.png
│ │ │ ├── SG.png
│ │ │ ├── SL.png
│ │ │ ├── SN.png
│ │ │ ├── SO.png
│ │ │ ├── SV.png
│ │ │ ├── TD.png
│ │ │ ├── TJ.png
│ │ │ ├── TL.png
│ │ │ ├── TR.png
│ │ │ ├── TZ.png
│ │ │ ├── UA.png
│ │ │ ├── US.png
│ │ │ ├── VE.png
│ │ │ ├── VN.png
│ │ │ └── YE.png
│ │ ├── icons
│ │ │ ├── 1.png
│ │ │ ├── 10.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ ├── 5.png
│ │ │ ├── 6.png
│ │ │ ├── 7.png
│ │ │ ├── 8.png
│ │ │ └── 9.png
│ │ ├── profile.jpg
│ │ ├── logo_star_mini.jpg
│ │ ├── logo_star_black.png
│ │ ├── star-admin-logo.png
│ │ ├── logo_star_black.svg
│ │ └── logo_star.svg
│ ├── css
│ │ ├── fonts
│ │ │ ├── FontAwesome.otf
│ │ │ ├── fontawesome-webfont.eot
│ │ │ ├── fontawesome-webfont.ttf
│ │ │ ├── fontawesome-webfont.woff
│ │ │ └── fontawesome-webfont.woff2
│ │ └── libs
│ │ │ ├── cropper.min.css
│ │ │ └── perfect-scrollbar.min.css
│ └── js
│ │ ├── off-canvas.js
│ │ ├── maps.js
│ │ ├── misc.js
│ │ ├── hoverable-collapse.js
│ │ ├── chart.js
│ │ └── libs
│ │ ├── tether.min.js
│ │ └── perfect-scrollbar.min.js
├── apps.py
├── utils
│ └── li.py
├── urls.py
├── admin.py
├── forms.py
├── models.py
└── views.py
├── media
└── avatars
│ ├── bot.jpeg
│ └── user_default.png
├── .travis.yml
├── provision
├── docker
│ ├── wait-for-it.sh
│ └── Dockerfile
└── compose
│ └── docker-compose.yml
├── req_dev.txt
├── manage.py
├── LICENSE
├── .gitignore
└── README.md
/EC/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ecweb/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ecweb/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ecweb/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ecweb/templates/registration/password_reset_subject.txt:
--------------------------------------------------------------------------------
1 | TestSite password reset
--------------------------------------------------------------------------------
/media/avatars/bot.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/media/avatars/bot.jpeg
--------------------------------------------------------------------------------
/ecweb/static/images/face.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/face.jpg
--------------------------------------------------------------------------------
/ecweb/static/images/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/.DS_Store
--------------------------------------------------------------------------------
/media/avatars/user_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/media/avatars/user_default.png
--------------------------------------------------------------------------------
/ecweb/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class EcwebConfig(AppConfig):
5 | name = 'ecweb'
6 |
--------------------------------------------------------------------------------
/ecweb/static/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/favicon.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/AD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/AD.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/AE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/AE.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/AG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/AG.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/AM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/AM.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/AR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/AR.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/AT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/AT.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/AU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/AU.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/BE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/BE.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/BF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/BF.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/BG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/BG.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/BO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/BO.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/BR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/BR.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/CA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/CA.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/CD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/CD.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/CG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/CG.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/CH.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/CH.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/CL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/CL.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/CM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/CM.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/CN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/CN.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/CO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/CO.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/CZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/CZ.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/DE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/DE.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/DJ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/DJ.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/DK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/DK.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/DZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/DZ.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/EE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/EE.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/EG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/EG.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/ES.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/ES.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/FI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/FI.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/FR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/FR.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/GA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/GA.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/GB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/GB.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/GM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/GM.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/GT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/GT.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/HN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/HN.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/HT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/HT.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/HU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/HU.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/ID.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/ID.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/IE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/IE.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/IL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/IL.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/IN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/IN.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/IQ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/IQ.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/IR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/IR.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/IT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/IT.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/JM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/JM.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/JO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/JO.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/JP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/JP.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/KG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/KG.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/KN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/KN.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/KP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/KP.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/KR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/KR.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/KW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/KW.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/KZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/KZ.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/LA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/LA.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/LB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/LB.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/LC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/LC.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/LS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/LS.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/LU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/LU.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/LV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/LV.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/MG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/MG.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/MK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/MK.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/ML.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/ML.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/MM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/MM.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/MT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/MT.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/MX.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/MX.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/NA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/NA.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/NE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/NE.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/NG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/NG.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/NI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/NI.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/NL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/NL.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/NO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/NO.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/OM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/OM.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/PA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/PA.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/PE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/PE.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/PG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/PG.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/PK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/PK.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/PL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/PL.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/PT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/PT.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/PY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/PY.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/QA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/QA.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/RO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/RO.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/RU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/RU.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/RW.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/RW.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/SA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/SA.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/SE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/SE.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/SG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/SG.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/SL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/SL.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/SN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/SN.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/SO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/SO.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/SV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/SV.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/TD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/TD.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/TJ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/TJ.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/TL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/TL.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/TR.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/TR.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/TZ.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/TZ.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/UA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/UA.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/US.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/US.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/VE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/VE.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/VN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/VN.png
--------------------------------------------------------------------------------
/ecweb/static/images/flags/YE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/flags/YE.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/1.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/10.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/2.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/3.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/4.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/5.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/6.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/7.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/8.png
--------------------------------------------------------------------------------
/ecweb/static/images/icons/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/icons/9.png
--------------------------------------------------------------------------------
/ecweb/static/images/profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/profile.jpg
--------------------------------------------------------------------------------
/ecweb/static/css/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/css/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/ecweb/static/images/logo_star_mini.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/logo_star_mini.jpg
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/home.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/base.html' %}
2 |
3 | {% block content %}
4 | Home
5 | {% endblock %}
6 |
--------------------------------------------------------------------------------
/ecweb/static/images/logo_star_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/logo_star_black.png
--------------------------------------------------------------------------------
/ecweb/static/images/star-admin-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/images/star-admin-logo.png
--------------------------------------------------------------------------------
/ecweb/static/css/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/css/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/ecweb/static/css/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/css/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/ecweb/static/css/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/css/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/ecweb/static/css/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jamesperes-zz/EC/HEAD/ecweb/static/css/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/ecweb/static/js/off-canvas.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | $(function() {
3 | $('[data-toggle="offcanvas"]').on("click", function () {
4 | $('.row-offcanvas').toggleClass('active')
5 | });
6 | });
7 | })(jQuery);
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "3.6"
4 | - "nightly"
5 | # command to install dependencies
6 | install:
7 | - pip install -r req_dev.txt
8 | # command to run tests
9 | script:
10 | - python manage.py test
--------------------------------------------------------------------------------
/ecweb/static/js/maps.js:
--------------------------------------------------------------------------------
1 | var map;
2 | function initMap() {
3 | map = new google.maps.Map(document.getElementById('map'), {
4 | center: {lat: -34.397, lng: 150.644},
5 | zoom: 8
6 | });
7 | }
8 |
--------------------------------------------------------------------------------
/ecweb/templates/registration/logout.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends 'ecweb/base.html' %}
3 |
4 | {% block content %}
5 |
6 |
Logged out!
7 |
8 | Click here to login again.
9 |
10 | {% endblock %}
--------------------------------------------------------------------------------
/ecweb/templates/registration/forget-complete.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends 'ecweb/base.html' %}
3 |
4 | {% block content %}
5 |
6 | Your password has been set. You may go ahead and sign in now.
7 |
8 | {% endblock %}
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 | English College
6 | {% block head %}{% endblock %}
7 |
8 |
9 | {% block content %}{% endblock %}
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ecweb/templates/registration/change-password.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% block content %}
4 | Forgot password123
5 |
10 | {% endblock %}
--------------------------------------------------------------------------------
/EC/docker_settings.py:
--------------------------------------------------------------------------------
1 | DATABASES = {
2 | 'default': {
3 | 'ENGINE': 'django.db.backends.postgresql_psycopg2',
4 | 'NAME': 'ec_development',
5 | 'USER': 'ec_development',
6 | 'HOST': 'db',
7 | 'PORT': 5432,
8 | 'PASSWORD': '98uonqwnepj021'
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ecweb/static/js/misc.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | $(function() {
3 | $('#sidebar .nav').perfectScrollbar();
4 | $('.container-scroller').perfectScrollbar( {suppressScrollX: true});
5 | $('[data-toggle="minimize"]').on("click", function () {
6 | $('body').toggleClass('sidebar-icon-only');
7 | });
8 | });
9 | })(jQuery);
10 |
--------------------------------------------------------------------------------
/provision/docker/wait-for-it.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # wait-for-postgres.sh
3 |
4 | set -e
5 |
6 | host="$1"
7 | shift
8 | cmd="$@"
9 |
10 | until psql -h "$host" -U "ec_development" --dbname "ec_development" -c '\l'; do
11 | >&2 echo "Postgres is unavailable - sleeping"
12 | sleep 1
13 | done
14 |
15 | >&2 echo "Postgres is up - executing command"
16 | exec $cmd
17 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/events.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content %}
5 |
6 | Events ---
7 |
8 |
9 |
10 | {% for event in events %}
11 |
12 |
13 | {{event.title}}
14 |
15 |
16 |
17 | {% endfor %}
18 |
19 |
20 | {% endblock %}
--------------------------------------------------------------------------------
/req_dev.txt:
--------------------------------------------------------------------------------
1 | decorator==4.1.2
2 | Django==2.0
3 | docopt==0.6.2
4 | ipython==6.2.1
5 | ipython-genutils==0.2.0
6 | jedi==0.11.0
7 | olefile==0.44
8 | parso==0.1.0
9 | pexpect==4.3.0
10 | pickleshare==0.7.4
11 | Pillow==4.3.0
12 | prompt-toolkit==1.0.15
13 | ptpython==0.41
14 | ptyprocess==0.5.2
15 | Pygments==2.2.0
16 | pytz==2017.3
17 | simplegeneric==0.8.1
18 | six==1.11.0
19 | traitlets==4.3.2
20 | wcwidth==0.1.7
21 | psycopg2
22 | geocoder==1.33.0
23 |
--------------------------------------------------------------------------------
/EC/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for EC project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "EC.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/ecweb/templates/registration/forget-done.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends 'ecweb/base.html' %}
3 |
4 | {% block content %}
5 |
6 | We've emailed you instructions for setting your password, if an account exists with the email you entered.
7 | You should receive them shortly.
8 |
9 |
10 | If you don't receive an email, please make sure you've entered the address you registered with,
11 | and check your spam folder.
12 |
13 | {% endblock %}
--------------------------------------------------------------------------------
/ecweb/templates/registration/password_reset_email.html:
--------------------------------------------------------------------------------
1 | {% autoescape off %}
2 | To initiate the password reset process for your {{ user.get_username }} TestSite Account,
3 | click the link below:
4 |
5 | {{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
6 |
7 | If clicking the link above doesn't work, please copy and paste the URL in a new browser
8 | window instead.
9 |
10 | Sincerely,
11 | The TestSite Team
12 | {% endautoescape %}
--------------------------------------------------------------------------------
/ecweb/utils/li.py:
--------------------------------------------------------------------------------
1 | """ List of CHOICES"""
2 |
3 |
4 | level_choices = (
5 | ('Beginner', 'Beginner'),
6 | ('Elementary', 'Elementary'),
7 | )
8 |
9 |
10 | type_list = (
11 | ('1-month', '1-month'),
12 | ('6-month', '6-month'),
13 | )
14 |
15 | test_choices = (
16 | ('listening', 'Listening'),
17 | ('reading', 'Reading'),
18 | )
19 |
20 | classroom_turns_choices = (
21 | ('morning', 'Morning'),
22 | ('afternoon', 'Afternoon')
23 | )
24 |
--------------------------------------------------------------------------------
/ecweb/migrations/0005_classroom_slug.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2017-12-29 18:33
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('ecweb', '0004_classroom_turn'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='classroom',
15 | name='slug',
16 | field=models.SlugField(blank=True, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/ecweb/migrations/0003_auto_20171227_0048.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2017-12-27 00:48
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('ecweb', '0002_createsuperuser'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name='coordinator',
15 | options={'permissions': (('view_all_classrooms', 'Can view all classrooms'),)},
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/ecweb/migrations/0008_classroom_is_active.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2017-12-30 12:14
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('ecweb', '0007_auto_20171229_1943'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='classroom',
15 | name='is_active',
16 | field=models.BooleanField(default=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/ecweb/templates/registration/forget-password.html:
--------------------------------------------------------------------------------
1 | {% extends "ecweb/base.html" %}
2 | {% block content %}
3 |
4 |
5 |
Reset your Password
6 |
11 |
12 |
13 | {% endblock %}
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/classroom.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}{% endblock %}
5 |
6 | {% block js %}{% endblock %}
7 |
8 |
9 | {% block content %}
10 |
11 |
12 |
13 | {% for classroom in classrooms%}
14 |
{{classroom.level}} - {{classroom.number_class}}
15 |
link
16 | {% endfor%}
17 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/ecweb/migrations/0009_auto_20171230_1715.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2017-12-30 17:15
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('ecweb', '0008_classroom_is_active'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='classroom',
15 | name='number_class',
16 | field=models.PositiveIntegerField(blank=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/classes.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}{% endblock %}
5 |
6 | {% block js %}{% endblock %}
7 |
8 | {% block content %}
9 |
10 |
Classes
11 |
12 | {% for class_obj in all_classes%}
13 |
{{class_obj.classroom.level}} -
14 |
15 | {{class_obj.date}}
16 |
17 | {% endfor %}
18 |
19 | {% endblock %}
20 |
--------------------------------------------------------------------------------
/ecweb/templates/registration/forget-confirm.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends 'ecweb/base.html' %}
3 |
4 | {% block content %}
5 | {% if validlink %}
6 | Change password
7 |
12 | {% else %}
13 |
14 | The password reset link was invalid, possibly because it has already been used.
15 | Please request a new password reset.
16 |
17 | {% endif %}
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/ecweb/migrations/0004_classroom_turn.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2017-12-29 18:28
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('ecweb', '0003_auto_20171227_0048'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='classroom',
15 | name='turn',
16 | field=models.CharField(choices=[('morning', 'Morning'), ('afternoon', 'Afternoon')], default=1, max_length=50),
17 | preserve_default=False,
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/ecweb/migrations/0007_auto_20171229_1943.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2017-12-29 19:43
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('ecweb', '0006_auto_20171229_1918'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name='coordinator',
15 | options={'permissions': (('view_all_classrooms', 'Can view all classrooms'),)},
16 | ),
17 | migrations.AlterModelOptions(
18 | name='teacher',
19 | options={},
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/class_attendance.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 |
6 |
7 | {% endblock %}
8 |
9 | {% block js %}
10 |
11 |
12 | {% endblock %}
13 |
14 |
15 |
16 |
17 | {% block content %}
18 |
19 |
20 | Date: {{ class_obj.date }}
21 |
22 | Lessons: {{ class_obj.lesson }}
23 |
24 |
29 |
30 | {% endblock %}
31 |
--------------------------------------------------------------------------------
/ecweb/migrations/0011_auto_20180103_0243.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2018-01-03 02:43
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('ecweb', '0010_event'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='event',
15 | name='end_event',
16 | field=models.DateTimeField(blank=True),
17 | ),
18 | migrations.AlterField(
19 | model_name='event',
20 | name='start_event',
21 | field=models.DateTimeField(),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/ecweb/tests/test_url.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.urls import resolve
3 |
4 |
5 | from ecweb import views
6 |
7 |
8 | class UrlsTest(TestCase):
9 | def test_root_url_resolves_to_home_view(self):
10 | found = resolve('/')
11 | self.assertEqual(found.func, views.home_dashboard)
12 |
13 | def test_home_page_not_loged_returns_login_html(self):
14 | response = self.client.get('/', follow=True)
15 | self.assertTemplateUsed(response, 'registration/login.html')
16 |
17 | def test_student_resolve_user_detail_view(self):
18 | found = resolve('/student/')
19 | self.assertEqual(found.func, views.user_detail)
20 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/classroom/create_classroom.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}{% endblock %}
5 |
6 | {% block js %}{% endblock %}
7 |
8 | {% block content %}
9 |
10 | {% for message in messages %}
11 | {{ message }}
12 | {% endfor %}
13 |
23 |
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/classroom/update_classroom.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}{% endblock %}
5 |
6 | {% block js %}{% endblock %}
7 |
8 | {% block content %}
9 |
10 | {% for message in messages %}
11 | {{ message }}
12 | {% endfor %}
13 |
23 |
24 | {% endblock %}
--------------------------------------------------------------------------------
/provision/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 |
3 | MAINTAINER Rondineli Gomes
4 |
5 | USER root
6 |
7 | RUN apt-get update && apt-get upgrade -yq
8 |
9 | RUN apt-get install python3-dev python3-pip postgresql-client postgresql-client-common -yq
10 |
11 | RUN mkdir /src
12 |
13 | ENV PYTHONPATH = /src/
14 |
15 | VOLUME /src
16 |
17 | ADD . /src
18 |
19 | WORKDIR /src
20 |
21 | RUN GIT_TRACE=1 pip3 install --exists-action s -r req_dev.txt
22 |
23 | RUN cp -rf /src/provision/docker/wait-for-it.sh /usr/bin/
24 |
25 | RUN chmod +x /usr/bin/wait-for-it.sh
26 |
27 | RUN python3 manage.py migrate
28 |
29 | CMD ["/usr/bin/python3", "manage.py", "runserver", "0.0.0.0:8080"]
30 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/teacher-dashboard.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 | {% endblock %}
6 |
7 | {% block js %}
8 | {% endblock %}
9 |
10 | {% block content %}
11 |
12 |
13 |
Teacher's Dashboard
14 |
17 |
18 |
25 | {% endblock %}
26 |
27 |
28 | {% block user_title %}
29 | Teacher
30 | {% endblock %}
--------------------------------------------------------------------------------
/ecweb/templates/registration/create_user.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 |
3 | {% block head %}
4 |
14 | {% endblock %}
15 |
16 | {% block content %}
17 |
31 | {% endblock %}
--------------------------------------------------------------------------------
/ecweb/migrations/0006_auto_20171229_1918.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2017-12-29 19:18
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('ecweb', '0005_classroom_slug'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name='coordinator',
15 | options={'permissions': (('view_all_classrooms', 'Can view all classrooms'), ('view_classroom_detail', 'Can view the classroom detail'))},
16 | ),
17 | migrations.AlterModelOptions(
18 | name='teacher',
19 | options={'permissions': (('view_classroom_detail', 'Can view the classroom detail'),)},
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/ecweb/migrations/0002_createsuperuser.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2017-12-24 04:04
2 |
3 | from django.utils import timezone
4 | from django.db import migrations
5 | from ecweb.models import BasicUser
6 |
7 | def create_superuser(apps, schema_editor):
8 | superuser = BasicUser()
9 | superuser.is_active = True
10 | superuser.is_superuser = True
11 | superuser.is_staff = True
12 | superuser.date_joined = timezone.now()
13 | superuser.username = 'admin'
14 | superuser.email = 'admin@admin.com'
15 | superuser.set_password('admin123')
16 | superuser.save()
17 |
18 |
19 | class Migration(migrations.Migration):
20 | dependencies = [
21 | ('ecweb', '0001_initial'),
22 | ]
23 |
24 | operations = [
25 | migrations.RunPython(create_superuser)
26 | ]
27 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "EC.settings")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError:
10 | # The above import may fail for some other reason. Ensure that the
11 | # issue is really that Django is missing to avoid masking other
12 | # exceptions on Python 2.
13 | try:
14 | import django
15 | except ImportError:
16 | raise ImportError(
17 | "Couldn't import Django. Are you sure it's installed and "
18 | "available on your PYTHONPATH environment variable? Did you "
19 | "forget to activate a virtual environment?"
20 | )
21 | raise
22 | execute_from_command_line(sys.argv)
23 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/classroom/classroom_confirm_delete.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}{% endblock %}
5 |
6 | {% block js %}{% endblock %}
7 |
8 | {% block content %}
9 |
27 | {% endblock %}
--------------------------------------------------------------------------------
/provision/compose/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.1'
2 | services:
3 | ec_app:
4 | build:
5 | context: ../../.
6 | dockerfile: provision/docker/Dockerfile
7 | restart: always
8 | ports:
9 | - 8080:8080
10 | command: bash -c "/usr/bin/wait-for-it.sh db sleep 5; python3 /src/manage.py migrate && python3 /src/manage.py runserver 0.0.0.0:8080"
11 |
12 | environment:
13 | - DOCKER_DEVELOPMENT=1
14 | - PGPASSWORD=98uonqwnepj021
15 |
16 | volumes:
17 | - ../../.:/src
18 |
19 | depends_on:
20 | - db
21 |
22 | links:
23 | - db:db
24 |
25 | db:
26 | image: postgres
27 | restart: always
28 |
29 | ports:
30 | - 5432:5432
31 | environment:
32 | - POSTGRES_USER=ec_development
33 | - POSTGRES_PASSWORD=98uonqwnepj021
34 | - POSTGRES_DB=ec_development
35 |
36 |
--------------------------------------------------------------------------------
/ecweb/templates/registration/create_student.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 |
3 | {% block head %}
4 |
14 | {% endblock %}
15 |
16 | {% block content %}
17 |
43 | {% endblock %}
--------------------------------------------------------------------------------
/ecweb/templates/registration/login.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends 'ecweb/base.html' %}
3 |
4 | {% block content %}
5 |
6 | {% if form.errors %}
7 | Your username and password didn't match. Please try again.
8 | {% endif %}
9 |
10 | {% if next %}
11 | {% if user.is_authenticated %}
12 | Your account doesn't have access to this page. To proceed,
13 | please login with an account that has access.
14 | {% else %}
15 | Please login to see this page.
16 | {% endif %}
17 | {% endif %}
18 |
19 |
36 |
37 |
38 | Lost password?
39 |
40 | {% endblock %}
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 James
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/classroom/classroom.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}{% endblock %}
5 |
6 | {% block js %}{% endblock %}
7 |
8 | {% block content %}
9 |
10 | {% if perms.ecweb.view_all_classrooms %}
11 |
12 | Create a classroom
13 |
14 | {% endif %}
15 |
16 | {% if perms.ecweb.view_all_classrooms %}
17 |
All Classrooms
18 | {% else %}
19 |
20 | {% with total_classrooms=object_list.count %}
21 | My Classroom{{ total_classrooms|pluralize }}
22 | {% endwith %}
23 |
24 | {% endif %}
25 |
26 |
27 | {% for classroom in object_list %}
28 |
35 | {% endfor %}
36 |
37 |
38 | {% endblock %}
--------------------------------------------------------------------------------
/ecweb/migrations/0010_event.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2018-01-03 02:42
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 | ('ecweb', '0009_auto_20171230_1715'),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='Event',
16 | fields=[
17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 | ('description', models.TextField(max_length=500)),
19 | ('title', models.CharField(max_length=50)),
20 | ('start_event', models.DateField()),
21 | ('end_event', models.DateField(blank=True)),
22 | ('address', models.CharField(max_length=250)),
23 | ('neighborhood', models.CharField(max_length=250)),
24 | ('city', models.CharField(max_length=250)),
25 | ('local_lat', models.CharField(blank=True, max_length=250, null=True)),
26 | ('local_long', models.CharField(blank=True, max_length=250, null=True)),
27 | ('limit', models.IntegerField(blank=True)),
28 | ('teacher', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='ecweb.Teacher')),
29 | ],
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/ecweb/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import include, url
2 | from django.urls import path
3 |
4 | from . import views
5 |
6 |
7 | urlpatterns = [
8 | url(r'^$', views.home_dashboard),
9 | url(r'^dashboard/$', views.home_dashboard, name='home_dashboard'),
10 | url(r'^events/$', views.events_list_view, name='events_list_view'),
11 | url(
12 | r'^create-student/$',
13 | views.create_student_view,
14 | name='create-student'
15 | ),
16 | url(
17 | r'^create-user/(?P[-\w]+)/$',
18 | views.create_user_type_view,
19 | name='create-user'
20 | ),
21 | url(r'^student/', views.user_detail, name='user_detail'),
22 | url(r'^classrooms/$', views.ClassRoomListView.as_view(), name='classroom_view'),
23 | path('classrooms//detail', views.ClassRoomDetailView.as_view(), name='classroom_detail_view'),
24 | path('classrooms//edit', views.ClassRoomUpdateView.as_view(), name='classroom_update_view'),
25 | path('classrooms//delete', views.ClassRoomDeactivateView.as_view(), name='classroom_delete_view'),
26 | url(r'^classrooms/create/$', views.ClassRoomCreateView.as_view(), name='classroom_create_view'),
27 | url(r'^logout/$', views.logout_view),
28 | url(
29 | r'^class/(?P[0-9]+)/$',
30 | views.list_classes_view,
31 | name='list_classes_view'
32 | ),
33 | url(
34 | r'^class/(?P[0-9]+)/attendances/$',
35 | views.class_view,
36 | name='class_view'
37 | ),
38 | ]
39 |
--------------------------------------------------------------------------------
/.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 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | dist/
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 | *.sqlite3
104 | /media/avatars/profiles
105 |
106 | # pycharm
107 | .idea
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/coordinator-dashboard.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 |
34 | {% endblock %}
35 |
36 | {% block js %}
37 | {% endblock %}
38 |
39 | {% block content %}
40 |
41 |
Coordinator's Dashboard
42 |
48 |
54 |
60 |
61 |
68 | {% endblock %}
69 |
70 | {% block user_title %}
71 | Coordinator
72 | {% endblock %}
73 |
--------------------------------------------------------------------------------
/EC/urls.py:
--------------------------------------------------------------------------------
1 | """EC URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.11/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.conf.urls import url, include
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
15 | """
16 | from django.conf.urls import url
17 | from django.urls import include, path
18 | from django.conf import settings
19 | from django.contrib import admin
20 | from django.conf.urls.static import static
21 | from django.contrib.auth import views as auth_views
22 |
23 | urlpatterns = [
24 | path('', include('ecweb.urls')),
25 | url(r'^accounts/', include('django.contrib.auth.urls')),
26 | url(r'^admin/', admin.site.urls),
27 | path('change-password/', auth_views.PasswordChangeView.as_view(
28 | template_name='registration/change-password.html'),),
29 | path('forget-password/done', auth_views.PasswordResetDoneView.as_view(
30 | template_name='registration/forget-done.html'),),
31 | path('forget-password/', auth_views.PasswordResetView.as_view(
32 | template_name='registration/forget-password.html',
33 | success_url='done'), name='forget_password'),
34 | url(r'^password/reset/\
35 | (?P[0-9A-Za-z_\-]+)/\
36 | (?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
37 | auth_views.PasswordResetConfirmView.as_view(
38 | template_name='registration/forget-confirm.html',),
39 | name='password_reset_confirm',
40 | ),
41 | url(r'^password/reset/complete/$',
42 | auth_views.PasswordResetCompleteView.as_view(
43 | template_name='registration/forget-complete.html'),
44 | name='password_reset_complete'),
45 |
46 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
47 |
--------------------------------------------------------------------------------
/ecweb/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.forms import CheckboxSelectMultiple
3 |
4 | from .forms import CreateUserForm, UpdateUserFormAdmin
5 | from .models import (ClassRoom, Teacher, Student, Coordinator,
6 | Youtube, PdfFile, Class, BasicUser, Event)
7 |
8 |
9 | class BasicUserAdmin(admin.ModelAdmin):
10 | list_display = (
11 | 'first_name', 'last_name',
12 | 'email',
13 | )
14 | date_hierarchy = 'date_joined'
15 | search_fields = (
16 | 'first_name', 'last_name', 'email',
17 | 'date_joined',
18 | )
19 | list_filter = ('date_joined',)
20 | add_form = CreateUserForm
21 | form = UpdateUserFormAdmin
22 |
23 | def get_form(self, request, obj=None, **kwargs):
24 | defaults = {}
25 | if obj is None:
26 | defaults['form'] = self.add_form
27 | defaults.update(kwargs)
28 | return super().get_form(request, obj, **defaults)
29 |
30 |
31 | class CalendarAdmin(admin.ModelAdmin):
32 | pass
33 |
34 |
35 | class MenssageAdmin(admin.ModelAdmin):
36 | pass
37 |
38 |
39 | class YoutubeAdmin(admin.ModelAdmin):
40 | pass
41 |
42 |
43 | class PdfFileAdmin(admin.ModelAdmin):
44 | pass
45 |
46 |
47 | class ClassRoomAdmin(admin.ModelAdmin):
48 | pass
49 |
50 |
51 | class ClassAdmin(admin.ModelAdmin):
52 | def get_form(self, request, obj=None, **kwargs):
53 | form = super(ClassAdmin, self).get_form(request, obj, **kwargs)
54 | form.base_fields['attendances'].widget = CheckboxSelectMultiple()
55 | return form
56 |
57 |
58 | class StudentAdmin(admin.ModelAdmin):
59 | pass
60 |
61 |
62 | class TeacherAdmin(admin.ModelAdmin):
63 | pass
64 |
65 |
66 | class CoordinatorAdmin(admin.ModelAdmin):
67 | pass
68 |
69 | class EventAdmin(admin.ModelAdmin):
70 | pass
71 |
72 |
73 | admin.site.register(Event, EventAdmin)
74 | admin.site.register(BasicUser, BasicUserAdmin)
75 | admin.site.register(ClassRoom, ClassRoomAdmin)
76 | admin.site.register(Teacher, TeacherAdmin)
77 | admin.site.register(Youtube, YoutubeAdmin)
78 | admin.site.register(PdfFile, PdfFileAdmin)
79 | admin.site.register(Class, ClassAdmin)
80 | admin.site.register(Student, StudentAdmin)
81 | admin.site.register(Coordinator, CoordinatorAdmin)
--------------------------------------------------------------------------------
/ecweb/tests/test_models.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from ecweb.models import (
3 | ClassRoom,
4 | BasicUser,
5 | Student,
6 | Teacher
7 | )
8 |
9 |
10 | class ClassRoomModelTest(TestCase):
11 | def setUp(self):
12 | self.classroom = ClassRoom.objects.create(
13 | number_class='01',
14 | level="upper"
15 | )
16 | self.BasicUser = BasicUser.objects.create(
17 | email="test@ec.com",
18 | first_name="TestName",
19 | last_name="TestLastName"
20 | )
21 |
22 | def test_create(self):
23 | self.assertTrue(ClassRoom.objects.exists())
24 |
25 | def test_int_number_class(self):
26 | number = 1
27 | self.assertEqual(number, int(self.classroom.number_class))
28 |
29 | def test_str_level(self):
30 | self.assertEqual('upper', str(self.classroom.level))
31 |
32 | def test_student_forengkey(self):
33 | self.student = Student.objects.create(
34 | user=self.BasicUser,
35 | classroom=self.classroom,
36 | cod=1
37 | )
38 | self.assertTrue(Student.objects.exists())
39 |
40 | def test_teacher_forengkey(self):
41 | self.teacher = Teacher.objects.create(
42 | user=self.BasicUser
43 | )
44 | self.assertTrue(Teacher.objects.exists())
45 |
46 |
47 | class BasicUserModelTest(TestCase):
48 | def setUp(self):
49 | self.basic_user = BasicUser.objects.create(
50 | email="test@ec.com",
51 | password="abcd1234ec",
52 | first_name="TestName",
53 | last_name="TestLastName"
54 | )
55 | self.student = Student.objects.create(user=self.basic_user,
56 | cod=1,
57 | type_of_course='1-month'
58 | )
59 |
60 | def test_create(self):
61 | self.assertTrue(BasicUser.objects.exists())
62 |
63 | def test_student_exists(self):
64 | self.assertTrue(Student.objects.exists())
65 |
66 | def test_is_instance_of_BasicUser(self):
67 | self.assertIsInstance(self.basic_user, BasicUser)
68 |
69 | def test_default_image_profile(self):
70 | avatar_image_url = self.basic_user.avatar
71 | self.assertEqual('avatars/user_default.png', avatar_image_url)
72 |
--------------------------------------------------------------------------------
/ecweb/tests/test_create_student_view.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.shortcuts import resolve_url as r
3 | from ecweb.models import (
4 | BasicUser,
5 | Student,
6 | Coordinator
7 | )
8 | from ecweb.forms import CreateUserForm, StudentForm
9 |
10 |
11 | class TestCreateStudentView(TestCase):
12 | """ Testing createuser system """
13 |
14 | def setUp(self):
15 | user = BasicUser.objects.create_superuser(
16 | username='admin',
17 | password='1234abc',
18 | email="admin_test@admin.com"
19 | )
20 | Coordinator.objects.create(user=user)
21 | self.data = {
22 | 'first_name': 'admin',
23 | 'last_name': 'test',
24 | 'email': 'admin_test3@test.com',
25 | 'password': '123456ab',
26 | 'confirm_password': '123456ab',
27 | 'cod': 1,
28 | 'type_of_course': '1-month'
29 | }
30 | self.url = r('create-student')
31 |
32 | self.client.login(
33 | username='admin_test@admin.com',
34 | password='1234abc'
35 | )
36 | self.response = self.client.get(self.url)
37 |
38 | def test_get_response_status(self):
39 | self.assertEqual(200, self.response.status_code)
40 |
41 | def test_template_used(self):
42 | self.assertTemplateUsed(
43 | self.response,
44 | 'registration/create_student.html'
45 | )
46 |
47 | def test_forms_used(self):
48 | userform = self.response.context['userform']
49 | studentform = self.response.context['studentform']
50 | self.assertIsInstance(userform, CreateUserForm)
51 | self.assertIsInstance(studentform, StudentForm)
52 |
53 | def test_create_object(self):
54 | response = self.client.post(self.url, self.data)
55 | student = Student.objects.get(
56 | user__email=self.data['email']
57 | )
58 | self.assertIsInstance(student, Student)
59 | self.assertRedirects(response, r('home_dashboard'))
60 |
61 | def test_password_validation(self):
62 | self.data['confirm_password'] = '789010abc'
63 | response = self.client.post(self.url, self.data)
64 | userform = response.context['userform']
65 | self.assertTrue(userform.errors)
66 | self.assertIn(
67 | "Passwords don't match",
68 | userform.errors['confirm_password']
69 | )
70 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/classroom/detail_classroom.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}{% endblock %}
5 |
6 | {% block js %}{% endblock %}
7 |
8 | {% block content %}
9 |
10 | {% for message in messages %}
11 | {{ message }}
12 | {% endfor %}
13 |
{{object.level}} - {{object.number_class}} ({{object.turn}})
14 |
15 | {% if perms.ecweb.view_all_classrooms %}
16 | Update
17 | Delete
18 | {% endif %}
19 |
20 |
21 |
22 |
23 |
24 | Teachers
25 |
26 | First Name
27 | Last Name
28 | Email
29 |
30 |
31 |
32 | {% for student in object.teachers.all %}
33 |
34 |
35 | {{student.user.first_name}}
36 |
37 |
38 | {{student.user.last_name}}
39 |
40 |
41 | {{student.user}}
42 |
43 |
44 | {% endfor %}
45 |
46 |
47 |
48 |
49 | Students
50 |
51 | First Name
52 | Last Name
53 | Email
54 |
55 |
56 |
57 | {% for student in object.students.all %}
58 |
59 |
60 | {{student.user.first_name}}
61 |
62 |
63 | {{student.user.last_name}}
64 |
65 |
66 | {{student.user}}
67 |
68 |
69 | {% endfor %}
70 |
71 |
72 |
73 | Classes
74 | {% if object.class_set.all %}
75 | {% for class in object.class_set.all %}
76 | {{class}}
77 | {% endfor %}
78 | {% else %}
79 | Not found.
80 | {% endif %}
81 |
82 |
83 |
84 | {% endblock %}
--------------------------------------------------------------------------------
/ecweb/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.contrib.auth import forms as auth_forms
3 | from .models import BasicUser, Student
4 | from PIL import Image
5 |
6 |
7 | class CreateUserForm(forms.ModelForm):
8 | confirm_password = forms.CharField(
9 | label='Confirm Password',
10 | max_length=128,
11 | widget=forms.PasswordInput()
12 | )
13 |
14 | class Meta:
15 | model = BasicUser
16 | fields = [
17 | 'avatar', 'first_name',
18 | 'last_name', 'email',
19 | 'password', 'confirm_password'
20 | ]
21 | widgets = {
22 | 'password': forms.PasswordInput()
23 | }
24 |
25 | def clean_confirm_password(self):
26 | password = self.cleaned_data.get('password')
27 | confirm_password = self.cleaned_data.get('confirm_password')
28 | if password != confirm_password:
29 | raise forms.ValidationError("Passwords don't match")
30 | return super(CreateUserForm, self).clean()
31 |
32 | def save(self, commit=True):
33 | user = super(CreateUserForm, self).save(commit=False)
34 | user.set_password(self.cleaned_data['password'])
35 | if commit:
36 | user.save()
37 | return user
38 |
39 |
40 | class StudentForm(forms.ModelForm):
41 |
42 | class Meta:
43 | model = Student
44 | fields = ['cod', 'type_of_course']
45 |
46 |
47 | class UpdateUserFormAdmin(CreateUserForm):
48 | password = auth_forms.ReadOnlyPasswordHashField(
49 | label=("Password"),
50 | help_text=(
51 | "Raw passwords are not stored, instead "
52 | "a hash is created from them, and is "
53 | "saved in the database. "
54 | )
55 | )
56 |
57 |
58 | class PhotoForm(forms.ModelForm):
59 | x = forms.FloatField(widget=forms.HiddenInput())
60 | y = forms.FloatField(widget=forms.HiddenInput())
61 | width = forms.FloatField(widget=forms.HiddenInput())
62 | height = forms.FloatField(widget=forms.HiddenInput())
63 |
64 | class Meta:
65 | model = BasicUser
66 | fields = ('avatar', 'x', 'y', 'width', 'height', )
67 | widgets = {
68 | 'avatar': forms.FileInput(attrs={
69 | # this is not an actual validation! don't rely on that!
70 | 'accept': 'image/*'
71 | })
72 | }
73 |
74 | def save(self):
75 | photo = super(PhotoForm, self).save()
76 |
77 | x = self.cleaned_data.get('x')
78 | y = self.cleaned_data.get('y')
79 | w = self.cleaned_data.get('width')
80 | h = self.cleaned_data.get('height')
81 |
82 | image = Image.open(photo.avatar)
83 | cropped_image = image.crop((x, y, w + x, h + y))
84 | resized_image = cropped_image.resize((200, 200), Image.ANTIALIAS)
85 | resized_image.save(photo.avatar.path)
86 |
87 | return photo
88 |
89 |
90 | class AttendanceForm(forms.Form):
91 | class_id = forms.CharField(
92 | label='Class id', max_length=100, widget=forms.HiddenInput())
93 | students = forms.MultipleChoiceField(
94 | widget=forms.CheckboxSelectMultiple(), required=False)
95 |
--------------------------------------------------------------------------------
/ecweb/static/js/hoverable-collapse.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | $(document).on('mouseenter mouseleave', '.sidebar .nav-item', function (ev) {
3 | sidebarMini = $('body').hasClass("sidebar-mini");
4 | sidebarIconOnly = $('body').hasClass("sidebar-icon-only");
5 | horizontalMenu = $('body').hasClass("horizontal-menu");
6 | horizontalMenuTop = $('body').hasClass("horizontal-menu-top");
7 | boxedLayout = $('body').hasClass("boxed-layout");
8 | rtlLayout = $('body').hasClass("rtl");
9 | if (sidebarMini || sidebarIconOnly || horizontalMenu) {
10 | var $menuItem = $(this),
11 | $submenuWrapper = $('> .collapse', $menuItem);
12 | if(ev.type === 'mouseenter') {
13 | $submenuWrapper.addClass('show');
14 | // grab the menu item's position relative to its positioned parent
15 | var menuItemPos = $menuItem.offset();
16 | // place the submenu in the correct position relevant to the menu item
17 | if(horizontalMenu) {
18 | if(horizontalMenuTop) {
19 | if(rtlLayout) {
20 | $submenuWrapper.css({
21 | top: menuItemPos.top+$menuItem.height(),
22 | left: menuItemPos.left,
23 | minWidth: $menuItem.outerWidth()
24 | });
25 | }
26 | else {
27 | $submenuWrapper.css({
28 | top: menuItemPos.top+$menuItem.height(),
29 | left: menuItemPos.left - $('.navbar-brand-wrapper').outerWidth(),
30 | minWidth: $menuItem.outerWidth()
31 | });
32 | }
33 | }
34 | else {
35 | $submenuWrapper.css({
36 | top: menuItemPos.top+$menuItem.height(),
37 | left: menuItemPos.left,
38 | minWidth: $menuItem.outerWidth()
39 | });
40 | }
41 | }
42 | else {
43 | if(menuItemPos.top>=$('.sidebar').height()/2){
44 | $submenuWrapper.css({
45 | top: menuItemPos.top+$menuItem.height()- $(window).scrollTop()-$submenuWrapper.height()
46 | });
47 | }
48 | else {
49 | $submenuWrapper.css({
50 | top: menuItemPos.top- $(window).scrollTop()
51 | });
52 | }
53 | if(boxedLayout) {
54 | if(rtlLayout) {
55 | $submenuWrapper.css({
56 | right: $menuItem.outerWidth() + $('.container-scroller').css('padding-right')
57 | });
58 | }
59 | else {
60 | $submenuWrapper.css({
61 | left: menuItemPos.left + Math.round($menuItem.outerWidth()-$('.container-scroller').css('padding-left'))
62 | });
63 | }
64 | }
65 | else {
66 | if(rtlLayout) {
67 | $submenuWrapper.css({
68 | right: $menuItem.outerWidth()
69 | });
70 | }
71 | else {
72 | $submenuWrapper.css({
73 | left: menuItemPos.left + Math.round($menuItem.outerWidth())
74 | });
75 | }
76 | }
77 | }
78 | }
79 | else {
80 | $submenuWrapper.removeClass('show');
81 | }
82 | }
83 | });
84 | })(jQuery);
85 |
--------------------------------------------------------------------------------
/ecweb/static/css/libs/cropper.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Cropper v3.0.0-beta
3 | * https://github.com/fengyuanchen/cropper
4 | *
5 | * Copyright (c) 2017 Fengyuan Chen
6 | * Released under the MIT license
7 | *
8 | * Date: 2017-02-25T07:44:44.656Z
9 | */
10 |
11 | .cropper-container{font-size:0;line-height:0;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;direction:ltr;-ms-touch-action:none;touch-action:none}.cropper-container img{display:block;min-width:0!important;max-width:none!important;min-height:0!important;max-height:none!important;width:100%;height:100%;image-orientation:0deg}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{position:absolute;top:0;right:0;bottom:0;left:0}.cropper-wrap-box{overflow:hidden}.cropper-drag-box{opacity:0;background-color:#fff}.cropper-modal{opacity:.5;background-color:#000}.cropper-view-box{display:block;overflow:hidden;width:100%;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75)}.cropper-dashed{position:absolute;display:block;opacity:.5;border:0 dashed #eee}.cropper-dashed.dashed-h{top:33.33333%;left:0;width:100%;height:33.33333%;border-top-width:1px;border-bottom-width:1px}.cropper-dashed.dashed-v{top:0;left:33.33333%;width:33.33333%;height:100%;border-right-width:1px;border-left-width:1px}.cropper-center{position:absolute;top:50%;left:50%;display:block;width:0;height:0;opacity:.75}.cropper-center:after,.cropper-center:before{position:absolute;display:block;content:" ";background-color:#eee}.cropper-center:before{top:0;left:-3px;width:7px;height:1px}.cropper-center:after{top:-3px;left:0;width:1px;height:7px}.cropper-face,.cropper-line,.cropper-point{position:absolute;display:block;width:100%;height:100%;opacity:.1}.cropper-face{top:0;left:0;background-color:#fff}.cropper-line{background-color:#39f}.cropper-line.line-e{top:0;right:-3px;width:5px;cursor:e-resize}.cropper-line.line-n{top:-3px;left:0;height:5px;cursor:n-resize}.cropper-line.line-w{top:0;left:-3px;width:5px;cursor:w-resize}.cropper-line.line-s{bottom:-3px;left:0;height:5px;cursor:s-resize}.cropper-point{width:5px;height:5px;opacity:.75;background-color:#39f}.cropper-point.point-e{top:50%;right:-3px;margin-top:-3px;cursor:e-resize}.cropper-point.point-n{top:-3px;left:50%;margin-left:-3px;cursor:n-resize}.cropper-point.point-w{top:50%;left:-3px;margin-top:-3px;cursor:w-resize}.cropper-point.point-s{bottom:-3px;left:50%;margin-left:-3px;cursor:s-resize}.cropper-point.point-ne{top:-3px;right:-3px;cursor:ne-resize}.cropper-point.point-nw{top:-3px;left:-3px;cursor:nw-resize}.cropper-point.point-sw{bottom:-3px;left:-3px;cursor:sw-resize}.cropper-point.point-se{right:-3px;bottom:-3px;width:20px;height:20px;cursor:se-resize;opacity:1}@media (min-width:768px){.cropper-point.point-se{width:15px;height:15px}}@media (min-width:992px){.cropper-point.point-se{width:10px;height:10px}}@media (min-width:1200px){.cropper-point.point-se{width:5px;height:5px;opacity:.75}}.cropper-point.point-se:before{position:absolute;right:-50%;bottom:-50%;display:block;width:200%;height:200%;content:" ";opacity:0;background-color:#39f}.cropper-invisible{opacity:0}.cropper-bg{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")}.cropper-hide{position:absolute;display:block;width:0;height:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/jamesperes/EC)
2 |
3 | # EC
4 | Project OpenSource for manage students in English College.
5 |
6 | The project is based on the rules of language schools in Ireland and used to show student information during the course.
7 |
8 | **For students:**
9 | - See test rates
10 | - Follow school attendances
11 | - Receive documents, videos and Links from teachers
12 | - Follow nexts events in school
13 |
14 | **For teachers:**
15 | - To plan classes lessons, with videos, links and documents
16 | - To manage the performed tests, giving notes to each student
17 | - To manage the students attendances
18 | - Notify student by emails
19 | - Disclose supplementary events for school
20 |
21 | **For Coordinators:**
22 | - To manage student, teachers and users
23 | - To create class rooms, assigning students, teachers
24 |
25 | ## Entities:
26 |
27 | - Student
28 | - Teacher
29 | - Coordinators
30 | - Class Rooms:
31 | - Number
32 | - Level
33 | - Morning / afternoon
34 | - Many Students
35 | - Many Teachers
36 | - Classes
37 | - Class Room
38 | - Date
39 | - Lessons
40 | - Videos
41 | - Documents
42 | - Students attendances < Class Room
43 | - Tests
44 | - Class room
45 | - Date
46 | - Type
47 | - Grade test for each students < Class room
48 | - Students attendance on the test < Class Room
49 |
50 | ## Running local server
51 |
52 | ### Requeriments
53 |
54 | - Python 3
55 | - Django 2
56 |
57 | ### Install VirtualEnv
58 |
59 | ```sh
60 | $ virtualenv --python=python3 venv
61 | $ source venv/bin/Activate
62 | ```
63 |
64 | ### For install Requeriments
65 |
66 | ```sh
67 | $ pip install -r req_dev.txt
68 | $ python manage.py migrate
69 | $ python manage.py test
70 | ```
71 |
72 |
73 | ### For run Server
74 | ```sh
75 | $ python manage.py runserver
76 | ```
77 |
78 | ### Access to django-admin
79 |
80 | login: admin@admin.com
81 |
82 | password: admin123
83 |
84 |
85 | ### Warm up development's environment by docker compose
86 | ```sh
87 | sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
88 | ```
89 | *P.s: S.O other than debian look at [here](https://docs.docker.com/compose/install/)*
90 |
91 | ```sh
92 | cd provision/compose
93 | sudo docker-compose build --no-cache
94 | sudo docker-compose up -d
95 | ```
96 |
97 | ### Warm up development's environment by docker
98 | ```sh
99 | sudo apt-get remove docker docker-engine docker.io
100 | sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
101 | curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | sudo apt-key add -
102 | sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable"
103 | sudo apt-get update && sudo apt-get install docker-ce
104 | ```
105 | *P.s: S.O other than debian look at [here](https://docs.docker.com/engine/installation/)*
106 | ```sh
107 | cd provision/docker
108 | sudo docker build -t ec_app -f Dockerfile
109 | sudo docker run -p 8080:8080 -it ec_app
110 | ```
111 |
112 | ## To debug and logs
113 | ```sh
114 | sudo docker exec -it /bin/bash
115 | sudo docker logs -f --tail=100
116 | ```
117 |
118 |
119 |
120 | ## Authors
121 |
122 | * **James Peres** - *Initial work* - [jamesperes](https://github.com/jamesperes)
123 | * **Samuel Sampaio** - *Initial work* - [samukasmk](https://github.com/samukasmk)
124 | * **Werberth Vinícius** - *Initial work* - [werberth](https://github.com/werberth)
125 |
126 | See also the list of [contributors](https://github.com/jamesperes/EC/graphs/contributors) who participated in this project.
127 |
128 | ## License
129 |
130 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
131 |
--------------------------------------------------------------------------------
/ecweb/static/images/logo_star_black.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
21 |
27 |
29 |
33 |
34 |
35 |
39 |
43 |
45 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/ecweb/tests/test_create_user_type_view.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.shortcuts import resolve_url as r
3 | from ecweb.models import (
4 | BasicUser,
5 | Teacher,
6 | Coordinator
7 | )
8 | from ecweb.forms import CreateUserForm
9 |
10 |
11 | class TestCreateUserTypeView(TestCase):
12 | """ Testing createuser system """
13 |
14 | def setUp(self):
15 | user = BasicUser.objects.create_superuser(
16 | username='admin',
17 | password='1234abc',
18 | email="admin_test@admin.com"
19 | )
20 | Coordinator.objects.create(user=user)
21 | self.data_coordinator = {
22 | 'first_name': 'admin',
23 | 'last_name': 'test',
24 | 'email': 'admin_test2@test.com',
25 | 'password': '123456ab',
26 | 'confirm_password': '123456ab'
27 | }
28 | self.data_teacher = {
29 | 'first_name': 'admin2',
30 | 'last_name': 'test2',
31 | 'email': 'admin_test2@test.com',
32 | 'password': '123456ab',
33 | 'confirm_password': '123456ab'
34 | }
35 | self.url_coordinator = r('create-user', 'coordinator')
36 | self.url_teacher = r('create-user', 'teacher')
37 |
38 | self.client.login(
39 | username='admin_test@admin.com',
40 | password='1234abc'
41 | )
42 |
43 | self.resp_teacher = self.client.get(
44 | self.url_teacher
45 | )
46 |
47 | self.resp_coordinator = self.client.get(
48 | self.url_coordinator
49 | )
50 |
51 | def test_response_status_with_type_coordinator(self):
52 | self.assertEqual(200, self.resp_coordinator.status_code)
53 |
54 | def test_response_status_with_type_teacher(self):
55 | self.assertEqual(200, self.resp_teacher.status_code)
56 |
57 | def test_template_used(self):
58 | self.assertTemplateUsed(
59 | self.resp_coordinator, 'registration/create_user.html')
60 | self.assertTemplateUsed(
61 | self.resp_teacher, 'registration/create_user.html')
62 |
63 | def test_forms_used(self):
64 | coordinatorform = self.resp_coordinator.context['form']
65 | teacherform = self.resp_teacher.context['form']
66 | self.assertIsInstance(coordinatorform, CreateUserForm)
67 | self.assertIsInstance(teacherform, CreateUserForm)
68 |
69 | def test_create_object_type_coordinator(self):
70 | self._test_create_object(
71 | data=self.data_coordinator,
72 | url=self.url_coordinator,
73 | model=Coordinator,
74 | )
75 |
76 | def test_create_object_type_teacher(self):
77 | self._test_create_object(
78 | data=self.data_teacher,
79 | url=self.url_teacher,
80 | model=Teacher,
81 | )
82 |
83 | def test_form_password_validation_on_teacher_post(self):
84 | self._test_password_validation(
85 | data=self.data_teacher,
86 | url=self.url_teacher
87 | )
88 |
89 | def test_form_password_validation_on_coordinator_post(self):
90 | self._test_password_validation(
91 | data=self.data_coordinator,
92 | url=self.url_coordinator
93 | )
94 |
95 | def _test_password_validation(self, data, url):
96 | data['confirm_password'] = '789010abc'
97 | response = self.client.post(url, data)
98 | form = response.context['form']
99 | self.assertTrue(form.errors)
100 | self.assertIn("Passwords don't match", form.errors['confirm_password'])
101 |
102 | def _test_create_object(self, data, model, url):
103 | response = self.client.post(url, data)
104 | instance = model.objects.get(
105 | user__email=data['email']
106 | )
107 | self.assertIsInstance(instance, model)
108 | self.assertRedirects(response, r('home_dashboard'))
109 |
--------------------------------------------------------------------------------
/EC/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for EC project.
3 |
4 | Generated by 'django-admin startproject' using Django 1.11.7.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.11/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.11/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 | MEDIA_ROOT = BASE_DIR + '/media'
19 |
20 | MEDIA_URL = '/media/'
21 |
22 | # Quick-start development settings - unsuitable for production
23 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
24 |
25 | # SECURITY WARNING: keep the secret key used in production secret!
26 | SECRET_KEY = 'yt(%4d^o0^ys9_^v9j45qqu5cb)=c#i0_y$w&t+so*^km@#e_c'
27 |
28 | # SECURITY WARNING: don't run with debug turned on in production!
29 | DEBUG = True
30 |
31 | ALLOWED_HOSTS = []
32 |
33 |
34 | # Application definition
35 |
36 | INSTALLED_APPS = [
37 | 'django.contrib.admin',
38 | 'django.contrib.auth',
39 | 'django.contrib.contenttypes',
40 | 'django.contrib.sessions',
41 | 'django.contrib.messages',
42 | 'django.contrib.staticfiles',
43 | 'ecweb'
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 | ]
55 |
56 | ROOT_URLCONF = 'EC.urls'
57 |
58 | TEMPLATES = [
59 | {
60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
61 | 'DIRS': [os.path.join(BASE_DIR, 'templates')],
62 | 'APP_DIRS': True,
63 | 'OPTIONS': {
64 | 'context_processors': [
65 | 'django.template.context_processors.debug',
66 | 'django.template.context_processors.request',
67 | 'django.contrib.auth.context_processors.auth',
68 | 'django.contrib.messages.context_processors.messages',
69 | ],
70 | },
71 | },
72 | ]
73 |
74 | WSGI_APPLICATION = 'EC.wsgi.application'
75 |
76 |
77 | # Database
78 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
79 |
80 | DATABASES = {
81 | 'default': {
82 | 'ENGINE': 'django.db.backends.sqlite3',
83 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
84 | }
85 | }
86 |
87 |
88 | # Password validation
89 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
90 |
91 | AUTH_PASSWORD_VALIDATORS = [
92 | {
93 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
94 | },
95 | {
96 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
97 | },
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
100 | },
101 | {
102 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
103 | },
104 | ]
105 |
106 |
107 |
108 | # Internationalization
109 | # https://docs.djangoproject.com/en/1.11/topics/i18n/
110 |
111 | LANGUAGE_CODE = 'en-us'
112 |
113 | TIME_ZONE = 'UTC'
114 |
115 | USE_I18N = True
116 |
117 | USE_L10N = True
118 |
119 | USE_TZ = True
120 |
121 |
122 | # Static files (CSS, JavaScript, Images)
123 | # https://docs.djangoproject.com/en/1.11/howto/static-files/
124 |
125 | STATIC_URL = '/static/'
126 |
127 | LOGIN_REDIRECT_URL = '/board'
128 |
129 | AUTH_USER_MODEL = 'ecweb.BasicUser'
130 |
131 | if os.environ.get("DOCKER_DEVELOPMENT"):
132 | try:
133 | from EC.docker_settings import *
134 | except ImportError:
135 | pass
136 |
137 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
--------------------------------------------------------------------------------
/ecweb/static/images/logo_star.svg:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
19 |
20 |
26 |
28 |
32 |
34 |
35 |
39 |
43 |
46 |
49 |
50 |
--------------------------------------------------------------------------------
/ecweb/static/css/libs/perfect-scrollbar.min.css:
--------------------------------------------------------------------------------
1 | /* perfect-scrollbar v0.7.1 */
2 | .ps{-ms-touch-action:auto;touch-action:auto;overflow:hidden !important;-ms-overflow-style:none}@supports (-ms-overflow-style: none){.ps{overflow:auto !important}}@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none){.ps{overflow:auto !important}}.ps.ps--active-x>.ps__scrollbar-x-rail,.ps.ps--active-y>.ps__scrollbar-y-rail{display:block;background-color:transparent}.ps.ps--in-scrolling.ps--x>.ps__scrollbar-x-rail{background-color:#eee;opacity:.9}.ps.ps--in-scrolling.ps--x>.ps__scrollbar-x-rail>.ps__scrollbar-x{background-color:#999;height:11px}.ps.ps--in-scrolling.ps--y>.ps__scrollbar-y-rail{background-color:#eee;opacity:.9}.ps.ps--in-scrolling.ps--y>.ps__scrollbar-y-rail>.ps__scrollbar-y{background-color:#999;width:11px}.ps>.ps__scrollbar-x-rail{display:none;position:absolute;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;bottom:0px;height:15px}.ps>.ps__scrollbar-x-rail>.ps__scrollbar-x{position:absolute;background-color:#aaa;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;-moz-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;-o-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;bottom:2px;height:6px}.ps>.ps__scrollbar-x-rail:hover>.ps__scrollbar-x,.ps>.ps__scrollbar-x-rail:active>.ps__scrollbar-x{height:11px}.ps>.ps__scrollbar-y-rail{display:none;position:absolute;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;right:0;width:15px}.ps>.ps__scrollbar-y-rail>.ps__scrollbar-y{position:absolute;background-color:#aaa;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;-moz-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;-o-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;right:2px;width:6px}.ps>.ps__scrollbar-y-rail:hover>.ps__scrollbar-y,.ps>.ps__scrollbar-y-rail:active>.ps__scrollbar-y{width:11px}.ps:hover.ps--in-scrolling.ps--x>.ps__scrollbar-x-rail{background-color:#eee;opacity:.9}.ps:hover.ps--in-scrolling.ps--x>.ps__scrollbar-x-rail>.ps__scrollbar-x{background-color:#999;height:11px}.ps:hover.ps--in-scrolling.ps--y>.ps__scrollbar-y-rail{background-color:#eee;opacity:.9}.ps:hover.ps--in-scrolling.ps--y>.ps__scrollbar-y-rail>.ps__scrollbar-y{background-color:#999;width:11px}.ps:hover>.ps__scrollbar-x-rail,.ps:hover>.ps__scrollbar-y-rail{opacity:.6}.ps:hover>.ps__scrollbar-x-rail:hover{background-color:#eee;opacity:.9}.ps:hover>.ps__scrollbar-x-rail:hover>.ps__scrollbar-x{background-color:#999}.ps:hover>.ps__scrollbar-y-rail:hover{background-color:#eee;opacity:.9}.ps:hover>.ps__scrollbar-y-rail:hover>.ps__scrollbar-y{background-color:#999}
3 |
--------------------------------------------------------------------------------
/ecweb/static/js/chart.js:
--------------------------------------------------------------------------------
1 | $(function () {
2 | /* ChartJS
3 | * -------
4 | * Data and config for chartjs
5 | */
6 | var data = {
7 | labels: ["Red", "teste", "Yellow", "Green", "Purple", "Orange"],
8 | datasets: [{
9 | label: '# of Votes',
10 | data: [12, 21, 3, 5, 2, 3],
11 | backgroundColor: [
12 | 'rgba(255, 99, 132, 0.2)',
13 | 'rgba(54, 162, 235, 0.2)',
14 | 'rgba(255, 206, 86, 0.2)',
15 | 'rgba(75, 192, 192, 0.2)',
16 | 'rgba(153, 102, 255, 0.2)',
17 | 'rgba(255, 159, 64, 0.2)'
18 | ],
19 | borderColor: [
20 | 'rgba(255,99,132,1)',
21 | 'rgba(54, 162, 235, 1)',
22 | 'rgba(255, 206, 86, 1)',
23 | 'rgba(75, 192, 192, 1)',
24 | 'rgba(153, 102, 255, 1)',
25 | 'rgba(255, 159, 64, 1)'
26 | ],
27 | borderWidth: 1
28 | }]
29 | };
30 | var options = {
31 | scales: {
32 | yAxes: [{
33 | ticks: {
34 | beginAtZero:true
35 | }
36 | }]
37 | }
38 | };
39 | var doughnutData ={
40 | datasets: [{
41 | data: [30, 40, 30],
42 | backgroundColor: ['#d9534f','#36a2eb','#5cb85c']
43 | }],
44 |
45 | // These labels appear in the legend and in the tooltips when hovering different arcs
46 | labels: [
47 | 'Red',
48 | 'Blue',
49 | 'Green'
50 | ]
51 | };
52 | var doughnutOptions = {
53 | responsive: true,
54 | animation: {
55 | animateScale: true,
56 | animateRotate: true
57 | }
58 | };
59 |
60 | var areaData = {
61 | labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
62 | datasets: [{
63 | label: '# of Votes',
64 | data: [12, 19, 3, 5, 2, 3],
65 | backgroundColor: [
66 | 'rgba(255, 99, 132, 0.2)',
67 | 'rgba(54, 162, 235, 0.2)',
68 | 'rgba(255, 206, 86, 0.2)',
69 | 'rgba(75, 192, 192, 0.2)',
70 | 'rgba(153, 102, 255, 0.2)',
71 | 'rgba(255, 159, 64, 0.2)'
72 | ],
73 | borderColor: [
74 | 'rgba(255,99,132,1)',
75 | 'rgba(54, 162, 235, 1)',
76 | 'rgba(255, 206, 86, 1)',
77 | 'rgba(75, 192, 192, 1)',
78 | 'rgba(153, 102, 255, 1)',
79 | 'rgba(255, 159, 64, 1)'
80 | ],
81 | borderWidth: 1,
82 | fill: 'origin', // 0: fill to 'origin'
83 | fill: '+2', // 1: fill to dataset 3
84 | fill: 1, // 2: fill to dataset 1
85 | fill: false, // 3: no fill
86 | fill: '-2' // 4: fill to dataset 2
87 | }]
88 | };
89 |
90 | var areaOptions = {
91 | plugins: {
92 | filler: {
93 | propagate: true
94 | }
95 | }
96 | }
97 | // Get context with jQuery - using jQuery's .get() method.
98 | if($("#barChart").length) {
99 | var barChartCanvas = $("#barChart").get(0).getContext("2d");
100 | // This will get the first returned node in the jQuery collection.
101 | var barChart = new Chart(barChartCanvas, {
102 | type: 'bar',
103 | data: data,
104 | options: options
105 | });
106 | }
107 |
108 | if($("#lineChart").length) {
109 | var lineChartCanvas = $("#lineChart").get(0).getContext("2d");
110 | var lineChart = new Chart(lineChartCanvas, {
111 | type: 'line',
112 | data: data,
113 | options: options
114 | });
115 | }
116 |
117 | if($("#doughnutChart").length) {
118 | var doughnutChartCanvas = $("#doughnutChart").get(0).getContext("2d");
119 | var doughnutChart = new Chart(doughnutChartCanvas, {
120 | type: 'doughnut',
121 | data: doughnutData,
122 | options: doughnutOptions
123 | });
124 | }
125 |
126 | if($("#areaChart").length) {
127 | var areaChartCanvas = $("#areaChart").get(0).getContext("2d");
128 | var areaChart = new Chart(areaChartCanvas, {
129 | type:'line',
130 | data: areaData,
131 | options: areaOptions
132 | });
133 | }
134 |
135 | });
136 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/student.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 |
6 |
7 |
8 | {% endblock %}
9 |
10 |
11 |
12 | {% block js %}
13 |
14 |
15 |
71 |
72 |
100 |
101 |
102 | {% endblock %}
103 |
104 | {% block content %}
105 |
106 |
107 |
Student Dashboard
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
122 |
123 |
124 |
125 |
126 |
127 |
133 |
134 |
135 |
136 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
{{current_user.first_name}}
157 | {% if current_user.is_staff == True%}
158 |
Staff
159 | {% else %}
160 |
Student
161 | {% endif %}
162 |
163 |
164 | Change password
165 |
166 |
167 |
168 | {% endblock %}
169 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/baseadmin.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 |
7 |
8 | EC Admin
9 |
10 |
11 |
12 |
13 | {% block head %}{% endblock %}
14 |
15 |
16 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | {% block js %}{% endblock %}
120 |
121 |
122 |
--------------------------------------------------------------------------------
/ecweb/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.utils.translation import ugettext_lazy as _
3 | from .utils.li import (
4 | level_choices,
5 | type_list,
6 | test_choices,
7 | classroom_turns_choices
8 | )
9 | from django.utils.text import slugify
10 | from django.urls import reverse
11 | from django.contrib.auth.models import Permission
12 |
13 |
14 | from django.contrib.auth.models import AbstractUser
15 |
16 | import geocoder
17 |
18 |
19 | class BasicUser(AbstractUser):
20 | email = models.EmailField(
21 | _('email address'), null=False, blank=False, unique=True)
22 |
23 | username = models.CharField(
24 | _('username'),
25 | max_length=150,
26 | blank=True,
27 | null=True)
28 |
29 | avatar = models.ImageField(
30 | upload_to='avatars/profiles',
31 | default='avatars/user_default.png',
32 | blank=True)
33 |
34 | USERNAME_FIELD = 'email'
35 | REQUIRED_FIELDS = []
36 |
37 |
38 | class Student(models.Model):
39 | user = models.OneToOneField(BasicUser, on_delete=models.CASCADE)
40 | cod = models.IntegerField(blank=False, null=False)
41 | type_of_course = models.CharField(
42 | max_length=30, choices=type_list, blank=False, null=False)
43 |
44 | def __str__(self):
45 | return self.user.first_name
46 |
47 |
48 | class Teacher(models.Model):
49 | user = models.OneToOneField(BasicUser, on_delete=models.CASCADE)
50 |
51 | def save(self, *args, **kwargs):
52 | self.user.is_staff = True
53 | self.user.save()
54 |
55 | super(Teacher, self).save(*args, **kwargs)
56 |
57 | def __str__(self):
58 | return self.user.first_name
59 |
60 |
61 | class Coordinator(models.Model):
62 | user = models.OneToOneField(BasicUser, on_delete=models.CASCADE)
63 |
64 | class Meta:
65 | permissions = (
66 | ("view_all_classrooms", "Can view all classrooms"),
67 | )
68 |
69 | def save(self, *args, **kwargs):
70 | permission = Permission.objects.get(codename='view_all_classrooms')
71 |
72 | self.user.user_permissions.add(permission)
73 | self.user.is_staff = True
74 | self.user.save()
75 |
76 | super(Coordinator, self).save(*args, **kwargs)
77 |
78 | def __str__(self):
79 | return self.user.first_name
80 |
81 |
82 | class ClassRoom(models.Model):
83 | number_class = models.PositiveIntegerField(blank=True)
84 | level = models.CharField(max_length=30, choices=level_choices, blank=True)
85 | students = models.ManyToManyField(Student)
86 | teachers = models.ManyToManyField(Teacher)
87 | turn = models.CharField(max_length=50, choices=classroom_turns_choices)
88 | slug = models.SlugField(null=True, blank=True)
89 | is_active = models.BooleanField(default=True)
90 |
91 | def save(self, *args, **kwargs):
92 | self.slug = slugify(self.__str__())
93 | super(ClassRoom, self).save(*args, **kwargs)
94 |
95 | def get_absolute_url(self):
96 | return reverse('classroom_detail_view', kwargs={'slug': self.slug})
97 |
98 | def __str__(self):
99 | return '{}: {} - {}'.format(self.level, self.turn, self.number_class)
100 |
101 |
102 | class Youtube(models.Model):
103 | description = models.CharField(max_length=50, blank=True)
104 | link = models.CharField(max_length=255, blank=True)
105 |
106 | def __str__(self):
107 | return self.description
108 |
109 |
110 | class PdfFile(models.Model):
111 | description = models.CharField(max_length=50, blank=True)
112 | file = models.FileField(upload_to="media/", blank=True)
113 |
114 | def __str__(self):
115 | return self.description
116 |
117 |
118 | class Class(models.Model):
119 | classroom = models.ForeignKey(ClassRoom, on_delete=models.CASCADE)
120 | date = models.DateField()
121 | lesson = models.TextField()
122 | videos = models.ManyToManyField(Youtube, blank=True)
123 | files = models.ManyToManyField(PdfFile, blank=True)
124 | attendances = models.ManyToManyField(Student, blank=True)
125 |
126 | def __str__(self):
127 | return "{}: {}".format(self.date, self.lesson[:30])
128 |
129 |
130 | class Test(models.Model):
131 | classroom = models.ForeignKey(ClassRoom, on_delete=models.CASCADE)
132 | date = models.DateField()
133 | type = models.CharField(max_length=50, choices=test_choices)
134 |
135 | def __str__(self):
136 | return "{}: {}".format(self.date, self.lesson[:30])
137 |
138 |
139 | class TestGrade(models.Model):
140 | test_event = models.ForeignKey(Test, on_delete=models.CASCADE)
141 | student = models.ForeignKey(Student, on_delete=models.CASCADE)
142 | grade = models.FloatField()
143 |
144 |
145 | class Event(models.Model):
146 | description = models.TextField(max_length=500)
147 | title = models.CharField(max_length=50)
148 | start_event = models.DateTimeField()
149 | end_event = models.DateTimeField(blank=True)
150 | address = models.CharField(max_length=250)
151 | neighborhood = models.CharField(max_length=250)
152 | city = models.CharField(max_length=250)
153 | local_lat = models.CharField(null=True, blank=True, max_length=250)
154 | local_long = models.CharField(null=True, blank=True, max_length=250)
155 | limit = models.IntegerField(blank=True)
156 | teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE, blank=True)
157 |
158 | def __str__(self):
159 | return self.title
160 |
161 | def geocoder(self):
162 | end_str = '{},{},{}'.format(self.address, self.neighborhood, self.city)
163 | g = geocoder.google(end_str)
164 | return [g.lat, g.lng]
165 |
166 | def update_geocode(self):
167 | try:
168 | self.local_lat, self.local_long = self.geocoder()
169 | except:
170 | self.local_lat, self.local_long = [None, None]
171 |
172 | def save(self, *args, **kwargs):
173 | self.update_geocode()
174 | super(Event, self).save(*args, **kwargs)
175 |
--------------------------------------------------------------------------------
/ecweb/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0 on 2017-12-24 05:13
2 |
3 | from django.conf import settings
4 | import django.contrib.auth.models
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 | import django.utils.timezone
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | initial = True
13 |
14 | dependencies = [
15 | ('auth', '0009_alter_user_last_name_max_length'),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='BasicUser',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('password', models.CharField(max_length=128, verbose_name='password')),
24 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
25 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
26 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
27 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
28 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
29 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
30 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
31 | ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')),
32 | ('username', models.CharField(blank=True, max_length=150, null=True, verbose_name='username')),
33 | ('avatar', models.ImageField(blank=True, default='avatars/user_default.png', upload_to='avatars/profiles')),
34 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
35 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
36 | ],
37 | options={
38 | 'verbose_name': 'user',
39 | 'verbose_name_plural': 'users',
40 | 'abstract': False,
41 | },
42 | managers=[
43 | ('objects', django.contrib.auth.models.UserManager()),
44 | ],
45 | ),
46 | migrations.CreateModel(
47 | name='Class',
48 | fields=[
49 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
50 | ('date', models.DateField()),
51 | ('lesson', models.TextField()),
52 | ],
53 | ),
54 | migrations.CreateModel(
55 | name='ClassRoom',
56 | fields=[
57 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
58 | ('number_class', models.IntegerField(blank=True)),
59 | ('level', models.CharField(blank=True, choices=[('Beginner', 'Beginner'), ('Elementary', 'Elementary')], max_length=30)),
60 | ],
61 | ),
62 | migrations.CreateModel(
63 | name='Coordinator',
64 | fields=[
65 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
66 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
67 | ],
68 | ),
69 | migrations.CreateModel(
70 | name='PdfFile',
71 | fields=[
72 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
73 | ('description', models.CharField(blank=True, max_length=50)),
74 | ('file', models.FileField(blank=True, upload_to='media/')),
75 | ],
76 | ),
77 | migrations.CreateModel(
78 | name='Student',
79 | fields=[
80 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
81 | ('cod', models.IntegerField()),
82 | ('type_of_course', models.CharField(choices=[('1-month', '1-month'), ('6-month', '6-month')], max_length=30)),
83 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
84 | ],
85 | ),
86 | migrations.CreateModel(
87 | name='Teacher',
88 | fields=[
89 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
90 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
91 | ],
92 | ),
93 | migrations.CreateModel(
94 | name='Test',
95 | fields=[
96 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
97 | ('date', models.DateField()),
98 | ('type', models.CharField(choices=[('listening', 'Listening'), ('reading', 'Reading')], max_length=50)),
99 | ('classroom', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecweb.ClassRoom')),
100 | ],
101 | ),
102 | migrations.CreateModel(
103 | name='TestGrade',
104 | fields=[
105 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
106 | ('grade', models.FloatField()),
107 | ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecweb.Student')),
108 | ('test_event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecweb.Test')),
109 | ],
110 | ),
111 | migrations.CreateModel(
112 | name='Youtube',
113 | fields=[
114 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
115 | ('description', models.CharField(blank=True, max_length=50)),
116 | ('link', models.CharField(blank=True, max_length=255)),
117 | ],
118 | ),
119 | migrations.AddField(
120 | model_name='classroom',
121 | name='students',
122 | field=models.ManyToManyField(to='ecweb.Student'),
123 | ),
124 | migrations.AddField(
125 | model_name='classroom',
126 | name='teachers',
127 | field=models.ManyToManyField(to='ecweb.Teacher'),
128 | ),
129 | migrations.AddField(
130 | model_name='class',
131 | name='attendances',
132 | field=models.ManyToManyField(blank=True, to='ecweb.Student'),
133 | ),
134 | migrations.AddField(
135 | model_name='class',
136 | name='classroom',
137 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ecweb.ClassRoom'),
138 | ),
139 | migrations.AddField(
140 | model_name='class',
141 | name='files',
142 | field=models.ManyToManyField(blank=True, to='ecweb.PdfFile'),
143 | ),
144 | migrations.AddField(
145 | model_name='class',
146 | name='videos',
147 | field=models.ManyToManyField(blank=True, to='ecweb.Youtube'),
148 | ),
149 | ]
150 |
--------------------------------------------------------------------------------
/ecweb/templates/ecweb/student-dashboard.html:
--------------------------------------------------------------------------------
1 | {% extends 'ecweb/baseadmin.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 |
6 |
7 | {% endblock %}
8 |
9 | {% block js %}
10 |
11 |
45 | {% endblock %}
46 |
47 | {% block content %}
48 |
49 |
50 |
Student Dashboard
51 |
52 |
53 |
54 |
55 |
{{ days_cont_int }} Days
56 |
Days studies
57 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
10 Days
68 |
Attendance
69 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
Next Event
80 |
81 |
82 |
83 | Conversetion Class
84 | 20/12 - 17h
85 | Room 02
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
Progress Grade
99 |
100 |
101 |
102 |
103 |
157 |
158 |
159 |
166 |
167 |
168 |
169 |
170 | {% endblock %}
171 |
172 | {% block user_title %}
173 | Student
174 | {% endblock %}
175 |
--------------------------------------------------------------------------------
/ecweb/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render, redirect, get_object_or_404
2 | from django.http import HttpResponseRedirect, Http404
3 | from django.urls import reverse as r
4 | from django.contrib.auth.decorators import login_required
5 | from datetime import date
6 | from django.urls import reverse_lazy
7 | from django.contrib.auth.mixins import PermissionRequiredMixin
8 | from django.contrib.auth.mixins import LoginRequiredMixin
9 | from django.contrib.auth import logout
10 | from django.contrib import messages
11 | from django.views.generic import CreateView
12 | from django.views.generic.list import ListView
13 | from django.views.generic.detail import DetailView
14 | from django.views.generic.edit import UpdateView, DeleteView
15 | from django.contrib import messages
16 | from django.contrib.auth import update_session_auth_hash
17 | from django.contrib.auth.forms import PasswordChangeForm
18 |
19 | from django.contrib.auth import logout
20 | from .forms import PhotoForm, AttendanceForm, CreateUserForm, StudentForm
21 | from .models import ClassRoom, Teacher, Student, Class, BasicUser, Coordinator,Event
22 |
23 |
24 | @login_required
25 | def create_user_type_view(request, user_type):
26 | template_name = 'registration/create_user.html'
27 | types = ('coordinator', 'teacher')
28 | current_user = request.user
29 | if user_type not in types:
30 | raise Http404
31 | if request.method == 'POST':
32 | form = CreateUserForm(request.POST)
33 | if form.is_valid():
34 | user = form.save()
35 | if user_type == 'coordinator':
36 | Coordinator.objects.create(user=user)
37 | elif user_type == 'teacher':
38 | Teacher.objects.create(user=user)
39 | return redirect(r('home_dashboard'))
40 | else:
41 | form = CreateUserForm()
42 | context = {'form': form,
43 | 'current_user': current_user}
44 | return render(request, template_name, context)
45 |
46 |
47 | @login_required
48 | def create_student_view(request):
49 | template_name = 'registration/create_student.html'
50 | current_user = request.user
51 | if request.method == 'POST':
52 | userform = CreateUserForm(request.POST)
53 | studentform = StudentForm(request.POST)
54 | if userform.is_valid() and studentform.is_valid():
55 | user = userform.save()
56 | student = studentform.save(commit=False)
57 | student.user = user
58 | student.save()
59 | return redirect(r('home_dashboard'))
60 | else:
61 | userform = CreateUserForm()
62 | studentform = StudentForm()
63 | context = {
64 | 'userform': userform,
65 | 'studentform': studentform,
66 | 'current_user': current_user
67 | }
68 | return render(request, template_name, context)
69 |
70 |
71 | @login_required
72 | def home_dashboard(request):
73 | current_user = request.user
74 | user = Coordinator.objects.filter(user__id=current_user.id)
75 |
76 | if user:
77 | return render(request, 'ecweb/coordinator-dashboard.html',
78 | {'coordinator': user.first(),
79 | 'current_user': current_user})
80 |
81 | user = Teacher.objects.filter(user__id=current_user.id)
82 | if user:
83 | return render(request, 'ecweb/teacher-dashboard.html',
84 | {'teacher': user.first(),
85 | 'current_user': current_user})
86 |
87 | user = Student.objects.filter(user__id=current_user.id)
88 | if user:
89 | date_start = current_user.date_joined.date()
90 | days_con = date.today() - date_start
91 | days_cont_int = int(days_con.days)
92 | if user[0].type_of_course == "1-month":
93 | count_day = 30 - days_cont_int
94 | percent = int(-100.0 * (count_day / 30))
95 |
96 | else:
97 | count_day = 30 * 6 - days_cont_int
98 | percent = int(100.0 * (count_day / (30 * 6)))
99 |
100 | return render(request, 'ecweb/student-dashboard.html',
101 | {'student': user.first(),
102 | 'current_user': current_user,
103 | 'days_cont_int': days_cont_int})
104 |
105 | raise Http404
106 |
107 |
108 | @login_required
109 | def user_detail(request):
110 | current_user = request.user
111 | insta = get_object_or_404(BasicUser, pk=int(current_user.id))
112 |
113 | if request.method == 'POST':
114 | if "change_password" in request.POST:
115 |
116 | form = PasswordChangeForm(request.user, request.POST)
117 | if form.is_valid():
118 | user = form.save()
119 | update_session_auth_hash(request, user) # Important!
120 | messages.success(
121 | request, 'Your password was successfully updated!')
122 | return redirect('user_detail')
123 | else:
124 | form = PasswordChangeForm(request.user)
125 | return render(request, 'ecweb/student.html', {
126 | 'form': form
127 | })
128 |
129 | else:
130 |
131 | form = PhotoForm(request.POST, request.FILES, instance=insta)
132 | if form.is_valid():
133 | profil = form.save()
134 | profil.user = current_user
135 | profil.save()
136 |
137 | return redirect('user_detail')
138 | else:
139 | form = PhotoForm()
140 | return render(request, 'ecweb/student.html',
141 | {'current_user': current_user, 'form': form})
142 |
143 |
144 | @login_required
145 | def change_password(request):
146 | if request.method == 'POST':
147 | form = PasswordChangeForm(request.user, request.POST)
148 | if form.is_valid():
149 | user = form.save()
150 | update_session_auth_hash(request, user) # Important!
151 | messages.success(
152 | request, 'Your password was successfully updated!')
153 | return redirect('change_password')
154 | else:
155 | messages.error(request, 'Please correct the error below.')
156 | else:
157 | form = PasswordChangeForm(request.user)
158 | return render(request, 'registration/change-password.html', {
159 | 'form': form
160 | })
161 |
162 |
163 | def logout_view(request):
164 | logout(request)
165 | return render(request, 'registration/logout.html')
166 |
167 |
168 | @login_required
169 | def calendar_view(request):
170 | events = Calendar.objects.all()
171 | return render(request, 'ecweb/calendar.html', {'events': events})
172 |
173 |
174 | class ClassRoomListView(LoginRequiredMixin, ListView):
175 |
176 | model = ClassRoom
177 | template_name = 'ecweb/classroom/classroom.html'
178 |
179 | def get_queryset(self):
180 | current_user = self.request.user
181 |
182 | queryset = super(ClassRoomListView, self).get_queryset()
183 |
184 | if current_user.is_staff:
185 | teacher = Teacher.objects.filter(user=current_user.id)
186 |
187 | if teacher.exists():
188 | queryset = ClassRoom.objects.filter(
189 | teachers=teacher.first().id,
190 | is_active=True
191 | )
192 |
193 | else:
194 | queryset = ClassRoom.objects.filter(is_active=True)
195 |
196 | else:
197 | student = Student.objects.get(user=current_user.id)
198 | queryset = ClassRoom.objects.filter(
199 | students=student.id, is_active=True)
200 |
201 | return queryset
202 |
203 |
204 | class ClassRoomDetailView(LoginRequiredMixin, DetailView):
205 | model = ClassRoom
206 | template_name = 'ecweb/classroom/detail_classroom.html'
207 |
208 | def dispatch(self, request, *args, **kwargs):
209 | classroom = self.get_object()
210 | user = request.user
211 |
212 | is_coordinator = Coordinator.objects.filter(user=user).exists()
213 | student_in_classroom = classroom.students.all().filter(user=user).exists()
214 | teacher_in_classroom = classroom.teachers.all().filter(user=user).exists()
215 |
216 | if not (student_in_classroom or teacher_in_classroom or is_coordinator):
217 | return redirect('classroom_view')
218 |
219 | if not classroom.is_active:
220 | return redirect('classroom_view')
221 |
222 | return super(ClassRoomDetailView, self).dispatch(request, *args, **kwargs)
223 |
224 |
225 | class ClassRoomCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
226 | model = ClassRoom
227 | template_name = 'ecweb/classroom/create_classroom.html'
228 | success_url = reverse_lazy('classroom_view')
229 | permission_required = 'ecweb.view_all_classrooms'
230 | fields = (
231 | 'number_class',
232 | 'level',
233 | 'students',
234 | 'teachers',
235 | 'turn'
236 | )
237 |
238 | def form_valid(self, form):
239 |
240 | self.object = form.save(commit=False)
241 |
242 | classroom_exists = ClassRoom.objects.filter(
243 | number_class=self.object.number_class,
244 | level=self.object.level,
245 | turn=self.object.turn
246 | ).exists()
247 |
248 | if classroom_exists:
249 | messages.error(
250 | self.request,
251 | 'This classroom already exists.'
252 | )
253 | return super(ClassRoomCreateView, self).form_invalid(form)
254 |
255 | return super(ClassRoomCreateView, self).form_valid(form)
256 |
257 |
258 | class ClassRoomUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
259 | model = ClassRoom
260 | template_name = 'ecweb/classroom/update_classroom.html'
261 | permission_required = 'ecweb.view_all_classrooms'
262 | fields = (
263 | 'number_class',
264 | 'level',
265 | 'students',
266 | 'teachers',
267 | 'turn'
268 | )
269 |
270 | def form_valid(self, form):
271 |
272 | self.object = form.save(commit=False)
273 | form_changed = form.has_changed()
274 |
275 | if form_changed:
276 | classroom_exists = ClassRoom.objects.filter(
277 | number_class=self.object.number_class,
278 | level=self.object.level,
279 | turn=self.object.turn
280 | ).exists()
281 |
282 | if classroom_exists:
283 | messages.error(
284 | self.request,
285 | 'This classroom already exists.'
286 | )
287 | return super(ClassRoomUpdateView, self).form_invalid(form)
288 | else:
289 | messages.success(
290 | self.request,
291 | 'Classroom successfully updated'
292 | )
293 |
294 | return super(ClassRoomUpdateView, self).form_valid(form)
295 |
296 | messages.info(
297 | self.request,
298 | 'The classroom does changed.'
299 | )
300 | return super(ClassRoomUpdateView, self).form_valid(form)
301 |
302 |
303 | class ClassRoomDeactivateView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
304 | model = ClassRoom
305 | template_name = 'ecweb/classroom/classroom_confirm_delete.html'
306 | success_url = reverse_lazy('classroom_view')
307 | permission_required = 'ecweb.view_all_classrooms'
308 |
309 | def delete(self, request, *args, **kwargs):
310 | self.object = self.get_object()
311 | success_url = self.get_success_url()
312 | self.object.is_active = False
313 | self.object.save()
314 |
315 | return HttpResponseRedirect(success_url)
316 |
317 |
318 | @login_required
319 | def list_classes_view(request, class_room_id):
320 |
321 | current_user = request.user
322 | user = Coordinator.objects.filter(user__id=current_user.id)
323 | if user:
324 | all_classes = Class.objects.filter(classroom=class_room_id)
325 |
326 | user = Teacher.objects.filter(user__id=current_user.id)
327 | if user:
328 | teacher = Teacher.objects.get(user=current_user.id)
329 | all_classes = Class.objects.filter(classroom__teachers=teacher.id)
330 |
331 | user = Student.objects.filter(user__id=current_user.id)
332 | if user:
333 | student = Student.objects.get(user=current_user.id)
334 | all_classes = Class.objects.filter(classroom__students=student.id)
335 |
336 | context = {
337 | 'all_classes': all_classes,
338 | 'current_user': current_user
339 | }
340 | return render(request, 'ecweb/classes.html', context)
341 |
342 |
343 | @login_required
344 | def class_view(request, class_id):
345 | current_user = request.user
346 | class_obj = Class.objects.get(id=class_id)
347 |
348 | choices_student = []
349 | for student in class_obj.classroom.students.all():
350 | student_id = student.id
351 | student_name = '{}, {}'.format(
352 | student.user.last_name, student.user.first_name)
353 | choices_student.append((student_id, student_name))
354 |
355 | if request.method == 'POST':
356 | form = AttendanceForm(request.POST)
357 | form.fields['students'].choices = tuple(choices_student)
358 |
359 | if form.is_valid():
360 | students_to_update = [int(s)
361 | for s in form.cleaned_data['students']]
362 | class_obj.attendances.clear()
363 | class_obj.attendances.add(*students_to_update)
364 |
365 | return HttpResponseRedirect('/class')
366 |
367 | else:
368 | attendanced_students = [s.id for s in class_obj.attendances.all()]
369 |
370 | form = AttendanceForm(
371 | initial={'class_id': class_id, 'students': attendanced_students})
372 | form.fields['students'].choices = tuple(choices_student)
373 |
374 | context = {
375 | 'form': form,
376 | 'current_user': current_user,
377 | 'class_id': class_id,
378 | 'class_obj': class_obj
379 | }
380 | return render(request, 'ecweb/class_attendance.html', context)
381 |
382 |
383 | @login_required
384 | def events_list_view(request):
385 | current_user = request.user
386 | events = Event.objects.all()
387 |
388 | return render(request, 'ecweb/events.html',
389 | {'current_user': current_user,
390 | 'events': events})
--------------------------------------------------------------------------------
/ecweb/static/js/libs/tether.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e(require,exports,module):t.Tether=e()}(this,function(t,e,o){"use strict";function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function n(t){var e=t.getBoundingClientRect(),o={};for(var i in e)o[i]=e[i];if(t.ownerDocument!==document){var r=t.ownerDocument.defaultView.frameElement;if(r){var s=n(r);o.top+=s.top,o.bottom+=s.top,o.left+=s.left,o.right+=s.left}}return o}function r(t){var e=getComputedStyle(t)||{},o=e.position,i=[];if("fixed"===o)return[t];for(var n=t;(n=n.parentNode)&&n&&1===n.nodeType;){var r=void 0;try{r=getComputedStyle(n)}catch(s){}if("undefined"==typeof r||null===r)return i.push(n),i;var a=r,f=a.overflow,l=a.overflowX,h=a.overflowY;/(auto|scroll)/.test(f+h+l)&&("absolute"!==o||["relative","absolute","fixed"].indexOf(r.position)>=0)&&i.push(n)}return i.push(t.ownerDocument.body),t.ownerDocument!==document&&i.push(t.ownerDocument.defaultView),i}function s(){A&&document.body.removeChild(A),A=null}function a(t){var e=void 0;t===document?(e=document,t=document.documentElement):e=t.ownerDocument;var o=e.documentElement,i=n(t),r=P();return i.top-=r.top,i.left-=r.left,"undefined"==typeof i.width&&(i.width=document.body.scrollWidth-i.left-i.right),"undefined"==typeof i.height&&(i.height=document.body.scrollHeight-i.top-i.bottom),i.top=i.top-o.clientTop,i.left=i.left-o.clientLeft,i.right=e.body.clientWidth-i.width-i.left,i.bottom=e.body.clientHeight-i.height-i.top,i}function f(t){return t.offsetParent||document.documentElement}function l(){if(M)return M;var t=document.createElement("div");t.style.width="100%",t.style.height="200px";var e=document.createElement("div");h(e.style,{position:"absolute",top:0,left:0,pointerEvents:"none",visibility:"hidden",width:"200px",height:"150px",overflow:"hidden"}),e.appendChild(t),document.body.appendChild(e);var o=t.offsetWidth;e.style.overflow="scroll";var i=t.offsetWidth;o===i&&(i=e.clientWidth),document.body.removeChild(e);var n=o-i;return M={width:n,height:n}}function h(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],e=[];return Array.prototype.push.apply(e,arguments),e.slice(1).forEach(function(e){if(e)for(var o in e)({}).hasOwnProperty.call(e,o)&&(t[o]=e[o])}),t}function d(t,e){if("undefined"!=typeof t.classList)e.split(" ").forEach(function(e){e.trim()&&t.classList.remove(e)});else{var o=new RegExp("(^| )"+e.split(" ").join("|")+"( |$)","gi"),i=c(t).replace(o," ");g(t,i)}}function p(t,e){if("undefined"!=typeof t.classList)e.split(" ").forEach(function(e){e.trim()&&t.classList.add(e)});else{d(t,e);var o=c(t)+(" "+e);g(t,o)}}function u(t,e){if("undefined"!=typeof t.classList)return t.classList.contains(e);var o=c(t);return new RegExp("(^| )"+e+"( |$)","gi").test(o)}function c(t){return t.className instanceof t.ownerDocument.defaultView.SVGAnimatedString?t.className.baseVal:t.className}function g(t,e){t.setAttribute("class",e)}function m(t,e,o){o.forEach(function(o){e.indexOf(o)===-1&&u(t,o)&&d(t,o)}),e.forEach(function(e){u(t,e)||p(t,e)})}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function v(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function y(t,e){var o=arguments.length<=2||void 0===arguments[2]?1:arguments[2];return t+o>=e&&e>=t-o}function b(){return"undefined"!=typeof performance&&"undefined"!=typeof performance.now?performance.now():+new Date}function w(){for(var t={top:0,left:0},e=arguments.length,o=Array(e),i=0;i1?o-1:0),n=1;n16?(e=Math.min(e-16,250),void(o=setTimeout(n,250))):void("undefined"!=typeof t&&b()-t<10||(null!=o&&(clearTimeout(o),o=null),t=b(),X(),e=b()-t))};"undefined"!=typeof window&&"undefined"!=typeof window.addEventListener&&["resize","scroll","touchmove"].forEach(function(t){window.addEventListener(t,i)})}();var F={center:"center",left:"right",right:"left"},H={middle:"middle",top:"bottom",bottom:"top"},N={top:0,left:0,middle:"50%",center:"50%",bottom:"100%",right:"100%"},U=function(t,e){var o=t.left,i=t.top;return"auto"===o&&(o=F[e.left]),"auto"===i&&(i=H[e.top]),{left:o,top:i}},V=function(t){var e=t.left,o=t.top;return"undefined"!=typeof N[t.left]&&(e=N[t.left]),"undefined"!=typeof N[t.top]&&(o=N[t.top]),{left:e,top:o}},R=function(t){var e=t.split(" "),o=z(e,2),i=o[0],n=o[1];return{top:i,left:n}},q=R,I=function(t){function e(t){var o=this;i(this,e),j(Object.getPrototypeOf(e.prototype),"constructor",this).call(this),this.position=this.position.bind(this),D.push(this),this.history=[],this.setOptions(t,!1),x.modules.forEach(function(t){"undefined"!=typeof t.initialize&&t.initialize.call(o)}),this.position()}return v(e,t),E(e,[{key:"getClass",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"":arguments[0],e=this.options.classes;return"undefined"!=typeof e&&e[t]?this.options.classes[t]:this.options.classPrefix?this.options.classPrefix+"-"+t:t}},{key:"setOptions",value:function(t){var e=this,o=arguments.length<=1||void 0===arguments[1]||arguments[1],i={offset:"0 0",targetOffset:"0 0",targetAttachment:"auto auto",classPrefix:"tether"};this.options=h(i,t);var n=this.options,s=n.element,a=n.target,f=n.targetModifier;if(this.element=s,this.target=a,this.targetModifier=f,"viewport"===this.target?(this.target=document.body,this.targetModifier="visible"):"scroll-handle"===this.target&&(this.target=document.body,this.targetModifier="scroll-handle"),["element","target"].forEach(function(t){if("undefined"==typeof e[t])throw new Error("Tether Error: Both element and target must be defined");"undefined"!=typeof e[t].jquery?e[t]=e[t][0]:"string"==typeof e[t]&&(e[t]=document.querySelector(e[t]))}),p(this.element,this.getClass("element")),this.options.addTargetClasses!==!1&&p(this.target,this.getClass("target")),!this.options.attachment)throw new Error("Tether Error: You must provide an attachment");this.targetAttachment=q(this.options.targetAttachment),this.attachment=q(this.options.attachment),this.offset=R(this.options.offset),this.targetOffset=R(this.options.targetOffset),"undefined"!=typeof this.scrollParents&&this.disable(),"scroll-handle"===this.targetModifier?this.scrollParents=[this.target]:this.scrollParents=r(this.target),this.options.enabled!==!1&&this.enable(o)}},{key:"getTargetBounds",value:function(){if("undefined"==typeof this.targetModifier)return a(this.target);if("visible"===this.targetModifier){if(this.target===document.body)return{top:pageYOffset,left:pageXOffset,height:innerHeight,width:innerWidth};var t=a(this.target),e={height:t.height,width:t.width,top:t.top,left:t.left};return e.height=Math.min(e.height,t.height-(pageYOffset-t.top)),e.height=Math.min(e.height,t.height-(t.top+t.height-(pageYOffset+innerHeight))),e.height=Math.min(innerHeight,e.height),e.height-=2,e.width=Math.min(e.width,t.width-(pageXOffset-t.left)),e.width=Math.min(e.width,t.width-(t.left+t.width-(pageXOffset+innerWidth))),e.width=Math.min(innerWidth,e.width),e.width-=2,e.topo.clientWidth||[i.overflow,i.overflowX].indexOf("scroll")>=0||this.target!==document.body,r=0;n&&(r=15);var s=t.height-parseFloat(i.borderTopWidth)-parseFloat(i.borderBottomWidth)-r,e={width:15,height:.975*s*(s/o.scrollHeight),left:t.left+t.width-parseFloat(i.borderLeftWidth)-15},f=0;s<408&&this.target===document.body&&(f=-11e-5*Math.pow(s,2)-.00727*s+22.58),this.target!==document.body&&(e.height=Math.max(e.height,24));var l=this.target.scrollTop/(o.scrollHeight-s);return e.top=l*(s-e.height-f)+t.top+parseFloat(i.borderTopWidth),this.target===document.body&&(e.height=Math.max(e.height,24)),e}}},{key:"clearCache",value:function(){this._cache={}}},{key:"cache",value:function(t,e){return"undefined"==typeof this._cache&&(this._cache={}),"undefined"==typeof this._cache[t]&&(this._cache[t]=e.call(this)),this._cache[t]}},{key:"enable",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]||arguments[0];this.options.addTargetClasses!==!1&&p(this.target,this.getClass("enabled")),p(this.element,this.getClass("enabled")),this.enabled=!0,this.scrollParents.forEach(function(e){e!==t.target.ownerDocument&&e.addEventListener("scroll",t.position)}),e&&this.position()}},{key:"disable",value:function(){var t=this;d(this.target,this.getClass("enabled")),d(this.element,this.getClass("enabled")),this.enabled=!1,"undefined"!=typeof this.scrollParents&&this.scrollParents.forEach(function(e){e.removeEventListener("scroll",t.position)})}},{key:"destroy",value:function(){var t=this;this.disable(),D.forEach(function(e,o){e===t&&D.splice(o,1)}),0===D.length&&s()}},{key:"updateAttachClasses",value:function(t,e){var o=this;t=t||this.attachment,e=e||this.targetAttachment;var i=["left","top","bottom","right","middle","center"];"undefined"!=typeof this._addAttachClasses&&this._addAttachClasses.length&&this._addAttachClasses.splice(0,this._addAttachClasses.length),"undefined"==typeof this._addAttachClasses&&(this._addAttachClasses=[]);var n=this._addAttachClasses;t.top&&n.push(this.getClass("element-attached")+"-"+t.top),t.left&&n.push(this.getClass("element-attached")+"-"+t.left),e.top&&n.push(this.getClass("target-attached")+"-"+e.top),e.left&&n.push(this.getClass("target-attached")+"-"+e.left);var r=[];i.forEach(function(t){r.push(o.getClass("element-attached")+"-"+t),r.push(o.getClass("target-attached")+"-"+t)}),k(function(){"undefined"!=typeof o._addAttachClasses&&(m(o.element,o._addAttachClasses,r),o.options.addTargetClasses!==!1&&m(o.target,o._addAttachClasses,r),delete o._addAttachClasses)})}},{key:"position",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]||arguments[0];if(this.enabled){this.clearCache();var o=U(this.targetAttachment,this.attachment);this.updateAttachClasses(this.attachment,o);var i=this.cache("element-bounds",function(){return a(t.element)}),n=i.width,r=i.height;if(0===n&&0===r&&"undefined"!=typeof this.lastSize){var s=this.lastSize;n=s.width,r=s.height}else this.lastSize={width:n,height:r};var h=this.cache("target-bounds",function(){return t.getTargetBounds()}),d=h,p=C(V(this.attachment),{width:n,height:r}),u=C(V(o),d),c=C(this.offset,{width:n,height:r}),g=C(this.targetOffset,d);p=w(p,c),u=w(u,g);for(var m=h.left+u.left-p.left,v=h.top+u.top-p.top,y=0;yA.documentElement.clientHeight&&(S=this.cache("scrollbar-size",l),E.viewport.bottom-=S.height),T.innerWidth>A.documentElement.clientWidth&&(S=this.cache("scrollbar-size",l),E.viewport.right-=S.width),["","static"].indexOf(A.body.style.position)!==-1&&["","static"].indexOf(A.body.parentElement.style.position)!==-1||(E.page.bottom=A.body.scrollHeight-v-r,E.page.right=A.body.scrollWidth-m-n),"undefined"!=typeof this.options.optimizations&&this.options.optimizations.moveElement!==!1&&"undefined"==typeof this.targetModifier&&!function(){var e=t.cache("target-offsetparent",function(){return f(t.target)}),o=t.cache("target-offsetparent-bounds",function(){return a(e)}),i=getComputedStyle(e),n=o,r={};if(["Top","Left","Bottom","Right"].forEach(function(t){r[t.toLowerCase()]=parseFloat(i["border"+t+"Width"])}),o.right=A.body.scrollWidth-o.left-n.width+r.right,o.bottom=A.body.scrollHeight-o.top-n.height+r.bottom,E.page.top>=o.top+r.top&&E.page.bottom>=o.bottom&&E.page.left>=o.left+r.left&&E.page.right>=o.right){var s=e.scrollTop,l=e.scrollLeft;E.offset={top:E.page.top-o.top+s-r.top,left:E.page.left-o.left+l-r.left}}}(),this.move(E),this.history.unshift(E),this.history.length>3&&this.history.pop(),e&&_(),!0}}},{key:"move",value:function(t){var e=this;if("undefined"!=typeof this.element.parentNode){var o={};for(var i in t){o[i]={};for(var n in t[i]){for(var r=!1,s=0;s=0){var c=a.split(" "),m=z(c,2);d=m[0],h=m[1]}else h=d=a;var b=O(e,r);"target"!==d&&"both"!==d||(ob[3]&&"bottom"===v.top&&(o-=p,v.top="top")),"together"===d&&("top"===v.top&&("bottom"===y.top&&ob[3]&&o-(s-p)>=b[1]&&(o-=s-p,v.top="bottom",y.top="bottom")),"bottom"===v.top&&("top"===y.top&&o+s>b[3]?(o-=p,v.top="top",o-=s,y.top="bottom"):"bottom"===y.top&&ob[3]&&"top"===y.top?(o-=s,y.top="bottom"):ob[2]&&"right"===v.left&&(i-=u,v.left="left")),"together"===h&&(ib[2]&&"right"===v.left?"left"===y.left?(i-=u,v.left="left",i-=f,y.left="right"):"right"===y.left&&(i-=u,v.left="left",i+=f,y.left="left"):"center"===v.left&&(i+f>b[2]&&"left"===y.left?(i-=f,y.left="right"):ib[3]&&"top"===y.top&&(o-=s,y.top="bottom")),"element"!==h&&"both"!==h||(ib[2]&&("left"===y.left?(i-=f,y.left="right"):"center"===y.left&&(i-=f/2,y.left="right"))),"string"==typeof l?l=l.split(",").map(function(t){return t.trim()}):l===!0&&(l=["top","left","right","bottom"]),l=l||[];var w=[],C=[];o=0?(o=b[1],w.push("top")):C.push("top")),o+s>b[3]&&(l.indexOf("bottom")>=0?(o=b[3]-s,w.push("bottom")):C.push("bottom")),i=0?(i=b[0],w.push("left")):C.push("left")),i+f>b[2]&&(l.indexOf("right")>=0?(i=b[2]-f,w.push("right")):C.push("right")),w.length&&!function(){var t=void 0;t="undefined"!=typeof e.options.pinnedClass?e.options.pinnedClass:e.getClass("pinned"),g.push(t),w.forEach(function(e){g.push(t+"-"+e)})}(),C.length&&!function(){var t=void 0;t="undefined"!=typeof e.options.outOfBoundsClass?e.options.outOfBoundsClass:e.getClass("out-of-bounds"),g.push(t),C.forEach(function(e){g.push(t+"-"+e)})}(),(w.indexOf("left")>=0||w.indexOf("right")>=0)&&(y.left=v.left=!1),(w.indexOf("top")>=0||w.indexOf("bottom")>=0)&&(y.top=v.top=!1),v.top===n.top&&v.left===n.left&&y.top===e.attachment.top&&y.left===e.attachment.left||(e.updateAttachClasses(y,v),e.trigger("update",{attachment:y,targetAttachment:v}))}),k(function(){e.options.addTargetClasses!==!1&&m(e.target,g,c),m(e.element,g,c)}),{top:o,left:i}}});var Y=x.Utils,a=Y.getBounds,m=Y.updateClasses,k=Y.defer;x.modules.push({position:function(t){var e=this,o=t.top,i=t.left,n=this.cache("element-bounds",function(){return a(e.element)}),r=n.height,s=n.width,f=this.getTargetBounds(),l=o+r,h=i+s,d=[];o<=f.bottom&&l>=f.top&&["left","right"].forEach(function(t){var e=f[t];e!==i&&e!==h||d.push(t)}),i<=f.right&&h>=f.left&&["top","bottom"].forEach(function(t){var e=f[t];e!==o&&e!==l||d.push(t)});var p=[],u=[],c=["left","top","right","bottom"];return p.push(this.getClass("abutted")),c.forEach(function(t){p.push(e.getClass("abutted")+"-"+t)}),d.length&&u.push(this.getClass("abutted")),d.forEach(function(t){u.push(e.getClass("abutted")+"-"+t)}),k(function(){e.options.addTargetClasses!==!1&&m(e.target,u,p),m(e.element,u,p)}),!0}});var z=function(){function t(t,e){var o=[],i=!0,n=!1,r=void 0;try{for(var s,a=t[Symbol.iterator]();!(i=(s=a.next()).done)&&(o.push(s.value),!e||o.length!==e);i=!0);}catch(f){n=!0,r=f}finally{try{!i&&a["return"]&&a["return"]()}finally{if(n)throw r}}return o}return function(e,o){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,o);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();return x.modules.push({position:function(t){var e=t.top,o=t.left;if(this.options.shift){var i=this.options.shift;"function"==typeof this.options.shift&&(i=this.options.shift.call(this,{top:e,left:o}));var n=void 0,r=void 0;if("string"==typeof i){i=i.split(" "),i[1]=i[1]||i[0];var s=i,a=z(s,2);n=a[0],r=a[1],n=parseFloat(n,10),r=parseFloat(r,10)}else n=i.top,r=i.left;return e+=n,o+=r,{top:e,left:o}}}}),$});
--------------------------------------------------------------------------------
/ecweb/static/js/libs/perfect-scrollbar.min.js:
--------------------------------------------------------------------------------
1 | /* perfect-scrollbar v0.7.1 */
2 | !function t(e,n,r){function o(i,s){if(!n[i]){if(!e[i]){var a="function"==typeof require&&require;if(!s&&a)return a(i,!0);if(l)return l(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var u=n[i]={exports:{}};e[i][0].call(u.exports,function(t){var n=e[i][1][t];return o(n?n:t)},u,u.exports,t,e,n,r)}return n[i].exports}for(var l="function"==typeof require&&require,i=0;i=0&&n.splice(r,1),t.className=n.join(" ")}n.add=function(t,e){t.classList?t.classList.add(e):r(t,e)},n.remove=function(t,e){t.classList?t.classList.remove(e):o(t,e)},n.list=function(t){return t.classList?Array.prototype.slice.apply(t.classList):t.className.split(" ")}},{}],3:[function(t,e,n){"use strict";function r(t,e){return window.getComputedStyle(t)[e]}function o(t,e,n){return"number"==typeof n&&(n=n.toString()+"px"),t.style[e]=n,t}function l(t,e){for(var n in e){var r=e[n];"number"==typeof r&&(r=r.toString()+"px"),t.style[n]=r}return t}var i={};i.e=function(t,e){var n=document.createElement(t);return n.className=e,n},i.appendTo=function(t,e){return e.appendChild(t),t},i.css=function(t,e,n){return"object"==typeof e?l(t,e):"undefined"==typeof n?r(t,e):o(t,e,n)},i.matches=function(t,e){return"undefined"!=typeof t.matches?t.matches(e):"undefined"!=typeof t.matchesSelector?t.matchesSelector(e):"undefined"!=typeof t.webkitMatchesSelector?t.webkitMatchesSelector(e):"undefined"!=typeof t.mozMatchesSelector?t.mozMatchesSelector(e):"undefined"!=typeof t.msMatchesSelector?t.msMatchesSelector(e):void 0},i.remove=function(t){"undefined"!=typeof t.remove?t.remove():t.parentNode&&t.parentNode.removeChild(t)},i.queryChildren=function(t,e){return Array.prototype.filter.call(t.childNodes,function(t){return i.matches(t,e)})},e.exports=i},{}],4:[function(t,e,n){"use strict";var r=function(t){this.element=t,this.events={}};r.prototype.bind=function(t,e){"undefined"==typeof this.events[t]&&(this.events[t]=[]),this.events[t].push(e),this.element.addEventListener(t,e,!1)},r.prototype.unbind=function(t,e){var n="undefined"!=typeof e;this.events[t]=this.events[t].filter(function(r){return n&&r!==e?!0:(this.element.removeEventListener(t,r,!1),!1)},this)},r.prototype.unbindAll=function(){for(var t in this.events)this.unbind(t)};var o=function(){this.eventElements=[]};o.prototype.eventElement=function(t){var e=this.eventElements.filter(function(e){return e.element===t})[0];return"undefined"==typeof e&&(e=new r(t),this.eventElements.push(e)),e},o.prototype.bind=function(t,e,n){this.eventElement(t).bind(e,n)},o.prototype.unbind=function(t,e,n){this.eventElement(t).unbind(e,n)},o.prototype.unbindAll=function(){for(var t=0;te.scrollbarYTop?1:-1;i(t,"top",t.scrollTop+s*e.containerHeight),l(t),r.stopPropagation()}),e.event.bind(e.scrollbarX,"click",r),e.event.bind(e.scrollbarXRail,"click",function(r){var o=r.pageX-window.pageXOffset-n(e.scrollbarXRail).left,s=o>e.scrollbarXLeft?1:-1;i(t,"left",t.scrollLeft+s*e.containerWidth),l(t),r.stopPropagation()})}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(t,e,n){"use strict";function r(t,e){function n(n){var o=r+n*e.railXRatio,i=Math.max(0,e.scrollbarXRail.getBoundingClientRect().left)+e.railXRatio*(e.railXWidth-e.scrollbarXWidth);0>o?e.scrollbarXLeft=0:o>i?e.scrollbarXLeft=i:e.scrollbarXLeft=o;var s=l.toInt(e.scrollbarXLeft*(e.contentWidth-e.containerWidth)/(e.containerWidth-e.railXRatio*e.scrollbarXWidth))-e.negativeScrollAdjustment;c(t,"left",s)}var r=null,o=null,s=function(e){n(e.pageX-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"x"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarX,"mousedown",function(n){o=n.pageX,r=l.toInt(i.css(e.scrollbarX,"left"))*e.railXRatio,l.startScrolling(t,"x"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}function o(t,e){function n(n){var o=r+n*e.railYRatio,i=Math.max(0,e.scrollbarYRail.getBoundingClientRect().top)+e.railYRatio*(e.railYHeight-e.scrollbarYHeight);0>o?e.scrollbarYTop=0:o>i?e.scrollbarYTop=i:e.scrollbarYTop=o;var s=l.toInt(e.scrollbarYTop*(e.contentHeight-e.containerHeight)/(e.containerHeight-e.railYRatio*e.scrollbarYHeight));c(t,"top",s)}var r=null,o=null,s=function(e){n(e.pageY-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"y"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarY,"mousedown",function(n){o=n.pageY,r=l.toInt(i.css(e.scrollbarY,"top"))*e.railYRatio,l.startScrolling(t,"y"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}var l=t("../../lib/helper"),i=t("../../lib/dom"),s=t("../instances"),a=t("../update-geometry"),c=t("../update-scroll");e.exports=function(t){var e=s.get(t);r(t,e),o(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&0>r)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&0>n||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}var r=!1;e.event.bind(t,"mouseenter",function(){r=!0}),e.event.bind(t,"mouseleave",function(){r=!1});var i=!1;e.event.bind(e.ownerDocument,"keydown",function(c){if(!(c.isDefaultPrevented&&c.isDefaultPrevented()||c.defaultPrevented)){var u=l.matches(e.scrollbarX,":focus")||l.matches(e.scrollbarY,":focus");if(r||u){var d=document.activeElement?document.activeElement:e.ownerDocument.activeElement;if(d){if("IFRAME"===d.tagName)d=d.contentDocument.activeElement;else for(;d.shadowRoot;)d=d.shadowRoot.activeElement;if(o.isEditable(d))return}var p=0,f=0;switch(c.which){case 37:p=c.metaKey?-e.contentWidth:c.altKey?-e.containerWidth:-30;break;case 38:f=c.metaKey?e.contentHeight:c.altKey?e.containerHeight:30;break;case 39:p=c.metaKey?e.contentWidth:c.altKey?e.containerWidth:30;break;case 40:f=c.metaKey?-e.contentHeight:c.altKey?-e.containerHeight:-30;break;case 33:f=90;break;case 32:f=c.shiftKey?90:-90;break;case 34:f=-90;break;case 35:f=c.ctrlKey?-e.contentHeight:-e.containerHeight;break;case 36:f=c.ctrlKey?t.scrollTop:e.containerHeight;break;default:return}a(t,"top",t.scrollTop-f),a(t,"left",t.scrollLeft+p),s(t),i=n(p,f),i&&c.preventDefault()}}})}var o=t("../../lib/helper"),l=t("../../lib/dom"),i=t("../instances"),s=t("../update-geometry"),a=t("../update-scroll");e.exports=function(t){var e=i.get(t);r(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&0>r)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&0>n||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}function r(t){var e=t.deltaX,n=-1*t.deltaY;return"undefined"!=typeof e&&"undefined"!=typeof n||(e=-1*t.wheelDeltaX/6,n=t.wheelDeltaY/6),t.deltaMode&&1===t.deltaMode&&(e*=10,n*=10),e!==e&&n!==n&&(e=0,n=t.wheelDelta),t.shiftKey?[-n,-e]:[e,n]}function o(e,n){var r=t.querySelector("textarea:hover, select[multiple]:hover, .ps-child:hover");if(r){var o=window.getComputedStyle(r),l=[o.overflow,o.overflowX,o.overflowY].join("");if(!l.match(/(scroll|auto)/))return!1;var i=r.scrollHeight-r.clientHeight;if(i>0&&!(0===r.scrollTop&&n>0||r.scrollTop===i&&0>n))return!0;var s=r.scrollLeft-r.clientWidth;if(s>0&&!(0===r.scrollLeft&&0>e||r.scrollLeft===s&&e>0))return!0}return!1}function s(s){var c=r(s),u=c[0],d=c[1];o(u,d)||(a=!1,e.settings.useBothWheelAxes?e.scrollbarYActive&&!e.scrollbarXActive?(d?i(t,"top",t.scrollTop-d*e.settings.wheelSpeed):i(t,"top",t.scrollTop+u*e.settings.wheelSpeed),a=!0):e.scrollbarXActive&&!e.scrollbarYActive&&(u?i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed):i(t,"left",t.scrollLeft-d*e.settings.wheelSpeed),a=!0):(i(t,"top",t.scrollTop-d*e.settings.wheelSpeed),i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed)),l(t),a=a||n(u,d),a&&(s.stopPropagation(),s.preventDefault()))}var a=!1;"undefined"!=typeof window.onwheel?e.event.bind(t,"wheel",s):"undefined"!=typeof window.onmousewheel&&e.event.bind(t,"mousewheel",s)}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(t,e,n){"use strict";function r(t,e){e.event.bind(t,"scroll",function(){l(t)})}var o=t("../instances"),l=t("../update-geometry");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19}],15:[function(t,e,n){"use strict";function r(t,e){function n(){var t=window.getSelection?window.getSelection():document.getSelection?document.getSelection():"";return 0===t.toString().length?null:t.getRangeAt(0).commonAncestorContainer}function r(){c||(c=setInterval(function(){return l.get(t)?(s(t,"top",t.scrollTop+u.top),s(t,"left",t.scrollLeft+u.left),void i(t)):void clearInterval(c)},50))}function a(){c&&(clearInterval(c),c=null),o.stopScrolling(t)}var c=null,u={top:0,left:0},d=!1;e.event.bind(e.ownerDocument,"selectionchange",function(){t.contains(n())?d=!0:(d=!1,a())}),e.event.bind(window,"mouseup",function(){d&&(d=!1,a())}),e.event.bind(window,"keyup",function(){d&&(d=!1,a())}),e.event.bind(window,"mousemove",function(e){if(d){var n={x:e.pageX,y:e.pageY},l={left:t.offsetLeft,right:t.offsetLeft+t.offsetWidth,top:t.offsetTop,bottom:t.offsetTop+t.offsetHeight};n.xl.right-3?(u.left=5,o.startScrolling(t,"x")):u.left=0,n.yl.bottom-3?(n.y-l.bottom+3<5?u.top=5:u.top=20,o.startScrolling(t,"y")):u.top=0,0===u.top&&0===u.left?a():r()}})}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){var e=l.get(t);r(t,e)}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(t,e,n){"use strict";function r(t,e,n,r){function o(n,r){var o=t.scrollTop,l=t.scrollLeft,i=Math.abs(n),s=Math.abs(r);if(s>i){if(0>r&&o===e.contentHeight-e.containerHeight||r>0&&0===o)return!e.settings.swipePropagation}else if(i>s&&(0>n&&l===e.contentWidth-e.containerWidth||n>0&&0===l))return!e.settings.swipePropagation;return!0}function a(e,n){s(t,"top",t.scrollTop-n),s(t,"left",t.scrollLeft-e),i(t)}function c(){w=!0}function u(){w=!1}function d(t){return t.targetTouches?t.targetTouches[0]:t}function p(t){return t.targetTouches&&1===t.targetTouches.length?!0:!(!t.pointerType||"mouse"===t.pointerType||t.pointerType===t.MSPOINTER_TYPE_MOUSE)}function f(t){if(p(t)){Y=!0;var e=d(t);g.pageX=e.pageX,g.pageY=e.pageY,v=(new Date).getTime(),null!==y&&clearInterval(y),t.stopPropagation()}}function h(t){if(!Y&&e.settings.swipePropagation&&f(t),!w&&Y&&p(t)){var n=d(t),r={pageX:n.pageX,pageY:n.pageY},l=r.pageX-g.pageX,i=r.pageY-g.pageY;a(l,i),g=r;var s=(new Date).getTime(),c=s-v;c>0&&(m.x=l/c,m.y=i/c,v=s),o(l,i)&&(t.stopPropagation(),t.preventDefault())}}function b(){!w&&Y&&(Y=!1,e.settings.swipeEasing&&(clearInterval(y),y=setInterval(function(){return l.get(t)&&(m.x||m.y)?Math.abs(m.x)<.01&&Math.abs(m.y)<.01?void clearInterval(y):(a(30*m.x,30*m.y),m.x*=.8,void(m.y*=.8)):void clearInterval(y)},10)))}var g={},v=0,m={},y=null,w=!1,Y=!1;n?(e.event.bind(window,"touchstart",c),e.event.bind(window,"touchend",u),e.event.bind(t,"touchstart",f),e.event.bind(t,"touchmove",h),e.event.bind(t,"touchend",b)):r&&(window.PointerEvent?(e.event.bind(window,"pointerdown",c),e.event.bind(window,"pointerup",u),e.event.bind(t,"pointerdown",f),e.event.bind(t,"pointermove",h),e.event.bind(t,"pointerup",b)):window.MSPointerEvent&&(e.event.bind(window,"MSPointerDown",c),e.event.bind(window,"MSPointerUp",u),e.event.bind(t,"MSPointerDown",f),e.event.bind(t,"MSPointerMove",h),e.event.bind(t,"MSPointerUp",b)))}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){if(o.env.supportsTouch||o.env.supportsIePointer){var e=l.get(t);r(t,e,o.env.supportsTouch,o.env.supportsIePointer)}}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/class"),l=t("./instances"),i=t("./update-geometry"),s={"click-rail":t("./handler/click-rail"),"drag-scrollbar":t("./handler/drag-scrollbar"),keyboard:t("./handler/keyboard"),wheel:t("./handler/mouse-wheel"),touch:t("./handler/touch"),selection:t("./handler/selection")},a=t("./handler/native-scroll");e.exports=function(t,e){e="object"==typeof e?e:{},o.add(t,"ps");var n=l.add(t);n.settings=r.extend(n.settings,e),o.add(t,"ps--theme_"+n.settings.theme),n.settings.handlers.forEach(function(e){s[e](t)}),a(t),i(t)}},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(t,e,n){"use strict";function r(t){function e(){a.add(t,"ps--focus")}function n(){a.remove(t,"ps--focus")}var r=this;r.settings=s.clone(c),r.containerWidth=null,r.containerHeight=null,r.contentWidth=null,r.contentHeight=null,r.isRtl="rtl"===u.css(t,"direction"),r.isNegativeScroll=function(){var e=t.scrollLeft,n=null;return t.scrollLeft=-1,n=t.scrollLeft<0,t.scrollLeft=e,n}(),r.negativeScrollAdjustment=r.isNegativeScroll?t.scrollWidth-t.clientWidth:0,r.event=new d,r.ownerDocument=t.ownerDocument||document,r.scrollbarXRail=u.appendTo(u.e("div","ps__scrollbar-x-rail"),t),r.scrollbarX=u.appendTo(u.e("div","ps__scrollbar-x"),r.scrollbarXRail),r.scrollbarX.setAttribute("tabindex",0),r.event.bind(r.scrollbarX,"focus",e),r.event.bind(r.scrollbarX,"blur",n),r.scrollbarXActive=null,r.scrollbarXWidth=null,r.scrollbarXLeft=null,r.scrollbarXBottom=s.toInt(u.css(r.scrollbarXRail,"bottom")),r.isScrollbarXUsingBottom=r.scrollbarXBottom===r.scrollbarXBottom,r.scrollbarXTop=r.isScrollbarXUsingBottom?null:s.toInt(u.css(r.scrollbarXRail,"top")),r.railBorderXWidth=s.toInt(u.css(r.scrollbarXRail,"borderLeftWidth"))+s.toInt(u.css(r.scrollbarXRail,"borderRightWidth")),u.css(r.scrollbarXRail,"display","block"),r.railXMarginWidth=s.toInt(u.css(r.scrollbarXRail,"marginLeft"))+s.toInt(u.css(r.scrollbarXRail,"marginRight")),u.css(r.scrollbarXRail,"display",""),r.railXWidth=null,r.railXRatio=null,r.scrollbarYRail=u.appendTo(u.e("div","ps__scrollbar-y-rail"),t),r.scrollbarY=u.appendTo(u.e("div","ps__scrollbar-y"),r.scrollbarYRail),r.scrollbarY.setAttribute("tabindex",0),r.event.bind(r.scrollbarY,"focus",e),r.event.bind(r.scrollbarY,"blur",n),r.scrollbarYActive=null,r.scrollbarYHeight=null,r.scrollbarYTop=null,r.scrollbarYRight=s.toInt(u.css(r.scrollbarYRail,"right")),r.isScrollbarYUsingRight=r.scrollbarYRight===r.scrollbarYRight,r.scrollbarYLeft=r.isScrollbarYUsingRight?null:s.toInt(u.css(r.scrollbarYRail,"left")),r.scrollbarYOuterWidth=r.isRtl?s.outerWidth(r.scrollbarY):null,r.railBorderYWidth=s.toInt(u.css(r.scrollbarYRail,"borderTopWidth"))+s.toInt(u.css(r.scrollbarYRail,"borderBottomWidth")),u.css(r.scrollbarYRail,"display","block"),r.railYMarginHeight=s.toInt(u.css(r.scrollbarYRail,"marginTop"))+s.toInt(u.css(r.scrollbarYRail,"marginBottom")),u.css(r.scrollbarYRail,"display",""),r.railYHeight=null,r.railYRatio=null}function o(t){return t.getAttribute("data-ps-id")}function l(t,e){t.setAttribute("data-ps-id",e)}function i(t){t.removeAttribute("data-ps-id")}var s=t("../lib/helper"),a=t("../lib/class"),c=t("./default-setting"),u=t("../lib/dom"),d=t("../lib/event-manager"),p=t("../lib/guid"),f={};n.add=function(t){var e=p();return l(t,e),f[e]=new r(t),f[e]},n.remove=function(t){delete f[o(t)],i(t)},n.get=function(t){return f[o(t)]}},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(t,e,n){"use strict";function r(t,e){return t.settings.minScrollbarLength&&(e=Math.max(e,t.settings.minScrollbarLength)),t.settings.maxScrollbarLength&&(e=Math.min(e,t.settings.maxScrollbarLength)),e}function o(t,e){var n={width:e.railXWidth};e.isRtl?n.left=e.negativeScrollAdjustment+t.scrollLeft+e.containerWidth-e.contentWidth:n.left=t.scrollLeft,e.isScrollbarXUsingBottom?n.bottom=e.scrollbarXBottom-t.scrollTop:n.top=e.scrollbarXTop+t.scrollTop,s.css(e.scrollbarXRail,n);var r={top:t.scrollTop,height:e.railYHeight};e.isScrollbarYUsingRight?e.isRtl?r.right=e.contentWidth-(e.negativeScrollAdjustment+t.scrollLeft)-e.scrollbarYRight-e.scrollbarYOuterWidth:r.right=e.scrollbarYRight-t.scrollLeft:e.isRtl?r.left=e.negativeScrollAdjustment+t.scrollLeft+2*e.containerWidth-e.contentWidth-e.scrollbarYLeft-e.scrollbarYOuterWidth:r.left=e.scrollbarYLeft+t.scrollLeft,s.css(e.scrollbarYRail,r),s.css(e.scrollbarX,{left:e.scrollbarXLeft,width:e.scrollbarXWidth-e.railBorderXWidth}),s.css(e.scrollbarY,{top:e.scrollbarYTop,height:e.scrollbarYHeight-e.railBorderYWidth})}var l=t("../lib/helper"),i=t("../lib/class"),s=t("../lib/dom"),a=t("./instances"),c=t("./update-scroll");e.exports=function(t){var e=a.get(t);e.containerWidth=t.clientWidth,e.containerHeight=t.clientHeight,e.contentWidth=t.scrollWidth,e.contentHeight=t.scrollHeight;var n;t.contains(e.scrollbarXRail)||(n=s.queryChildren(t,".ps__scrollbar-x-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarXRail,t)),t.contains(e.scrollbarYRail)||(n=s.queryChildren(t,".ps__scrollbar-y-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarYRail,t)),!e.settings.suppressScrollX&&e.containerWidth+e.settings.scrollXMarginOffset=e.railXWidth-e.scrollbarXWidth&&(e.scrollbarXLeft=e.railXWidth-e.scrollbarXWidth),e.scrollbarYTop>=e.railYHeight-e.scrollbarYHeight&&(e.scrollbarYTop=e.railYHeight-e.scrollbarYHeight),o(t,e),e.scrollbarXActive?i.add(t,"ps--active-x"):(i.remove(t,"ps--active-x"),e.scrollbarXWidth=0,e.scrollbarXLeft=0,c(t,"left",0)),e.scrollbarYActive?i.add(t,"ps--active-y"):(i.remove(t,"ps--active-y"),e.scrollbarYHeight=0,e.scrollbarYTop=0,c(t,"top",0))}},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(t,e,n){"use strict";var r=t("./instances"),o=function(t){var e=document.createEvent("Event");return e.initEvent(t,!0,!0),e};e.exports=function(t,e,n){if("undefined"==typeof t)throw"You must provide an element to the update-scroll function";if("undefined"==typeof e)throw"You must provide an axis to the update-scroll function";if("undefined"==typeof n)throw"You must provide a value to the update-scroll function";"top"===e&&0>=n&&(t.scrollTop=n=0,t.dispatchEvent(o("ps-y-reach-start"))),"left"===e&&0>=n&&(t.scrollLeft=n=0,t.dispatchEvent(o("ps-x-reach-start")));var l=r.get(t);"top"===e&&n>=l.contentHeight-l.containerHeight&&(n=l.contentHeight-l.containerHeight,n-t.scrollTop<=1?n=t.scrollTop:t.scrollTop=n,t.dispatchEvent(o("ps-y-reach-end"))),"left"===e&&n>=l.contentWidth-l.containerWidth&&(n=l.contentWidth-l.containerWidth,n-t.scrollLeft<=1?n=t.scrollLeft:t.scrollLeft=n,t.dispatchEvent(o("ps-x-reach-end"))),void 0===l.lastTop&&(l.lastTop=t.scrollTop),void 0===l.lastLeft&&(l.lastLeft=t.scrollLeft),"top"===e&&nl.lastTop&&t.dispatchEvent(o("ps-scroll-down")),"left"===e&&nl.lastLeft&&t.dispatchEvent(o("ps-scroll-right")),"top"===e&&n!==l.lastTop&&(t.scrollTop=l.lastTop=n,t.dispatchEvent(o("ps-scroll-y"))),"left"===e&&n!==l.lastLeft&&(t.scrollLeft=l.lastLeft=n,t.dispatchEvent(o("ps-scroll-x")))}},{"./instances":18}],21:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/dom"),l=t("./instances"),i=t("./update-geometry"),s=t("./update-scroll");e.exports=function(t){var e=l.get(t);e&&(e.negativeScrollAdjustment=e.isNegativeScroll?t.scrollWidth-t.clientWidth:0,o.css(e.scrollbarXRail,"display","block"),o.css(e.scrollbarYRail,"display","block"),e.railXMarginWidth=r.toInt(o.css(e.scrollbarXRail,"marginLeft"))+r.toInt(o.css(e.scrollbarXRail,"marginRight")),e.railYMarginHeight=r.toInt(o.css(e.scrollbarYRail,"marginTop"))+r.toInt(o.css(e.scrollbarYRail,"marginBottom")),o.css(e.scrollbarXRail,"display","none"),o.css(e.scrollbarYRail,"display","none"),i(t),s(t,"top",t.scrollTop),s(t,"left",t.scrollLeft),o.css(e.scrollbarXRail,"display",""),o.css(e.scrollbarYRail,"display",""))}},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]);
--------------------------------------------------------------------------------