diff --git a/Pipfile b/Pipfile index 41efdfbc..aa2b916b 100644 --- a/Pipfile +++ b/Pipfile @@ -38,6 +38,8 @@ django-model-utils = "*" django-taggit = "*" braintree = "*" django-environ = "*" +celery = "*" +redis = "*" [requires] python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 27e152e6..e686c303 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "546ca376ad9d19496963ea5077e626ca9634cae1116f18602fa11e513a3e292e" + "sha256": "3b5868f5f590154da864b5136ebe6f65bbcedfb90fa63ceb0414311039e50a00" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,14 @@ ] }, "default": { + "amqp": { + "hashes": [ + "sha256:9881f8e6fe23e3db9faa6cfd8c05390213e1d1b95c0162bc50552cad75bffa5f", + "sha256:a8fb8151eb9d12204c9f1784c0da920476077609fa0a70f2468001e3a4258484" + ], + "markers": "python_version >= '3.6'", + "version": "==5.0.1" + }, "autopep8": { "hashes": [ "sha256:4d8eec30cc81bc5617dbf1218201d770dc35629363547f17577c61683ccfb3ee" @@ -23,6 +31,13 @@ "index": "pypi", "version": "==1.4.4" }, + "billiard": { + "hashes": [ + "sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede", + "sha256:d91725ce6425f33a97dfa72fb6bfef0e47d4652acd98a032bd1a7fbf06d5fa6a" + ], + "version": "==3.6.3.0" + }, "braintree": { "hashes": [ "sha256:10b99ea6ea482d1438b0d058dbe51f6a33fe98fe6d0d977d4b7c5868fe3b9e35", @@ -31,6 +46,14 @@ "index": "pypi", "version": "==4.4.0" }, + "celery": { + "hashes": [ + "sha256:012c814967fe89e3f5d2cf49df2dba3de5f29253a7f4f2270e8fce6b901b4ebf", + "sha256:930c3acd55349d028c4e7104a7d377729cbcca19d9fce470c17172d9e7f9a8b6" + ], + "index": "pypi", + "version": "==5.0.2" + }, "certifi": { "hashes": [ "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", @@ -47,6 +70,27 @@ "index": "pypi", "version": "==3.0.4" }, + "click": { + "hashes": [ + "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", + "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==7.1.2" + }, + "click-didyoumean": { + "hashes": [ + "sha256:112229485c9704ff51362fe34b2d4f0b12fc71cc20f6d2b3afabed4b8bfa6aeb" + ], + "version": "==0.0.3" + }, + "click-repl": { + "hashes": [ + "sha256:9c4c3d022789cae912aad8a3f5e1d7c2cdd016ee1225b5212ad3e8691563cda5", + "sha256:b9f29d52abc4d6059f8e276132a111ab8d94980afe6a5432b9d996544afa95d5" + ], + "version": "==0.1.6" + }, "dateparser": { "hashes": [ "sha256:983d84b5e3861cb0aa240cad07f12899bb10b62328aae188b9007e04ce37d665", @@ -156,39 +200,47 @@ "index": "pypi", "version": "==1.0.5" }, + "kombu": { + "hashes": [ + "sha256:6dc509178ac4269b0e66ab4881f70a2035c33d3a622e20585f965986a5182006", + "sha256:f4965fba0a4718d47d470beeb5d6446e3357a62402b16c510b6a2f251e05ac3c" + ], + "markers": "python_version >= '3.6'", + "version": "==5.0.2" + }, "pillow": { "hashes": [ - "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", - "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", - "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", - "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", - "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", - "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", - "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", - "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", - "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", - "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", - "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", - "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", - "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", - "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", - "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", - "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", - "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", - "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" - ], - "index": "pypi", - "version": "==7.2.0" + "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a", + "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae", + "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce", + "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e", + "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140", + "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb", + "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021", + "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6", + "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302", + "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c", + "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271", + "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09", + "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3", + "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015", + "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3", + "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544", + "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8", + "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792", + "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0", + "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3", + "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8", + "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11", + "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7", + "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11", + "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e", + "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039", + "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5", + "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72" + ], + "index": "pypi", + "version": "==8.0.1" }, "prompt-toolkit": { "hashes": [ @@ -223,6 +275,14 @@ "index": "pypi", "version": "==2019.3" }, + "redis": { + "hashes": [ + "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", + "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" + ], + "index": "pypi", + "version": "==3.5.3" + }, "regex": { "hashes": [ "sha256:15454b37c5a278f46f7aa2d9339bda450c300617ca2fca6558d05d870245edc7", @@ -297,6 +357,14 @@ "index": "pypi", "version": "==1.25.7" }, + "vine": { + "hashes": [ + "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30", + "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e" + ], + "markers": "python_version >= '3.6'", + "version": "==5.0.0" + }, "wcwidth": { "hashes": [ "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", @@ -312,6 +380,7 @@ "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703", "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386" ], + "markers": "python_version >= '3.5'", "version": "==2.4.2" }, "autopep8": { @@ -323,10 +392,11 @@ }, "isort": { "hashes": [ - "sha256:2c1d044a96c74367ab12188593d134c596747d97aec43b9cdb12eea83b6889d2", - "sha256:3820dd92c3214290cda6351f2ae2cedd5170759bc434af600eaad4f7a82a6ade" + "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7", + "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58" ], - "version": "==5.6.3" + "markers": "python_version >= '3.6' and python_version < '4.0'", + "version": "==5.6.4" }, "lazy-object-proxy": { "hashes": [ @@ -352,6 +422,7 @@ "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.4.3" }, "mccabe": { @@ -387,10 +458,11 @@ }, "toml": { "hashes": [ - "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", - "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], - "version": "==0.10.1" + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.10.2" }, "wrapt": { "hashes": [ diff --git a/README.md b/README.md index 2f894f70..c23babf2 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,7 @@ In order to test this project with dummy data: then for result, subjects, combinations you can run your script or just create data manually for now. + +* APPLY CARD INFO AT `admission` link +`card number: 5555555555554444` +`expiracy: any valid future date` diff --git a/config/.env.example b/config/.env.example index 1f52ba2c..dae976df 100644 --- a/config/.env.example +++ b/config/.env.example @@ -8,4 +8,6 @@ EMAIL_HOST_PASSWORD= DEFAULT_FROM_EMAIL= BRAINTREE_MERCHANT_ID= BRAINTREE_PUBLIC_KEY= -BRAINTREE_PRIVATE_KEY= \ No newline at end of file +BRAINTREE_PRIVATE_KEY= +BROKER_URL= +CELERY_RESULT_BACKEND= \ No newline at end of file diff --git a/config/__init__.py b/config/__init__.py index e69de29b..9e0d95fd 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -0,0 +1,3 @@ +from .celery import app as celery_app + +__all__ = ('celery_app',) \ No newline at end of file diff --git a/config/celery.py b/config/celery.py new file mode 100644 index 00000000..3d3f41cd --- /dev/null +++ b/config/celery.py @@ -0,0 +1,24 @@ +import os + +from celery import Celery + +from django.conf import settings + +# set the default Django settings module for the 'celery' program. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + +app = Celery('config') + +# Using a string here means the worker doesn't have to serialize +# the configuration object to child processes. +# - namespace='CELERY' means all celery-related configuration keys +# should have a `CELERY_` prefix. +app.config_from_object('django.conf:settings', namespace='CELERY') + +# Load task modules from all registered Django app configs. +app.autodiscover_tasks(lambda:settings.INSTALLED_APPS) + + +@app.task(bind=True) +def debug_task(self): + print(f'Request: {self.request!r}') \ No newline at end of file diff --git a/config/settings.py b/config/settings.py index 3d2009e5..4208cfe5 100644 --- a/config/settings.py +++ b/config/settings.py @@ -137,7 +137,7 @@ LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'UTC' +TIME_ZONE = 'Asia/Dhaka' USE_I18N = True @@ -174,7 +174,6 @@ EMAIL_PORT = env('EMAIL_PORT') EMAIL_HOST_USER = env('EMAIL_HOST_USER') EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD') -DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL') # login/register redirects LOGIN_REDIRECT_URL = 'index_view' @@ -191,4 +190,12 @@ # BRAINTREE FOR HANDLING PAYMENTS BRAINTREE_MERCHANT_ID = env('BRAINTREE_MERCHANT_ID') BRAINTREE_PUBLIC_KEY = env('BRAINTREE_PUBLIC_KEY') -BRAINTREE_PRIVATE_KEY = env('BRAINTREE_PRIVATE_KEY') \ No newline at end of file +BRAINTREE_PRIVATE_KEY = env('BRAINTREE_PRIVATE_KEY') + +# CELERY BROKER CONFIG +CELERY_BROKER_URL = env('CELERY_BROKER_URL') +CELERY_RESULT_BACKEND = env('CELERY_RESULT_BACKEND') +CELERY_ACCEPT_CONTENT = ['application/json'] +CELERY_TASK_SERIALIZER = 'json' +CELERY_RESULT_SERIALIZER = 'json' +CELERY_TIMEZONE = 'Asia/Dhaka' \ No newline at end of file diff --git a/pages/views.py b/pages/views.py index a8d09610..31b4618a 100644 --- a/pages/views.py +++ b/pages/views.py @@ -5,6 +5,8 @@ from students.forms import StudentForm from students.models import AdmissionStudent +from students.tasks import send_admission_confirmation_email + def index(request): return render(request, 'landing/index.html') @@ -71,4 +73,6 @@ def payment(request, pk): if result.is_success: registrant.paid = True registrant.save() + print(registrant.email) + send_admission_confirmation_email(registrant.id) return redirect('pages:online_admission') diff --git a/students/tasks.py b/students/tasks.py new file mode 100644 index 00000000..70a8a339 --- /dev/null +++ b/students/tasks.py @@ -0,0 +1,23 @@ +from celery.decorators import task +from celery.utils.log import get_logger + +from django.core.mail import send_mail +from config.settings import EMAIL_HOST_USER + +from .models import AdmissionStudent + +logger = get_logger(__name__) + + +@task(name='send_admission_confirmation_email') +def send_admission_confirmation_email(student_id): + student = AdmissionStudent.objects.get(id=student_id) + name = student.name + choosen_dept = student.choosen_department + send_mail( + f'SMS-LIO: Admission confirmed for student {name}', + f'Choosen Dept: {choosen_dept}', + EMAIL_HOST_USER, + [student.email, ], + fail_silently=False + ) \ No newline at end of file diff --git a/students/views.py b/students/views.py index 193f00b3..757c6e15 100644 --- a/students/views.py +++ b/students/views.py @@ -15,6 +15,8 @@ from .forms import (StudentForm, AdmissionForm, StudentRegistrantUpdateForm, CounselingDataForm) +from .tasks import send_admission_confirmation_email + @user_passes_test(user_is_staff) def students_dashboard_index(request): @@ -82,6 +84,7 @@ def admit_student(request, pk): student.admitted = True student.admission_date = date.today() student.save() + send_admission_confirmation_email.delay(student.id) return redirect('students:admitted_student_list') else: form = AdmissionForm()