├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── TODO.md ├── activity ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── consumers.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── 0003_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ ├── 0003_initial.cpython-312.pyc │ │ ├── 0004_delete_tag_share_activity_sh_user_id_9497e8_idx_and_more.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── routing.py ├── signals.py ├── tests.py └── views.py ├── certifications ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── 0003_initial.py │ ├── 0004_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ ├── 0003_initial.cpython-312.pyc │ │ ├── 0004_initial.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── signals.py ├── tests.py └── views.py ├── companies ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── signals.py ├── tests.py └── views.py ├── connections ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── consumers.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── routing.py ├── signals.py ├── tests.py └── views.py ├── courses ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── 0003_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_alter_course_tags_alter_coursecompletion_tags.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ ├── 0003_initial.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── signals.py ├── tests.py └── views.py ├── db.sqlite3 ├── events ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ ├── 0002_remove_event_category_event_tags.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── signals.py ├── tests.py └── views.py ├── followers ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── signals.py ├── tests.py └── views.py ├── groups ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ ├── 0003_alter_group_tags.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── signals.py ├── tests.py └── views.py ├── jobs ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ ├── 0003_alter_joblisting_tags.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── signals.py ├── tests.py └── views.py ├── manage.py ├── messaging ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── consumers.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── routing.py ├── signals.py ├── tests.py └── views.py ├── notifications ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── forms.cpython-312.pyc │ ├── models.cpython-312.pyc │ ├── services.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── consumers.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── 0003_alter_notification_recipient.py │ ├── 0004_alter_notificationtype_type_name.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ ├── 0003_alter_notification_recipient.cpython-312.pyc │ │ ├── 0004_alter_notificationtype_type_name.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── routing.py ├── services.py ├── signals.py ├── tests.py └── views.py ├── posts ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ └── models.cpython-312.pyc ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ ├── 0002_initial.cpython-312.pyc │ │ ├── 0003_alter_comment_content_alter_post_content_and_more.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── signals.py ├── tests.py └── views.py ├── profiles ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── admin.cpython-312.pyc │ ├── apps.cpython-312.pyc │ ├── models.cpython-312.pyc │ └── signals.cpython-312.pyc ├── admin.py ├── apps.py ├── consumers.py ├── migrations │ ├── 0001_initial.py │ ├── __init__.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-312.pyc │ │ └── __init__.cpython-312.pyc ├── models.py ├── routing.py ├── signals.py ├── tests.py └── views.py ├── project ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-312.pyc │ ├── settings.cpython-312.pyc │ ├── urls.cpython-312.pyc │ └── wsgi.cpython-312.pyc ├── asgi.py ├── routing.py ├── settings.py ├── urls.py └── wsgi.py ├── requirements.txt └── settings ├── __init__.py ├── admin.py ├── apps.py ├── migrations └── __init__.py ├── models.py ├── services.py ├── signals.py ├── tests.py └── views.py /.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 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 110 | .pdm.toml 111 | .pdm-python 112 | .pdm-build/ 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | #.idea/ 163 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Django LMS 2 | 3 | Thank you for considering contributing to the Django LMS project! Your help is greatly appreciated. Please take a moment to review the following guidelines before making contributions. 4 | 5 | ## Code of Conduct 6 | 7 | This project and everyone participating in it are governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [email@example.com]. 8 | 9 | ## How Can I Contribute? 10 | 11 | - Reporting Bugs: If you encounter a bug, please open an issue on the GitHub repository. Be sure to include detailed information about the bug and steps to reproduce it. 12 | - Suggesting Enhancements: If you have ideas for enhancements or new features, feel free to open an issue to discuss them. 13 | - Pull Requests: Contributions via pull requests are welcome! Fork the repository, make your changes, and submit a pull request with a clear description of your changes. 14 | 15 | ## Development Setup 16 | 17 | To set up the project for development, follow the instructions in the [README.md](README.md) file. Be sure to create a virtual environment and install the necessary dependencies before starting development. 18 | 19 | ## Code Style 20 | 21 | Please adhere to the PEP 8 style guide for Python code. Ensure that your code is well-documented and follows best practices. 22 | 23 | ## Testing 24 | 25 | Before submitting a pull request, make sure to test your changes thoroughly. Write unit tests and ensure that all existing tests pass. 26 | 27 | ## Pull Request Guidelines 28 | 29 | - Follow the pull request template provided when submitting a pull request. 30 | - Provide a clear description of the problem you're solving and the changes you've made. 31 | - Ensure that your code is properly formatted and passes all tests. 32 | - Be responsive to feedback and be willing to make changes if necessary. 33 | 34 | ## Code Reviews 35 | 36 | All pull requests will undergo code review. Be prepared to receive feedback and make revisions as needed. Code reviews help maintain code quality and ensure that contributions meet project standards. 37 | 38 | ## Attribution 39 | 40 | Contributors will be acknowledged in the project's documentation. By contributing to this project, you agree to have your contributions licensed under the [MIT License](LICENSE). 41 | 42 | --- 43 | 44 | We appreciate your interest in contributing to Django LMS! If you have any questions or need assistance, feel free to reach out to the project maintainers. 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] [Abdullah Bakir] 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 | -------------------------------------------------------------------------------- /activity/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/__init__.py -------------------------------------------------------------------------------- /activity/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /activity/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /activity/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /activity/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /activity/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /activity/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.contenttypes.admin import GenericTabularInline 3 | from .models import Category, Share, Reaction, Attachment, Thread, UserActivity, UserStatistics, MarketingCampaign, LearningService, Analytics 4 | 5 | # Inline for GenericRelation Attachment 6 | class AttachmentInline(GenericTabularInline): 7 | model = Attachment 8 | extra = 1 9 | 10 | # Admin classes for each model 11 | @admin.register(Category) 12 | class CategoryAdmin(admin.ModelAdmin): 13 | list_display = ('name', 'description') 14 | search_fields = ('name',) 15 | 16 | 17 | 18 | @admin.register(Share) 19 | class ShareAdmin(admin.ModelAdmin): 20 | list_display = ('user', 'shared_at', 'content_object') 21 | list_filter = ('user', 'shared_at') 22 | search_fields = ('user__username', 'shared_to__username', 'content_object__name') 23 | 24 | @admin.register(Reaction) 25 | class ReactionAdmin(admin.ModelAdmin): 26 | list_display = ('type', 'user', 'get_content_object', 'message', 'post', 'comment', 'job_post', 'group') 27 | list_filter = ('type', 'user') 28 | search_fields = ('user__username',) 29 | 30 | def get_content_object(self, obj): 31 | return obj.content_object 32 | 33 | get_content_object.short_description = 'Content Object' # Customize the column header 34 | get_content_object.admin_order_field = 'content_object__id' # Optionally, enable sorting by content object ID 35 | 36 | @admin.register(Attachment) 37 | class AttachmentAdmin(admin.ModelAdmin): 38 | list_display = ('attachment_type', 'uploaded_at', 'content_object') 39 | list_filter = ('attachment_type', 'uploaded_at') 40 | search_fields = ('content_object__name',) 41 | inlines = [AttachmentInline] 42 | 43 | @admin.register(Thread) 44 | class ThreadAdmin(admin.ModelAdmin): 45 | list_display = ('subject', 'last_message_at') 46 | list_filter = ('last_message_at',) 47 | search_fields = ('subject',) 48 | 49 | @admin.register(UserActivity) 50 | class UserActivityAdmin(admin.ModelAdmin): 51 | list_display = ('user', 'activity_type', 'timestamp') 52 | list_filter = ('activity_type', 'timestamp') 53 | search_fields = ('user__username',) 54 | 55 | @admin.register(UserStatistics) 56 | class UserStatisticsAdmin(admin.ModelAdmin): 57 | list_display = ('user', 'connections_count', 'posts_count', 'engagement_rate') 58 | list_filter = ('connections_count', 'posts_count', 'engagement_rate') 59 | search_fields = ('user__username',) 60 | 61 | @admin.register(MarketingCampaign) 62 | class MarketingCampaignAdmin(admin.ModelAdmin): 63 | list_display = ('campaign_name', 'start_date', 'end_date') 64 | list_filter = ('start_date', 'end_date') 65 | search_fields = ('campaign_name',) 66 | 67 | @admin.register(LearningService) 68 | class LearningServiceAdmin(admin.ModelAdmin): 69 | list_display = ('service_name', 'resources') 70 | search_fields = ('service_name',) 71 | 72 | @admin.register(Analytics) 73 | class AnalyticsAdmin(admin.ModelAdmin): 74 | list_display = ('activity_type', 'engagement_rate') 75 | search_fields = ('activity_type', 'engagement_rate') 76 | -------------------------------------------------------------------------------- /activity/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ActivityConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "activity" 7 | 8 | def ready(self): 9 | import activity.signals 10 | -------------------------------------------------------------------------------- /activity/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | initial = True 9 | 10 | dependencies = [ 11 | ("activity", "0001_initial"), 12 | ("groups", "0001_initial"), 13 | ("jobs", "0001_initial"), 14 | ("messaging", "0001_initial"), 15 | ("posts", "0001_initial"), 16 | ] 17 | 18 | operations = [ 19 | migrations.AddField( 20 | model_name="reaction", 21 | name="comment", 22 | field=models.ForeignKey( 23 | blank=True, 24 | null=True, 25 | on_delete=django.db.models.deletion.CASCADE, 26 | to="posts.comment", 27 | ), 28 | ), 29 | migrations.AddField( 30 | model_name="reaction", 31 | name="group", 32 | field=models.ForeignKey( 33 | blank=True, 34 | null=True, 35 | on_delete=django.db.models.deletion.CASCADE, 36 | to="groups.group", 37 | ), 38 | ), 39 | migrations.AddField( 40 | model_name="reaction", 41 | name="job_post", 42 | field=models.ForeignKey( 43 | blank=True, 44 | null=True, 45 | on_delete=django.db.models.deletion.CASCADE, 46 | to="jobs.joblisting", 47 | ), 48 | ), 49 | migrations.AddField( 50 | model_name="reaction", 51 | name="message", 52 | field=models.ForeignKey( 53 | blank=True, 54 | null=True, 55 | on_delete=django.db.models.deletion.CASCADE, 56 | to="messaging.message", 57 | ), 58 | ), 59 | migrations.AddField( 60 | model_name="reaction", 61 | name="post", 62 | field=models.ForeignKey( 63 | blank=True, 64 | null=True, 65 | on_delete=django.db.models.deletion.CASCADE, 66 | to="posts.post", 67 | ), 68 | ), 69 | ] 70 | -------------------------------------------------------------------------------- /activity/migrations/0003_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | initial = True 10 | 11 | dependencies = [ 12 | ("activity", "0002_initial"), 13 | ("contenttypes", "0002_remove_content_type_name"), 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name="reaction", 20 | name="user", 21 | field=models.ForeignKey( 22 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL 23 | ), 24 | ), 25 | migrations.AddField( 26 | model_name="share", 27 | name="content_type", 28 | field=models.ForeignKey( 29 | on_delete=django.db.models.deletion.CASCADE, 30 | to="contenttypes.contenttype", 31 | ), 32 | ), 33 | migrations.AddField( 34 | model_name="share", 35 | name="shared_to", 36 | field=models.ManyToManyField( 37 | related_name="received_shares", to=settings.AUTH_USER_MODEL 38 | ), 39 | ), 40 | migrations.AddField( 41 | model_name="share", 42 | name="user", 43 | field=models.ForeignKey( 44 | null=True, 45 | on_delete=django.db.models.deletion.SET_NULL, 46 | to=settings.AUTH_USER_MODEL, 47 | ), 48 | ), 49 | migrations.AddField( 50 | model_name="thread", 51 | name="participants", 52 | field=models.ManyToManyField( 53 | related_name="threads", to=settings.AUTH_USER_MODEL 54 | ), 55 | ), 56 | migrations.AddField( 57 | model_name="useractivity", 58 | name="categories", 59 | field=models.ManyToManyField( 60 | related_name="user_activity_categories", to="activity.category" 61 | ), 62 | ), 63 | migrations.AddField( 64 | model_name="useractivity", 65 | name="user", 66 | field=models.ForeignKey( 67 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL 68 | ), 69 | ), 70 | migrations.AddField( 71 | model_name="userstatistics", 72 | name="user", 73 | field=models.OneToOneField( 74 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL 75 | ), 76 | ), 77 | migrations.AddIndex( 78 | model_name="share", 79 | index=models.Index( 80 | fields=["user", "shared_at"], name="activity_sh_user_id_9497e8_idx" 81 | ), 82 | ), 83 | migrations.AddIndex( 84 | model_name="share", 85 | index=models.Index( 86 | fields=["content_type", "object_id"], 87 | name="activity_sh_content_b8ee3e_idx", 88 | ), 89 | ), 90 | ] 91 | -------------------------------------------------------------------------------- /activity/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/migrations/__init__.py -------------------------------------------------------------------------------- /activity/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /activity/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /activity/migrations/__pycache__/0003_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/migrations/__pycache__/0003_initial.cpython-312.pyc -------------------------------------------------------------------------------- /activity/migrations/__pycache__/0004_delete_tag_share_activity_sh_user_id_9497e8_idx_and_more.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/migrations/__pycache__/0004_delete_tag_share_activity_sh_user_id_9497e8_idx_and_more.cpython-312.pyc -------------------------------------------------------------------------------- /activity/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/activity/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /activity/routing.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | from . import consumers 3 | 4 | websocket_urlpatterns = [ 5 | re_path(r'ws/activity/(?P\w+)/$', consumers.ActivityConsumer.as_asgi()), 6 | ] -------------------------------------------------------------------------------- /activity/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /activity/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /certifications/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/__init__.py -------------------------------------------------------------------------------- /certifications/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Certification, Attachment 3 | from activity.models import Category 4 | from django.contrib.contenttypes.admin import GenericTabularInline 5 | 6 | # Inline for GenericRelation Attachment 7 | class AttachmentInline(GenericTabularInline): 8 | model = Attachment 9 | extra = 1 10 | 11 | 12 | 13 | # ModelAdmin for Certification 14 | @admin.register(Certification) 15 | class CertificationAdmin(admin.ModelAdmin): 16 | list_display = ('name', 'user', 'issue_date', 'expiration_date', 'verification_status') 17 | list_filter = ('issue_date', 'expiration_date', 'verification_status', 'categories') 18 | search_fields = ('name', 'user__username', 'issuing_organization', 'description') 19 | 20 | fieldsets = ( 21 | ('Certification Information', { 22 | 'fields': ('name', 'user', 'issuing_organization', 'issue_date', 'expiration_date', 'verification_status') 23 | }), 24 | ('Additional Details', { 25 | 'fields': ('credential_id', 'credential_url', 'description', 'certificate_image', 'categories', 'related_jobs', 'related_courses') 26 | }), 27 | ) 28 | 29 | filter_horizontal = ('categories', 'related_jobs', 'related_courses') 30 | inlines = [AttachmentInline] 31 | 32 | def save_model(self, request, obj, form, change): 33 | """Override save_model to associate the user with the Certification.""" 34 | if not obj.user_id: 35 | obj.user = request.user 36 | obj.save() 37 | 38 | def get_queryset(self, request): 39 | """Limit queryset to current user's Certifications.""" 40 | qs = super().get_queryset(request) 41 | if request.user.is_superuser: 42 | return qs 43 | return qs.filter(user=request.user) 44 | 45 | def get_readonly_fields(self, request, obj=None): 46 | """Limit fields that are readonly based on user permissions.""" 47 | if request.user.is_superuser: 48 | return [] 49 | return ['user', 'verification_status'] 50 | 51 | def get_inline_instances(self, request, obj=None): 52 | """Override to hide AttachmentInline for non-superusers.""" 53 | if request.user.is_superuser: 54 | return super().get_inline_instances(request, obj) 55 | return [] 56 | 57 | -------------------------------------------------------------------------------- /certifications/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CertificationsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "certifications" 7 | 8 | def ready(self): 9 | import certifications.signals 10 | -------------------------------------------------------------------------------- /certifications/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [ 10 | ("activity", "0001_initial"), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name="Certification", 16 | fields=[ 17 | ( 18 | "id", 19 | models.BigAutoField( 20 | auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name="ID", 24 | ), 25 | ), 26 | ("name", models.CharField(max_length=255)), 27 | ("issuing_organization", models.CharField(max_length=255)), 28 | ("issue_date", models.DateField()), 29 | ("expiration_date", models.DateField(blank=True, null=True)), 30 | ("credential_id", models.CharField(blank=True, max_length=255)), 31 | ("credential_url", models.URLField(blank=True)), 32 | ("description", models.TextField(blank=True)), 33 | ( 34 | "certificate_image", 35 | models.ImageField(blank=True, upload_to="certificates/"), 36 | ), 37 | ("verification_status", models.BooleanField(default=False)), 38 | ( 39 | "categories", 40 | models.ManyToManyField( 41 | related_name="certifications_categories", to="activity.category" 42 | ), 43 | ), 44 | ], 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /certifications/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [ 10 | ("certifications", "0001_initial"), 11 | ("courses", "0001_initial"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="certification", 17 | name="related_courses", 18 | field=models.ManyToManyField( 19 | blank=True, related_name="courses_certifications", to="courses.course" 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /certifications/migrations/0003_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [ 10 | ("certifications", "0002_initial"), 11 | ("jobs", "0001_initial"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="certification", 17 | name="related_jobs", 18 | field=models.ManyToManyField( 19 | blank=True, related_name="job_certifications", to="jobs.joblisting" 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /certifications/migrations/0004_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | initial = True 10 | 11 | dependencies = [ 12 | ("certifications", "0003_initial"), 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name="certification", 19 | name="user", 20 | field=models.ForeignKey( 21 | on_delete=django.db.models.deletion.CASCADE, 22 | related_name="user_certifications", 23 | to=settings.AUTH_USER_MODEL, 24 | ), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /certifications/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/migrations/__init__.py -------------------------------------------------------------------------------- /certifications/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/migrations/__pycache__/0003_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/migrations/__pycache__/0003_initial.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/migrations/__pycache__/0004_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/migrations/__pycache__/0004_initial.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/certifications/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /certifications/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.conf import settings 3 | from activity.models import Attachment 4 | from django.contrib.contenttypes.fields import GenericRelation 5 | 6 | class Certification(models.Model): 7 | user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_certifications', on_delete=models.CASCADE) 8 | name = models.CharField(max_length=255) 9 | attachments = GenericRelation(Attachment) 10 | issuing_organization = models.CharField(max_length=255) 11 | issue_date = models.DateField() 12 | expiration_date = models.DateField(null=True, blank=True) 13 | credential_id = models.CharField(max_length=255, blank=True) 14 | credential_url = models.URLField(blank=True) 15 | description = models.TextField(blank=True) 16 | categories = models.ManyToManyField('activity.Category', related_name='certifications_categories') 17 | certificate_image = models.ImageField(upload_to='certificates/', blank=True) 18 | verification_status = models.BooleanField(default=False) 19 | related_jobs = models.ManyToManyField('jobs.JobListing', related_name='job_certifications', blank=True) 20 | related_courses = models.ManyToManyField('courses.Course', related_name='courses_certifications', blank=True) 21 | 22 | def __str__(self): 23 | return f"{self.name} - {self.user.user.username}" 24 | -------------------------------------------------------------------------------- /certifications/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save, post_delete 2 | from django.dispatch import receiver 3 | from .models import Certification 4 | from profiles.models import User, UserProfile 5 | from jobs.models import JobListing 6 | from courses.models import Course 7 | from notifications.models import Notification 8 | 9 | # Signal to send notification when a new certification is created 10 | @receiver(post_save, sender=Certification) 11 | def send_certification_notification(sender, instance, created, **kwargs): 12 | if created: 13 | user = instance.user 14 | notification_message = f"Congratulations! You have earned the {instance.name} certification." 15 | Notification.objects.create(user=user, message=notification_message) 16 | 17 | # Signal to update related job listings when a user earns a new certification 18 | @receiver(post_save, sender=Certification) 19 | def update_related_jobs(sender, instance, created, **kwargs): 20 | if created: 21 | related_jobs = instance.related_jobs.all() 22 | for job in related_jobs: 23 | job.required_certifications.add(instance) 24 | 25 | # Signal to update related courses when a user earns a new certification 26 | @receiver(post_save, sender=Certification) 27 | def update_related_courses(sender, instance, created, **kwargs): 28 | if created: 29 | related_courses = instance.related_courses.all() 30 | for course in related_courses: 31 | course.required_certifications.add(instance) 32 | 33 | # Signal to update certification count when a certification is created or deleted 34 | @receiver(post_save, sender=Certification) 35 | @receiver(post_delete, sender=Certification) 36 | def update_certification_count(sender, instance, **kwargs): 37 | user = instance.user 38 | user.certification_count = user.certifications.count() 39 | user.save() 40 | 41 | # Signal to notify users when a certification is updated 42 | @receiver(post_save, sender=Certification) 43 | def notify_users_on_certification_update(sender, instance, **kwargs): 44 | if not instance.verification_status: 45 | user = instance.user 46 | notification_message = f"Your {instance.name} certification has been updated. Please verify your information." 47 | Notification.objects.create(user=user, message=notification_message) 48 | -------------------------------------------------------------------------------- /certifications/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /certifications/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /companies/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/__init__.py -------------------------------------------------------------------------------- /companies/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /companies/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /companies/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /companies/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /companies/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /companies/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Company, CompanyUpdate 3 | from activity.models import Category, Attachment 4 | from django.contrib.contenttypes.admin import GenericTabularInline 5 | 6 | # Inline for GenericRelation Attachment 7 | class AttachmentInline(GenericTabularInline): 8 | model = Attachment 9 | extra = 1 10 | 11 | # Inline for Company Updates 12 | class CompanyUpdateInline(admin.TabularInline): 13 | model = CompanyUpdate 14 | extra = 1 15 | fields = ('title', 'content', 'created_at') 16 | readonly_fields = ('created_at',) 17 | 18 | @admin.register(Company) 19 | class CompanyAdmin(admin.ModelAdmin): 20 | list_display = ('name', 'website', 'location', 'industry', 'founded_date', 'employee_count') 21 | list_filter = ('industry', 'founded_date', 'employee_count') 22 | search_fields = ('name', 'location', 'industry', 'description') 23 | 24 | fieldsets = ( 25 | ('Company Information', { 26 | 'fields': ('name', 'website', 'location', 'industry', 'description', 'logo') 27 | }), 28 | ('Additional Details', { 29 | 'fields': ('founded_date', 'employee_count', 'revenue', 'categories', 'members', 'followers') 30 | }), 31 | ) 32 | 33 | filter_horizontal = ('categories', 'members', 'followers') 34 | inlines = [AttachmentInline, CompanyUpdateInline] 35 | 36 | @admin.register(CompanyUpdate) 37 | class CompanyUpdateAdmin(admin.ModelAdmin): 38 | list_display = ('company', 'title', 'created_at') 39 | list_filter = ('created_at',) 40 | search_fields = ('company__name', 'title') 41 | 42 | fieldsets = ( 43 | ('Company Update Details', { 44 | 'fields': ('company', 'title', 'content', 'created_at') 45 | }), 46 | ) 47 | 48 | readonly_fields = ('created_at',) 49 | 50 | def get_queryset(self, request): 51 | """Limit queryset to current user's Company Updates.""" 52 | qs = super().get_queryset(request) 53 | if request.user.is_superuser: 54 | return qs 55 | return qs.filter(company__members=request.user) 56 | 57 | def save_model(self, request, obj, form, change): 58 | """Override save_model to associate the current user with the Company Update.""" 59 | if not obj.pk: 60 | obj.company = Company.objects.get(id=form.data.get('company')) 61 | obj.save() 62 | 63 | 64 | -------------------------------------------------------------------------------- /companies/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CompaniesConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "companies" 7 | 8 | def ready(self): 9 | import companies.signals 10 | -------------------------------------------------------------------------------- /companies/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [ 10 | ("activity", "0001_initial"), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name="CompanyUpdate", 16 | fields=[ 17 | ( 18 | "id", 19 | models.BigAutoField( 20 | auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name="ID", 24 | ), 25 | ), 26 | ("title", models.CharField(max_length=255)), 27 | ("content", models.TextField()), 28 | ("created_at", models.DateTimeField(auto_now_add=True)), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name="Company", 33 | fields=[ 34 | ( 35 | "id", 36 | models.BigAutoField( 37 | auto_created=True, 38 | primary_key=True, 39 | serialize=False, 40 | verbose_name="ID", 41 | ), 42 | ), 43 | ("name", models.CharField(max_length=255)), 44 | ("website", models.URLField(blank=True)), 45 | ("location", models.CharField(blank=True, max_length=255)), 46 | ("industry", models.CharField(blank=True, max_length=255)), 47 | ("description", models.TextField(blank=True)), 48 | ( 49 | "logo", 50 | models.ImageField( 51 | blank=True, null=True, upload_to="company_logos/" 52 | ), 53 | ), 54 | ("founded_date", models.DateField(blank=True, null=True)), 55 | ("employee_count", models.IntegerField(default=0)), 56 | ( 57 | "revenue", 58 | models.DecimalField( 59 | blank=True, decimal_places=2, max_digits=12, null=True 60 | ), 61 | ), 62 | ( 63 | "categories", 64 | models.ManyToManyField( 65 | related_name="companies_categories", to="activity.category" 66 | ), 67 | ), 68 | ], 69 | ), 70 | ] 71 | -------------------------------------------------------------------------------- /companies/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | initial = True 10 | 11 | dependencies = [ 12 | ("companies", "0001_initial"), 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name="company", 19 | name="followers", 20 | field=models.ManyToManyField( 21 | related_name="followed_companies", to=settings.AUTH_USER_MODEL 22 | ), 23 | ), 24 | migrations.AddField( 25 | model_name="company", 26 | name="members", 27 | field=models.ManyToManyField( 28 | related_name="member_companies", to=settings.AUTH_USER_MODEL 29 | ), 30 | ), 31 | migrations.AddField( 32 | model_name="companyupdate", 33 | name="company", 34 | field=models.ForeignKey( 35 | on_delete=django.db.models.deletion.CASCADE, 36 | related_name="company_updates", 37 | to="companies.company", 38 | ), 39 | ), 40 | ] 41 | -------------------------------------------------------------------------------- /companies/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/migrations/__init__.py -------------------------------------------------------------------------------- /companies/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /companies/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /companies/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/companies/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /companies/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from activity.models import Attachment, Category 3 | from django.contrib.contenttypes.fields import GenericRelation 4 | from django.conf import settings 5 | 6 | 7 | class Company(models.Model): 8 | name = models.CharField(max_length=255) 9 | website = models.URLField(blank=True) 10 | location = models.CharField(max_length=255, blank=True) 11 | industry = models.CharField(max_length=255, blank=True) 12 | description = models.TextField(blank=True) 13 | attachments = GenericRelation(Attachment) 14 | categories = models.ManyToManyField(Category, related_name='companies_categories') 15 | logo = models.ImageField(upload_to='company_logos/', blank=True, null=True) 16 | founded_date = models.DateField(null=True, blank=True) 17 | employee_count = models.IntegerField(default=0) 18 | revenue = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True) 19 | members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='member_companies') 20 | followers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='followed_companies') 21 | 22 | def __str__(self): 23 | return self.name 24 | 25 | 26 | class CompanyUpdate(models.Model): 27 | company = models.ForeignKey(Company, related_name='company_updates', on_delete=models.CASCADE) 28 | title = models.CharField(max_length=255) 29 | content = models.TextField() 30 | attachments = GenericRelation(Attachment) 31 | created_at = models.DateTimeField(auto_now_add=True) 32 | 33 | def __str__(self): 34 | return f"{self.company.name} Update: {self.title}" -------------------------------------------------------------------------------- /companies/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save, post_delete 2 | from django.dispatch import receiver 3 | from .models import Company, CompanyUpdate 4 | from profiles.models import User, UserProfile 5 | from notifications.models import Notification, NotificationType 6 | 7 | # Signal to create a company profile when a new user is created 8 | @receiver(post_save, sender=UserProfile) 9 | def create_company_profile(sender, instance, created, **kwargs): 10 | if created: 11 | Company.objects.create(name=f"{instance.user.username}'s Company") 12 | 13 | # Signal to send notification when a new company update is created 14 | @receiver(post_save, sender=CompanyUpdate) 15 | def send_company_update_notification(sender, instance, created, **kwargs): 16 | if created: 17 | company_followers = instance.company.followers.all() 18 | notification_message = f"New update from {instance.company.name}: {instance.title}" 19 | for follower in company_followers: 20 | Notification.objects.create(user=follower, message=notification_message) 21 | 22 | # Signal to update follower count when a user follows or unfollows a company 23 | @receiver(post_save, sender=Company.followers.through) 24 | @receiver(post_delete, sender=Company.followers.through) 25 | def update_follower_count(sender, instance, **kwargs): 26 | company = instance.company 27 | company.follower_count = company.followers.count() 28 | company.save() 29 | 30 | # Signal to update company member count when a user joins or leaves a company 31 | @receiver(post_save, sender=Company.members.through) 32 | @receiver(post_delete, sender=Company.members.through) 33 | def update_member_count(sender, instance, **kwargs): 34 | company = instance.company 35 | company.member_count = company.members.count() 36 | company.save() 37 | 38 | # Signal to delete associated company updates when a company is deleted 39 | @receiver(post_save, sender=Company) 40 | def notify_admins_on_company_creation(sender, instance, created, **kwargs): 41 | if created: 42 | notification_message = f"A new company '{instance.name}' has been created." 43 | notification_type = NotificationType.objects.get_or_create(type_name='company_creation')[0] 44 | admin_profiles = UserProfile.objects.filter(user__is_staff=True) 45 | 46 | for admin_profile in admin_profiles: 47 | Notification.objects.create( 48 | recipient=admin_profile.user, 49 | content_object=instance, 50 | content=notification_message, 51 | url=f'/companies/{instance.id}/', 52 | notification_type=notification_type 53 | ) -------------------------------------------------------------------------------- /companies/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /companies/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /connections/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/__init__.py -------------------------------------------------------------------------------- /connections/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /connections/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /connections/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /connections/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /connections/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /connections/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import ConnectionRequest, Connection, Recommendation 3 | from profiles.models import UserProfile 4 | 5 | # Inline for Connection in UserProfileAdmin 6 | class ConnectionInline(admin.TabularInline): 7 | model = Connection 8 | fk_name = 'user' 9 | verbose_name_plural = 'Connections' 10 | readonly_fields = ('created_at',) 11 | 12 | # Inline for Recommendation in UserProfileAdmin 13 | class RecommendationInline(admin.TabularInline): 14 | model = Recommendation 15 | fk_name = 'recommended_user' 16 | verbose_name_plural = 'Recommendations' 17 | 18 | @admin.register(ConnectionRequest) 19 | class ConnectionRequestAdmin(admin.ModelAdmin): 20 | list_display = ('from_user', 'to_user', 'status', 'created_at', 'updated_at') 21 | list_filter = ('status', 'created_at', 'updated_at') 22 | search_fields = ('from_user__user__username', 'to_user__user__username') 23 | 24 | def save_model(self, request, obj, form, change): 25 | """Override save_model to associate the current user with the ConnectionRequest.""" 26 | if not obj.pk: 27 | obj.from_user = UserProfile.objects.get(user=request.user) 28 | obj.save() 29 | 30 | @admin.register(Connection) 31 | class ConnectionAdmin(admin.ModelAdmin): 32 | list_display = ('user', 'connection', 'created_at') 33 | list_filter = ('created_at',) 34 | search_fields = ('user__user__username', 'connection__user__username') 35 | 36 | @admin.register(Recommendation) 37 | class RecommendationAdmin(admin.ModelAdmin): 38 | list_display = ('recommended_by', 'recommended_user', 'content') 39 | search_fields = ('recommended_by__user__username', 'recommended_user__user__username') 40 | 41 | 42 | # Register Inline models in UserProfileAdmin 43 | 44 | class UserProfileAdmin(admin.ModelAdmin): 45 | list_display = ('user', 'location', 'role', 'is_verified') 46 | list_filter = ('role', 'is_verified') 47 | search_fields = ('user__username', 'location') 48 | 49 | inlines = [ConnectionInline, RecommendationInline] 50 | 51 | def get_queryset(self, request): 52 | """Limit queryset to current user's UserProfile.""" 53 | qs = super().get_queryset(request) 54 | if not request.user.is_superuser: 55 | return qs.filter(user=request.user) 56 | return qs 57 | 58 | def save_model(self, request, obj, form, change): 59 | """Override save_model to associate the current user with the UserProfile.""" 60 | if not obj.pk: 61 | obj.user = request.user 62 | obj.save() 63 | 64 | -------------------------------------------------------------------------------- /connections/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ConnectionsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "connections" 7 | 8 | def ready(self): 9 | import connections.signals 10 | -------------------------------------------------------------------------------- /connections/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name="Connection", 14 | fields=[ 15 | ( 16 | "id", 17 | models.BigAutoField( 18 | auto_created=True, 19 | primary_key=True, 20 | serialize=False, 21 | verbose_name="ID", 22 | ), 23 | ), 24 | ("created_at", models.DateTimeField(auto_now_add=True)), 25 | ], 26 | ), 27 | migrations.CreateModel( 28 | name="ConnectionRequest", 29 | fields=[ 30 | ( 31 | "id", 32 | models.BigAutoField( 33 | auto_created=True, 34 | primary_key=True, 35 | serialize=False, 36 | verbose_name="ID", 37 | ), 38 | ), 39 | ( 40 | "status", 41 | models.CharField( 42 | choices=[ 43 | ("pending", "Pending"), 44 | ("accepted", "Accepted"), 45 | ("rejected", "Rejected"), 46 | ], 47 | default="pending", 48 | max_length=10, 49 | ), 50 | ), 51 | ("created_at", models.DateTimeField(auto_now_add=True)), 52 | ("updated_at", models.DateTimeField(auto_now=True)), 53 | ], 54 | ), 55 | migrations.CreateModel( 56 | name="Recommendation", 57 | fields=[ 58 | ( 59 | "id", 60 | models.BigAutoField( 61 | auto_created=True, 62 | primary_key=True, 63 | serialize=False, 64 | verbose_name="ID", 65 | ), 66 | ), 67 | ("content", models.TextField()), 68 | ], 69 | ), 70 | ] 71 | -------------------------------------------------------------------------------- /connections/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | initial = True 9 | 10 | dependencies = [ 11 | ("connections", "0001_initial"), 12 | ("profiles", "0001_initial"), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name="connection", 18 | name="connection", 19 | field=models.ForeignKey( 20 | on_delete=django.db.models.deletion.CASCADE, 21 | related_name="connected_users", 22 | to="profiles.userprofile", 23 | ), 24 | ), 25 | migrations.AddField( 26 | model_name="connection", 27 | name="user", 28 | field=models.ForeignKey( 29 | on_delete=django.db.models.deletion.CASCADE, 30 | related_name="connections", 31 | to="profiles.userprofile", 32 | ), 33 | ), 34 | migrations.AddField( 35 | model_name="connectionrequest", 36 | name="from_user", 37 | field=models.ForeignKey( 38 | on_delete=django.db.models.deletion.CASCADE, 39 | related_name="connection_requests_sent", 40 | to="profiles.userprofile", 41 | ), 42 | ), 43 | migrations.AddField( 44 | model_name="connectionrequest", 45 | name="to_user", 46 | field=models.ForeignKey( 47 | on_delete=django.db.models.deletion.CASCADE, 48 | related_name="connection_requests_received", 49 | to="profiles.userprofile", 50 | ), 51 | ), 52 | migrations.AddField( 53 | model_name="recommendation", 54 | name="recommended_by", 55 | field=models.ForeignKey( 56 | on_delete=django.db.models.deletion.CASCADE, 57 | related_name="recommendations_given", 58 | to="profiles.userprofile", 59 | ), 60 | ), 61 | migrations.AddField( 62 | model_name="recommendation", 63 | name="recommended_user", 64 | field=models.ForeignKey( 65 | on_delete=django.db.models.deletion.CASCADE, 66 | related_name="recommendations_received", 67 | to="profiles.userprofile", 68 | ), 69 | ), 70 | ] 71 | -------------------------------------------------------------------------------- /connections/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/migrations/__init__.py -------------------------------------------------------------------------------- /connections/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /connections/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /connections/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/connections/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /connections/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from profiles.models import UserProfile 3 | 4 | class ConnectionRequest(models.Model): 5 | from_user = models.ForeignKey(UserProfile, related_name='connection_requests_sent', on_delete=models.CASCADE) 6 | to_user = models.ForeignKey(UserProfile, related_name='connection_requests_received', on_delete=models.CASCADE) 7 | status = models.CharField(max_length=10, choices=[('pending', 'Pending'), ('accepted', 'Accepted'), ('rejected', 'Rejected')], default='pending') 8 | created_at = models.DateTimeField(auto_now_add=True) 9 | updated_at = models.DateTimeField(auto_now=True) 10 | 11 | def __str__(self): 12 | return f"{self.from_user} wants to connect with {self.to_user}" 13 | 14 | def accept(self): 15 | self.status = 'accepted' 16 | Connection.objects.create(user=self.to_user, connection=self.from_user) 17 | self.save() 18 | 19 | def reject(self): 20 | self.status = 'rejected' 21 | self.save() 22 | 23 | class Connection(models.Model): 24 | user = models.ForeignKey(UserProfile, related_name='connections', on_delete=models.CASCADE) 25 | connection = models.ForeignKey(UserProfile, related_name='connected_users', on_delete=models.CASCADE) 26 | created_at = models.DateTimeField(auto_now_add=True) 27 | 28 | def __str__(self): 29 | return f"{self.user} is connected with {self.connection}" 30 | 31 | class Recommendation(models.Model): 32 | recommended_by = models.ForeignKey(UserProfile, related_name='recommendations_given', on_delete=models.CASCADE) 33 | recommended_user = models.ForeignKey(UserProfile, related_name='recommendations_received', on_delete=models.CASCADE) 34 | content = models.TextField() 35 | 36 | def __str__(self): 37 | return f"{self.recommended_by} recommended {self.recommended_user}" -------------------------------------------------------------------------------- /connections/routing.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | from . import consumers 3 | 4 | websocket_urlpatterns = [ 5 | re_path(r'ws/connection/(?P\w+)/$', consumers.ConnectionConsumer.as_asgi()), 6 | ] -------------------------------------------------------------------------------- /connections/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save, post_delete 2 | from django.dispatch import receiver 3 | from .models import ConnectionRequest, Connection, Recommendation 4 | from notifications.models import Notification 5 | 6 | # Signal to send notification when a connection request is sent 7 | @receiver(post_save, sender=ConnectionRequest) 8 | def send_connection_request_notification(sender, instance, created, **kwargs): 9 | if created: 10 | to_user = instance.to_user.user 11 | from_user = instance.from_user.userprofile 12 | notification_message = f"{from_user} wants to connect with you." 13 | Notification.objects.create(user=to_user, message=notification_message) 14 | 15 | # Signal to send notification when a connection request is accepted 16 | @receiver(post_save, sender=ConnectionRequest) 17 | def send_connection_accept_notification(sender, instance, created, **kwargs): 18 | if not created and instance.status == 'accepted': 19 | to_user = instance.from_user.user 20 | from_user = instance.to_user.userprofile 21 | notification_message = f"{from_user} accepted your connection request." 22 | Notification.objects.create(user=to_user, message=notification_message) 23 | 24 | # Signal to establish connection when a connection request is accepted 25 | @receiver(post_save, sender=ConnectionRequest) 26 | def establish_connection(sender, instance, created, **kwargs): 27 | if not created and instance.status == 'accepted': 28 | Connection.objects.create(user=instance.from_user, connection=instance.to_user) 29 | 30 | # Signal to send recommendation notification 31 | @receiver(post_save, sender=Recommendation) 32 | def send_recommendation_notification(sender, instance, created, **kwargs): 33 | if created: 34 | recommended_user = instance.recommended_user.user 35 | recommended_by = instance.recommended_by.userprofile 36 | notification_message = f"{recommended_by} recommended you." 37 | Notification.objects.create(user=recommended_user, message=notification_message) 38 | 39 | # Signal to update connection count when a new connection is established 40 | @receiver(post_save, sender=Connection) 41 | def update_connection_count(sender, instance, created, **kwargs): 42 | if created: 43 | user = instance.user.userprofile 44 | user.connection_count = Connection.objects.filter(user=user).count() 45 | user.save() 46 | 47 | # Signal to delete associated connection requests when a connection is deleted 48 | @receiver(post_delete, sender=Connection) 49 | def delete_associated_connection_requests(sender, instance, **kwargs): 50 | ConnectionRequest.objects.filter(from_user=instance.user).delete() 51 | -------------------------------------------------------------------------------- /connections/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /connections/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /courses/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/__init__.py -------------------------------------------------------------------------------- /courses/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /courses/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /courses/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /courses/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /courses/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /courses/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django import forms 3 | from taggit.forms import TagWidget 4 | from django.contrib.contenttypes.admin import GenericTabularInline 5 | from .models import Course, CourseEnrollment, CourseCompletion 6 | from certifications.models import Certification 7 | from activity.models import Attachment 8 | from posts.models import Comment 9 | 10 | # Inline for GenericRelation Attachment 11 | class AttachmentInline(GenericTabularInline): 12 | model = Attachment 13 | extra = 1 14 | ct_field = 'content_type' 15 | ct_fk_field = 'object_id' 16 | 17 | # Inline for CourseEnrollment in UserProfileAdmin 18 | class CourseEnrollmentInline(admin.TabularInline): 19 | model = CourseEnrollment 20 | extra = 1 21 | readonly_fields = ('enrolled_at',) 22 | 23 | # Inline for CourseCompletion in UserProfileAdmin 24 | class CourseCompletionInline(admin.TabularInline): 25 | model = CourseCompletion 26 | extra = 1 27 | readonly_fields = ('completed_at',) 28 | 29 | # Custom Admin Forms 30 | class CourseAdminForm(forms.ModelForm): 31 | class Meta: 32 | model = Course 33 | fields = '__all__' 34 | widgets = { 35 | 'tags': TagWidget, 36 | } 37 | 38 | @admin.register(Course) 39 | class CourseAdmin(admin.ModelAdmin): 40 | form = CourseAdminForm 41 | list_display = ('title', 'instructor', 'get_students_count', 'get_completions_count') 42 | list_filter = ('instructor', 'categories') 43 | search_fields = ('title', 'instructor__username') 44 | 45 | fieldsets = ( 46 | ('Course Information', { 47 | 'fields': ('title', 'description', 'instructor', 'categories', 'tags') 48 | }), 49 | ('Related Content', { 50 | 'fields': ('shares', 'comments', 'reactions') 51 | }), 52 | ) 53 | 54 | inlines = [AttachmentInline, CourseEnrollmentInline, CourseCompletionInline] 55 | 56 | def get_queryset(self, request): 57 | """Limit queryset to current user's instructed courses.""" 58 | qs = super().get_queryset(request) 59 | if not request.user.is_superuser: 60 | return qs.filter(instructor=request.user) 61 | return qs 62 | 63 | def get_students_count(self, obj): 64 | """Custom method to display number of enrolled students.""" 65 | return obj.students.count() 66 | get_students_count.short_description = 'Students' 67 | 68 | def get_completions_count(self, obj): 69 | """Custom method to display number of course completions.""" 70 | return obj.completions.count() 71 | get_completions_count.short_description = 'Completions' 72 | 73 | @admin.register(CourseEnrollment) 74 | class CourseEnrollmentAdmin(admin.ModelAdmin): 75 | list_display = ('course', 'student', 'enrolled_at') 76 | list_filter = ('enrolled_at',) 77 | search_fields = ('course__title', 'student__user__username') 78 | 79 | @admin.register(CourseCompletion) 80 | class CourseCompletionAdmin(admin.ModelAdmin): 81 | list_display = ('course', 'student', 'completed_at', 'certificate_url', 'certificate') 82 | list_filter = ('completed_at',) 83 | search_fields = ('course__title', 'student__user__username') 84 | 85 | 86 | -------------------------------------------------------------------------------- /courses/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CoursesConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "courses" 7 | 8 | def ready(self): 9 | import courses.signals 10 | -------------------------------------------------------------------------------- /courses/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [ 10 | ("activity", "0001_initial"), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name="CourseCompletion", 16 | fields=[ 17 | ( 18 | "id", 19 | models.BigAutoField( 20 | auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name="ID", 24 | ), 25 | ), 26 | ("completed_at", models.DateTimeField(auto_now_add=True)), 27 | ("certificate_url", models.URLField()), 28 | ], 29 | ), 30 | migrations.CreateModel( 31 | name="CourseEnrollment", 32 | fields=[ 33 | ( 34 | "id", 35 | models.BigAutoField( 36 | auto_created=True, 37 | primary_key=True, 38 | serialize=False, 39 | verbose_name="ID", 40 | ), 41 | ), 42 | ("enrolled_at", models.DateTimeField(auto_now_add=True)), 43 | ], 44 | ), 45 | migrations.CreateModel( 46 | name="Course", 47 | fields=[ 48 | ( 49 | "id", 50 | models.BigAutoField( 51 | auto_created=True, 52 | primary_key=True, 53 | serialize=False, 54 | verbose_name="ID", 55 | ), 56 | ), 57 | ("title", models.CharField(max_length=255)), 58 | ("description", models.TextField()), 59 | ( 60 | "categories", 61 | models.ManyToManyField( 62 | related_name="courses_categories", to="activity.category" 63 | ), 64 | ), 65 | ], 66 | ), 67 | ] 68 | -------------------------------------------------------------------------------- /courses/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [ 10 | ("courses", "0001_initial"), 11 | ("posts", "0001_initial"), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name="course", 17 | name="comments", 18 | field=models.ManyToManyField( 19 | blank=True, related_name="course_comments", to="posts.comment" 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /courses/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/migrations/__init__.py -------------------------------------------------------------------------------- /courses/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /courses/migrations/__pycache__/0002_alter_course_tags_alter_coursecompletion_tags.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/migrations/__pycache__/0002_alter_course_tags_alter_coursecompletion_tags.cpython-312.pyc -------------------------------------------------------------------------------- /courses/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /courses/migrations/__pycache__/0003_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/migrations/__pycache__/0003_initial.cpython-312.pyc -------------------------------------------------------------------------------- /courses/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/courses/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /courses/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from activity.models import Attachment 3 | # from posts.models import Comment 4 | # from certifications.models import Certification 5 | from taggit.managers import TaggableManager 6 | from django.contrib.contenttypes.fields import GenericRelation 7 | from django.conf import settings 8 | 9 | 10 | class Course(models.Model): 11 | title = models.CharField(max_length=255) 12 | description = models.TextField() 13 | attachments = GenericRelation(Attachment) 14 | categories = models.ManyToManyField('activity.Category', related_name='courses_categories') 15 | instructor = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='instructed_courses', on_delete=models.CASCADE) 16 | students = models.ManyToManyField(settings.AUTH_USER_MODEL, through='CourseEnrollment', related_name='enrolled_courses') 17 | shares = models.ManyToManyField('activity.Share', related_name='course_shares', blank=True) 18 | comments = models.ManyToManyField('posts.Comment', related_name='course_comments', blank=True) 19 | reactions = models.ManyToManyField('activity.Reaction', related_name='course_reactions', blank=True) 20 | tags = TaggableManager() 21 | 22 | def __str__(self): 23 | return self.title 24 | 25 | class CourseEnrollment(models.Model): 26 | course = models.ForeignKey(Course, related_name='enrolled_courses', on_delete=models.CASCADE) 27 | student = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 28 | enrolled_at = models.DateTimeField(auto_now_add=True) 29 | 30 | def __str__(self): 31 | return f"{self.student.user.username} enrolled in {self.course.title}" 32 | 33 | class CourseCompletion(models.Model): 34 | course = models.ForeignKey(Course, related_name='completions', on_delete=models.CASCADE) 35 | student = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='completed_courses', on_delete=models.CASCADE) 36 | completed_at = models.DateTimeField(auto_now_add=True) 37 | certificate_url = models.URLField() 38 | certificate = models.ForeignKey('certifications.Certification', related_name='course_completions', on_delete=models.SET_NULL, null=True, blank=True) 39 | tags = TaggableManager() 40 | 41 | def __str__(self): 42 | return f"{self.student.user.username} completed {self.course.title}" -------------------------------------------------------------------------------- /courses/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save, post_delete 2 | from django.dispatch import receiver 3 | from .models import Course, CourseEnrollment, CourseCompletion 4 | from notifications.models import Notification 5 | 6 | # Signal to send notification when a user enrolls in a course 7 | @receiver(post_save, sender=CourseEnrollment) 8 | def send_course_enrollment_notification(sender, instance, created, **kwargs): 9 | if created: 10 | user = instance.student.userprofile 11 | course = instance.course 12 | notification_message = f"You have enrolled in the course '{course.title}'." 13 | Notification.objects.create(user=user, message=notification_message) 14 | 15 | # Signal to send notification when a user completes a course 16 | @receiver(post_save, sender=CourseCompletion) 17 | def send_course_completion_notification(sender, instance, created, **kwargs): 18 | if created: 19 | user = instance.student.userprofile 20 | course = instance.course 21 | notification_message = f"Congratulations! You have completed the course '{course.title}'." 22 | Notification.objects.create(user=user, message=notification_message) 23 | 24 | # Signal to update completion count when a new course completion is added 25 | @receiver(post_save, sender=CourseCompletion) 26 | def update_completion_count(sender, instance, created, **kwargs): 27 | if created: 28 | course = instance.course 29 | course.completion_count = CourseCompletion.objects.filter(course=course).count() 30 | course.save() 31 | 32 | # Signal to update enrollment count when a new course enrollment is added 33 | @receiver(post_save, sender=CourseEnrollment) 34 | def update_enrollment_count(sender, instance, created, **kwargs): 35 | if created: 36 | course = instance.course 37 | course.enrollment_count = CourseEnrollment.objects.filter(course=course).count() 38 | course.save() 39 | 40 | # Signal to update enrollment count when a course enrollment is deleted 41 | @receiver(post_delete, sender=CourseEnrollment) 42 | def decrease_enrollment_count(sender, instance, **kwargs): 43 | course = instance.course 44 | course.enrollment_count = CourseEnrollment.objects.filter(course=course).count() 45 | course.save() 46 | 47 | # Signal to update course duration when start or end time is changed 48 | @receiver(post_save, sender=Course) 49 | def update_course_duration(sender, instance, created, **kwargs): 50 | if not created: 51 | instance.duration_days = (instance.end_time - instance.start_time).days 52 | instance.save() 53 | 54 | # Signal to delete associated course completions when a course is deleted 55 | @receiver(post_delete, sender=Course) 56 | def delete_associated_course_completions(sender, instance, **kwargs): 57 | CourseCompletion.objects.filter(course=instance).delete() 58 | 59 | # Signal to delete associated course enrollments when a course is deleted 60 | @receiver(post_delete, sender=Course) 61 | def delete_associated_course_enrollments(sender, instance, **kwargs): 62 | CourseEnrollment.objects.filter(course=instance).delete() 63 | -------------------------------------------------------------------------------- /courses/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /courses/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/db.sqlite3 -------------------------------------------------------------------------------- /events/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/__init__.py -------------------------------------------------------------------------------- /events/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /events/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /events/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /events/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /events/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /events/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Event 3 | from profiles.models import UserProfile 4 | from activity.models import Attachment, Category 5 | from django.contrib.contenttypes.admin import GenericTabularInline 6 | 7 | # Inline for GenericRelation Attachment 8 | class AttachmentInline(GenericTabularInline): 9 | model = Attachment 10 | extra = 1 11 | 12 | @admin.register(Event) 13 | class EventAdmin(admin.ModelAdmin): 14 | list_display = ('title', 'organizer', 'location', 'start_time', 'end_time', 'get_attendees_count') 15 | list_filter = ('organizer', 'location', 'start_time', 'end_time', 'event_date') 16 | search_fields = ('title', 'organizer__user__username', 'location') 17 | 18 | fieldsets = ( 19 | ('Event Information', { 20 | 'fields': ('title', 'description', 'organizer', 'categories', 'location', 'start_time', 'end_time', 'event_date', 'capacity', 'category') 21 | }), 22 | ) 23 | 24 | filter_horizontal = ('categories', 'attendees') 25 | inlines = [AttachmentInline] 26 | 27 | readonly_fields = ('get_attendees_count',) # Assuming this is a readonly method 28 | 29 | def get_attendees_count(self, obj): 30 | """Custom method to display number of attendees.""" 31 | return obj.attendees.count() 32 | get_attendees_count.short_description = 'Attendees' 33 | 34 | def save_model(self, request, obj, form, change): 35 | """Override save_model to associate the current user with the Event.""" 36 | if not obj.pk: # Only set organizer on creation 37 | obj.organizer = UserProfile.objects.get(user=request.user) 38 | obj.save() 39 | 40 | def get_form(self, request, obj=None, **kwargs): 41 | """Override get_form to exclude attachments field.""" 42 | form = super().get_form(request, obj, **kwargs) 43 | if 'attachments' in form.base_fields: 44 | del form.base_fields['attachments'] 45 | return form 46 | 47 | def get_inline_instances(self, request, obj=None): 48 | """Override get_inline_instances to pass request to inlines.""" 49 | inline_instances = [] 50 | for inline_class in self.inlines: 51 | inline = inline_class(self.model, self.admin_site) 52 | if request: 53 | inline.request = request 54 | inline_instances.append(inline) 55 | return inline_instances 56 | -------------------------------------------------------------------------------- /events/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class EventsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "events" 7 | 8 | def ready(self): 9 | import events.signals 10 | -------------------------------------------------------------------------------- /events/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name="Event", 14 | fields=[ 15 | ( 16 | "id", 17 | models.BigAutoField( 18 | auto_created=True, 19 | primary_key=True, 20 | serialize=False, 21 | verbose_name="ID", 22 | ), 23 | ), 24 | ("title", models.CharField(max_length=255)), 25 | ("description", models.TextField()), 26 | ("location", models.CharField(max_length=255)), 27 | ("start_time", models.DateTimeField()), 28 | ("end_time", models.DateTimeField()), 29 | ("event_date", models.DateField()), 30 | ("capacity", models.PositiveIntegerField()), 31 | ], 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /events/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | import taggit.managers 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | initial = True 10 | 11 | dependencies = [ 12 | ("activity", "0003_initial"), 13 | ("events", "0001_initial"), 14 | ("profiles", "0001_initial"), 15 | ( 16 | "taggit", 17 | "0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx", 18 | ), 19 | ] 20 | 21 | operations = [ 22 | migrations.AddField( 23 | model_name="event", 24 | name="attendees", 25 | field=models.ManyToManyField( 26 | blank=True, related_name="events", to="profiles.userprofile" 27 | ), 28 | ), 29 | migrations.AddField( 30 | model_name="event", 31 | name="categories", 32 | field=models.ManyToManyField( 33 | related_name="events_categoories", to="activity.category" 34 | ), 35 | ), 36 | migrations.AddField( 37 | model_name="event", 38 | name="organizer", 39 | field=models.ForeignKey( 40 | on_delete=django.db.models.deletion.CASCADE, 41 | related_name="organized_events", 42 | to="profiles.userprofile", 43 | ), 44 | ), 45 | migrations.AddField( 46 | model_name="event", 47 | name="tags", 48 | field=taggit.managers.TaggableManager( 49 | help_text="A comma-separated list of tags.", 50 | through="taggit.TaggedItem", 51 | to="taggit.Tag", 52 | verbose_name="Tags", 53 | ), 54 | ), 55 | ] 56 | -------------------------------------------------------------------------------- /events/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/migrations/__init__.py -------------------------------------------------------------------------------- /events/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /events/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /events/migrations/__pycache__/0002_remove_event_category_event_tags.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/migrations/__pycache__/0002_remove_event_category_event_tags.cpython-312.pyc -------------------------------------------------------------------------------- /events/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/events/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /events/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from profiles.models import UserProfile 3 | from activity.models import Attachment, Category 4 | from django.contrib.contenttypes.fields import GenericRelation 5 | from taggit.managers import TaggableManager 6 | 7 | class Event(models.Model): 8 | organizer = models.ForeignKey(UserProfile, related_name='organized_events', on_delete=models.CASCADE) 9 | title = models.CharField(max_length=255) 10 | description = models.TextField() 11 | attachments = GenericRelation(Attachment) 12 | categories = models.ManyToManyField(Category, related_name='events_categoories') 13 | location = models.CharField(max_length=255) 14 | start_time = models.DateTimeField() 15 | end_time = models.DateTimeField() 16 | attendees = models.ManyToManyField(UserProfile, related_name='events', blank=True) 17 | event_date = models.DateField() 18 | capacity = models.PositiveIntegerField() 19 | tags = TaggableManager() 20 | 21 | def __str__(self): 22 | return f'{self.title} organized by {self.organizer.user.username}' -------------------------------------------------------------------------------- /events/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save, post_delete 2 | from django.dispatch import receiver 3 | from .models import Event 4 | from notifications.models import Notification 5 | 6 | # Signal to send notification when a user is added to an event 7 | @receiver(post_save, sender=Event) 8 | def send_event_notification(sender, instance, created, **kwargs): 9 | if created: 10 | organizer = instance.organizer 11 | attendees = instance.attendees.exclude(id=organizer.id) # Exclude organizer from attendees 12 | notification_message = f"You have been added to the event '{instance.title}' organized by {organizer.user.username}." 13 | Notification.objects.bulk_create([Notification(user=user, message=notification_message) for user in attendees]) 14 | 15 | # Signal to send notification when a user leaves an event 16 | @receiver(post_delete, sender=Event.attendees.through) 17 | def send_leave_event_notification(sender, instance, **kwargs): 18 | user = instance.userprofile 19 | event = instance.event 20 | notification_message = f"You have left the event '{event.title}'." 21 | Notification.objects.create(user=user, message=notification_message) 22 | 23 | # Signal to update attendees count when a new attendee is added 24 | @receiver(post_save, sender=Event.attendees.through) 25 | def update_attendees_count(sender, instance, created, **kwargs): 26 | if created: 27 | event = instance.event 28 | event.attendees_count = event.attendees.count() 29 | event.save() -------------------------------------------------------------------------------- /events/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /events/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /followers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/__init__.py -------------------------------------------------------------------------------- /followers/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /followers/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /followers/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /followers/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /followers/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /followers/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Follower, FollowRequest, FollowNotification 3 | from django.conf import settings 4 | from django.contrib.contenttypes.admin import GenericTabularInline 5 | from django.utils import timezone 6 | 7 | # Inline for GenericRelation Attachment 8 | class FollowerInline(admin.TabularInline): 9 | model = Follower 10 | extra = 1 11 | readonly_fields = ('followed_at',) 12 | 13 | class FollowRequestAdmin(admin.ModelAdmin): 14 | list_display = ('from_user', 'to_user', 'status', 'created_at') 15 | list_filter = ('status', 'created_at') 16 | search_fields = ('from_user__user__username', 'to_user__user__username') 17 | 18 | def save_model(self, request, obj, form, change): 19 | """Override save_model to send notification on request acceptance.""" 20 | if obj.status == 'accepted': 21 | notification_message = f"{obj.from_user.user.username} started following you." 22 | FollowNotification.objects.create(user=obj.to_user, message=notification_message) 23 | obj.save() 24 | 25 | @admin.register(Follower) 26 | class FollowerAdmin(admin.ModelAdmin): 27 | list_display = ('user', 'follower', 'followed_at') 28 | list_filter = ('followed_at',) 29 | search_fields = ('user__user__username', 'follower__user__username') 30 | 31 | def save_model(self, request, obj, form, change): 32 | """Override save_model to send notification on new follower.""" 33 | if not obj.pk: # If new follower 34 | notification_message = f"{obj.follower.user.username} started following you." 35 | FollowNotification.objects.create(user=obj.user, message=notification_message) 36 | obj.save() 37 | 38 | @admin.register(FollowNotification) 39 | class FollowNotificationAdmin(admin.ModelAdmin): 40 | list_display = ('user', 'message', 'created_at') 41 | list_filter = ('created_at',) 42 | search_fields = ('user__user__username', 'message') 43 | 44 | # Register models 45 | admin.site.register(FollowRequest, FollowRequestAdmin) 46 | -------------------------------------------------------------------------------- /followers/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class FollowersConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "followers" 7 | 8 | def ready(self): 9 | import followers.signals 10 | -------------------------------------------------------------------------------- /followers/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.utils.timezone 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | initial = True 9 | 10 | dependencies = [] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="Follower", 15 | fields=[ 16 | ( 17 | "id", 18 | models.BigAutoField( 19 | auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name="ID", 23 | ), 24 | ), 25 | ( 26 | "followed_at", 27 | models.DateTimeField(default=django.utils.timezone.now), 28 | ), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name="FollowNotification", 33 | fields=[ 34 | ( 35 | "id", 36 | models.BigAutoField( 37 | auto_created=True, 38 | primary_key=True, 39 | serialize=False, 40 | verbose_name="ID", 41 | ), 42 | ), 43 | ("message", models.TextField()), 44 | ("created_at", models.DateTimeField(auto_now_add=True)), 45 | ], 46 | ), 47 | migrations.CreateModel( 48 | name="FollowRequest", 49 | fields=[ 50 | ( 51 | "id", 52 | models.BigAutoField( 53 | auto_created=True, 54 | primary_key=True, 55 | serialize=False, 56 | verbose_name="ID", 57 | ), 58 | ), 59 | ( 60 | "status", 61 | models.CharField( 62 | choices=[ 63 | ("pending", "Pending"), 64 | ("accepted", "Accepted"), 65 | ("rejected", "Rejected"), 66 | ], 67 | default="pending", 68 | max_length=10, 69 | ), 70 | ), 71 | ("created_at", models.DateTimeField(auto_now_add=True)), 72 | ("updated_at", models.DateTimeField(auto_now=True)), 73 | ("message", models.TextField(blank=True)), 74 | ], 75 | ), 76 | ] 77 | -------------------------------------------------------------------------------- /followers/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | initial = True 10 | 11 | dependencies = [ 12 | ("followers", "0001_initial"), 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name="follower", 19 | name="follower", 20 | field=models.ForeignKey( 21 | on_delete=django.db.models.deletion.CASCADE, 22 | related_name="user_following", 23 | to=settings.AUTH_USER_MODEL, 24 | ), 25 | ), 26 | migrations.AddField( 27 | model_name="follower", 28 | name="user", 29 | field=models.ForeignKey( 30 | on_delete=django.db.models.deletion.CASCADE, 31 | related_name="user_followers", 32 | to=settings.AUTH_USER_MODEL, 33 | ), 34 | ), 35 | migrations.AddField( 36 | model_name="follownotification", 37 | name="user", 38 | field=models.ForeignKey( 39 | on_delete=django.db.models.deletion.CASCADE, 40 | related_name="follow_notifications", 41 | to=settings.AUTH_USER_MODEL, 42 | ), 43 | ), 44 | migrations.AddField( 45 | model_name="followrequest", 46 | name="from_user", 47 | field=models.ForeignKey( 48 | on_delete=django.db.models.deletion.CASCADE, 49 | related_name="follow_requests_sent", 50 | to=settings.AUTH_USER_MODEL, 51 | ), 52 | ), 53 | migrations.AddField( 54 | model_name="followrequest", 55 | name="to_user", 56 | field=models.ForeignKey( 57 | on_delete=django.db.models.deletion.CASCADE, 58 | related_name="follow_requests_received", 59 | to=settings.AUTH_USER_MODEL, 60 | ), 61 | ), 62 | ] 63 | -------------------------------------------------------------------------------- /followers/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/migrations/__init__.py -------------------------------------------------------------------------------- /followers/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /followers/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /followers/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/followers/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /followers/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.utils import timezone 3 | from django.conf import settings 4 | 5 | class Follower(models.Model): 6 | user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_followers', on_delete=models.CASCADE) 7 | follower = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_following', on_delete=models.CASCADE) 8 | followed_at = models.DateTimeField(default=timezone.now) 9 | 10 | def __str__(self): 11 | return f"{self.follower.user.username} follows {self.user.user.username}" 12 | 13 | @staticmethod 14 | def is_follower(user, follower): 15 | return Follower.objects.filter(user=user, follower=follower).exists() 16 | 17 | @staticmethod 18 | def get_followers(user): 19 | return Follower.objects.filter(user=user) 20 | 21 | class FollowRequest(models.Model): 22 | from_user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='follow_requests_sent', on_delete=models.CASCADE) 23 | to_user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='follow_requests_received', on_delete=models.CASCADE) 24 | status = models.CharField(max_length=10, choices=[('pending', 'Pending'), ('accepted', 'Accepted'), ('rejected', 'Rejected')], default='pending') 25 | created_at = models.DateTimeField(auto_now_add=True) 26 | updated_at = models.DateTimeField(auto_now=True) 27 | message = models.TextField(blank=True) 28 | 29 | def __str__(self): 30 | return f"{self.from_user.user.username} wants to follow {self.to_user.user.username}" 31 | 32 | def accept(self): 33 | self.status = 'accepted' 34 | Follower.objects.create(user=self.to_user, follower=self.from_user) 35 | self.save() 36 | 37 | def reject(self): 38 | self.status = 'rejected' 39 | self.save() 40 | 41 | class FollowNotification(models.Model): 42 | user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='follow_notifications', on_delete=models.CASCADE) 43 | message = models.TextField() 44 | created_at = models.DateTimeField(auto_now_add=True) 45 | 46 | def __str__(self): 47 | return f"Notification for {self.user.user.username}: {self.message}" -------------------------------------------------------------------------------- /followers/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save, post_delete 2 | from django.dispatch import receiver 3 | from .models import Follower, FollowRequest, FollowNotification 4 | from notifications.models import Notification 5 | 6 | # Signal to send notification when a user gains a new follower 7 | @receiver(post_save, sender=Follower) 8 | def send_follow_notification(sender, instance, created, **kwargs): 9 | if created: 10 | user = instance.user 11 | follower = instance.follower 12 | notification_message = f"{follower.user.username} started following you." 13 | Notification.objects.create(user=user, message=notification_message) 14 | 15 | # Signal to send notification when a follow request is accepted 16 | @receiver(post_save, sender=FollowRequest) 17 | def send_follow_request_notification(sender, instance, created, **kwargs): 18 | if not created and instance.status == 'accepted': 19 | from_user = instance.from_user 20 | to_user = instance.to_user 21 | notification_message = f"{from_user.user.username} has accepted your follow request." 22 | Notification.objects.create(user=to_user, message=notification_message) 23 | 24 | # Signal to send notification when a follow request is received 25 | @receiver(post_save, sender=FollowRequest) 26 | def send_follow_request_received_notification(sender, instance, created, **kwargs): 27 | if created: 28 | from_user = instance.from_user 29 | to_user = instance.to_user 30 | notification_message = f"{from_user.user.username} wants to follow you." 31 | FollowNotification.objects.create(user=to_user, message=notification_message) 32 | 33 | # Signal to update follow count when a new follower is added 34 | @receiver(post_save, sender=Follower) 35 | def update_follow_count(sender, instance, created, **kwargs): 36 | if created: 37 | user = instance.user 38 | user.follow_count = Follower.objects.filter(user=user).count() 39 | user.save() -------------------------------------------------------------------------------- /followers/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /followers/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /groups/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/__init__.py -------------------------------------------------------------------------------- /groups/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /groups/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /groups/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /groups/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /groups/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /groups/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Group, GroupMembership 3 | from django.conf import settings 4 | from django.utils.html import format_html 5 | from activity.models import Share, Category 6 | 7 | # Inline for GroupMembership 8 | class GroupMembershipInline(admin.TabularInline): 9 | model = GroupMembership 10 | extra = 1 11 | readonly_fields = ('joined_at',) 12 | 13 | @admin.register(Group) 14 | class GroupAdmin(admin.ModelAdmin): 15 | list_display = ('name', 'description_summary', 'group_type', 'privacy_level', 'created_at') 16 | list_filter = ('group_type', 'privacy_level', 'categories') 17 | search_fields = ('name', 'description') 18 | 19 | def description_summary(self, obj): 20 | return obj.description[:100] + '...' if len(obj.description) > 100 else obj.description 21 | description_summary.short_description = 'Description' 22 | 23 | @admin.register(GroupMembership) 24 | class GroupMembershipAdmin(admin.ModelAdmin): 25 | list_display = ('user', 'group', 'role', 'joined_at') 26 | list_filter = ('role', 'joined_at') 27 | search_fields = ('user__user__username', 'group__name') 28 | 29 | -------------------------------------------------------------------------------- /groups/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class GroupsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "groups" 7 | 8 | def ready(self): 9 | import groups.signals 10 | -------------------------------------------------------------------------------- /groups/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | import taggit.managers 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | initial = True 10 | 11 | dependencies = [ 12 | ("activity", "0001_initial"), 13 | ( 14 | "taggit", 15 | "0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx", 16 | ), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name="Group", 22 | fields=[ 23 | ( 24 | "id", 25 | models.BigAutoField( 26 | auto_created=True, 27 | primary_key=True, 28 | serialize=False, 29 | verbose_name="ID", 30 | ), 31 | ), 32 | ("name", models.CharField(max_length=255)), 33 | ("description", models.TextField()), 34 | ("created_at", models.DateTimeField(auto_now_add=True)), 35 | ( 36 | "group_type", 37 | models.CharField( 38 | choices=[("public", "Public"), ("private", "Private")], 39 | default="public", 40 | max_length=20, 41 | ), 42 | ), 43 | ( 44 | "privacy_level", 45 | models.CharField( 46 | choices=[ 47 | ("low", "Low"), 48 | ("medium", "Medium"), 49 | ("high", "High"), 50 | ], 51 | default="medium", 52 | max_length=20, 53 | ), 54 | ), 55 | ( 56 | "cover_image", 57 | models.ImageField(blank=True, null=True, upload_to="group_covers/"), 58 | ), 59 | ( 60 | "categories", 61 | models.ManyToManyField( 62 | related_name="groupes_categories", to="activity.category" 63 | ), 64 | ), 65 | ( 66 | "shares", 67 | models.ManyToManyField( 68 | blank=True, 69 | db_index=True, 70 | related_name="group_shares", 71 | to="activity.share", 72 | ), 73 | ), 74 | ( 75 | "tags", 76 | taggit.managers.TaggableManager( 77 | help_text="A comma-separated list of tags.", 78 | through="taggit.TaggedItem", 79 | to="taggit.Tag", 80 | verbose_name="Tags", 81 | ), 82 | ), 83 | ], 84 | ), 85 | migrations.CreateModel( 86 | name="GroupMembership", 87 | fields=[ 88 | ( 89 | "id", 90 | models.BigAutoField( 91 | auto_created=True, 92 | primary_key=True, 93 | serialize=False, 94 | verbose_name="ID", 95 | ), 96 | ), 97 | ( 98 | "role", 99 | models.CharField( 100 | choices=[("admin", "Admin"), ("member", "Member")], 101 | default="member", 102 | max_length=20, 103 | ), 104 | ), 105 | ("joined_at", models.DateTimeField(auto_now_add=True)), 106 | ( 107 | "group", 108 | models.ForeignKey( 109 | on_delete=django.db.models.deletion.CASCADE, to="groups.group" 110 | ), 111 | ), 112 | ], 113 | ), 114 | ] 115 | -------------------------------------------------------------------------------- /groups/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | initial = True 10 | 11 | dependencies = [ 12 | ("groups", "0001_initial"), 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name="groupmembership", 19 | name="user", 20 | field=models.ForeignKey( 21 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL 22 | ), 23 | ), 24 | migrations.AddField( 25 | model_name="group", 26 | name="members", 27 | field=models.ManyToManyField( 28 | db_index=True, 29 | related_name="user_groups", 30 | through="groups.GroupMembership", 31 | to=settings.AUTH_USER_MODEL, 32 | ), 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /groups/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/migrations/__init__.py -------------------------------------------------------------------------------- /groups/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /groups/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /groups/migrations/__pycache__/0003_alter_group_tags.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/migrations/__pycache__/0003_alter_group_tags.cpython-312.pyc -------------------------------------------------------------------------------- /groups/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/groups/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /groups/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.conf import settings 3 | from activity.models import Reaction, Share, Category 4 | from taggit.managers import TaggableManager 5 | 6 | class Group(models.Model): 7 | name = models.CharField(max_length=255) 8 | description = models.TextField() 9 | categories = models.ManyToManyField(Category, related_name='groupes_categories') 10 | members = models.ManyToManyField(settings.AUTH_USER_MODEL, through='GroupMembership', related_name='user_groups', db_index=True) 11 | created_at = models.DateTimeField(auto_now_add=True) 12 | group_type = models.CharField(max_length=20, choices=[('public', 'Public'), ('private', 'Private')], default='public') 13 | privacy_level = models.CharField(max_length=20, choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], default='medium') 14 | tags = models.CharField(max_length=100, blank=True) 15 | cover_image = models.ImageField(upload_to='group_covers/', blank=True, null=True) 16 | shares = models.ManyToManyField(Share, related_name='group_shares', blank=True, db_index=True) 17 | tags = TaggableManager() 18 | 19 | def __str__(self): 20 | return self.name 21 | 22 | class GroupMembership(models.Model): 23 | user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 24 | group = models.ForeignKey(Group, on_delete=models.CASCADE) 25 | role = models.CharField(max_length=20, choices=[('admin', 'Admin'), ('member', 'Member')], default='member') 26 | joined_at = models.DateTimeField(auto_now_add=True) 27 | 28 | def __str__(self): 29 | return f"{self.user.user.username} in {self.group.name}" -------------------------------------------------------------------------------- /groups/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save, post_delete 2 | from django.dispatch import receiver 3 | from .models import Group, GroupMembership 4 | from notifications.models import Notification 5 | 6 | # Signal to send notifications when a user joins a group 7 | @receiver(post_save, sender=GroupMembership) 8 | def send_group_join_notification(sender, instance, created, **kwargs): 9 | if created: 10 | group = instance.group 11 | user = instance.user 12 | notification_message = f"You have joined the group '{group.name}'." 13 | Notification.objects.create(user=user, message=notification_message) 14 | 15 | # Signal to send notifications when a user leaves a group 16 | @receiver(post_delete, sender=GroupMembership) 17 | def send_group_leave_notification(sender, instance, **kwargs): 18 | group = instance.group 19 | user = instance.user 20 | notification_message = f"You have left the group '{group.name}'." 21 | Notification.objects.create(user=user, message=notification_message) 22 | 23 | # Signal to update group member count when a user joins or leaves a group 24 | @receiver(post_save, sender=GroupMembership) 25 | @receiver(post_delete, sender=GroupMembership) 26 | def update_group_member_count(sender, instance, **kwargs): 27 | group = instance.group 28 | group.member_count = group.groupmembership_set.count() 29 | group.save() -------------------------------------------------------------------------------- /groups/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /groups/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /jobs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/__init__.py -------------------------------------------------------------------------------- /jobs/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /jobs/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /jobs/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /jobs/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /jobs/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /jobs/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django import forms 3 | from taggit.forms import TagWidget 4 | from django.contrib.contenttypes.admin import GenericTabularInline 5 | from .models import JobListing, JobApplication, JobNotification 6 | from activity.models import Attachment 7 | 8 | # Inline for Attachments 9 | class AttachmentInline(GenericTabularInline): 10 | model = Attachment 11 | extra = 1 12 | 13 | # Custom Admin Form for JobListing 14 | class JobListingAdminForm(forms.ModelForm): 15 | class Meta: 16 | model = JobListing 17 | fields = '__all__' 18 | widgets = { 19 | 'tags': TagWidget, 20 | } 21 | 22 | @admin.register(JobListing) 23 | class JobListingAdmin(admin.ModelAdmin): 24 | form = JobListingAdminForm 25 | list_display = ('title', 'company', 'location', 'posted_date', 'closing_date', 'is_active') 26 | list_filter = ('company', 'location', 'posted_date', 'closing_date', 'is_active', 'categories') 27 | search_fields = ('title', 'description', 'location', 'requirements', 'responsibilities') 28 | readonly_fields = ('posted_date',) 29 | inlines = (AttachmentInline,) 30 | filter_horizontal = ('categories', 'skills_required', 'shares') 31 | 32 | @admin.register(JobApplication) 33 | class JobApplicationAdmin(admin.ModelAdmin): 34 | list_display = ('applicant', 'job_listing', 'status', 'applied_date') 35 | list_filter = ('job_listing', 'status', 'applied_date') 36 | search_fields = ('applicant__username', 'job_listing__title') 37 | readonly_fields = ('applied_date',) 38 | filter_horizontal = ('shares',) 39 | 40 | @admin.register(JobNotification) 41 | class JobNotificationAdmin(admin.ModelAdmin): 42 | list_display = ('user', 'job_listing', 'created_at', 'read') 43 | list_filter = ('job_listing', 'created_at', 'read') 44 | search_fields = ('user__username', 'job_listing__title') 45 | readonly_fields = ('created_at',) 46 | filter_horizontal = ('shares',) 47 | -------------------------------------------------------------------------------- /jobs/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class JobsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "jobs" 7 | 8 | def ready(self): 9 | import jobs.signals -------------------------------------------------------------------------------- /jobs/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name="JobApplication", 14 | fields=[ 15 | ( 16 | "id", 17 | models.BigAutoField( 18 | auto_created=True, 19 | primary_key=True, 20 | serialize=False, 21 | verbose_name="ID", 22 | ), 23 | ), 24 | ("resume", models.FileField(upload_to="resumes/")), 25 | ("cover_letter", models.TextField(blank=True)), 26 | ("applied_date", models.DateTimeField(auto_now_add=True)), 27 | ( 28 | "status", 29 | models.CharField( 30 | choices=[ 31 | ("applied", "Applied"), 32 | ("reviewed", "Reviewed"), 33 | ("interview", "Interview"), 34 | ("offered", "Offered"), 35 | ("rejected", "Rejected"), 36 | ], 37 | default="applied", 38 | max_length=20, 39 | ), 40 | ), 41 | ], 42 | ), 43 | migrations.CreateModel( 44 | name="JobListing", 45 | fields=[ 46 | ( 47 | "id", 48 | models.BigAutoField( 49 | auto_created=True, 50 | primary_key=True, 51 | serialize=False, 52 | verbose_name="ID", 53 | ), 54 | ), 55 | ("title", models.CharField(max_length=255)), 56 | ("description", models.TextField()), 57 | ("location", models.CharField(max_length=255)), 58 | ("posted_date", models.DateTimeField(auto_now_add=True)), 59 | ("closing_date", models.DateTimeField()), 60 | ("is_active", models.BooleanField(default=True)), 61 | ( 62 | "salary", 63 | models.DecimalField( 64 | blank=True, decimal_places=2, max_digits=10, null=True 65 | ), 66 | ), 67 | ("requirements", models.TextField(blank=True)), 68 | ("responsibilities", models.TextField(blank=True)), 69 | ( 70 | "employment_type", 71 | models.CharField( 72 | blank=True, 73 | choices=[ 74 | ("full_time", "Full Time"), 75 | ("part_time", "Part Time"), 76 | ("education", "Education"), 77 | ("contract", "Contract"), 78 | ], 79 | max_length=50, 80 | ), 81 | ), 82 | ( 83 | "experience_level", 84 | models.CharField( 85 | blank=True, 86 | choices=[ 87 | ("entry_level", "Entry Level"), 88 | ("mid_level", "Mid Level"), 89 | ("senior_level", "Senior Level"), 90 | ], 91 | max_length=50, 92 | ), 93 | ), 94 | ], 95 | ), 96 | migrations.CreateModel( 97 | name="JobNotification", 98 | fields=[ 99 | ( 100 | "id", 101 | models.BigAutoField( 102 | auto_created=True, 103 | primary_key=True, 104 | serialize=False, 105 | verbose_name="ID", 106 | ), 107 | ), 108 | ("created_at", models.DateTimeField(auto_now_add=True)), 109 | ("read", models.BooleanField(default=False)), 110 | ], 111 | ), 112 | ] 113 | -------------------------------------------------------------------------------- /jobs/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/migrations/__init__.py -------------------------------------------------------------------------------- /jobs/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /jobs/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /jobs/migrations/__pycache__/0003_alter_joblisting_tags.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/migrations/__pycache__/0003_alter_joblisting_tags.cpython-312.pyc -------------------------------------------------------------------------------- /jobs/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/jobs/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /jobs/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth import get_user_model 3 | # from profiles.models import Skill, Experience, Education, Endorsement 4 | from activity.models import Attachment 5 | from django.contrib.contenttypes.fields import GenericRelation 6 | from taggit.managers import TaggableManager 7 | from django.conf import settings 8 | 9 | class JobListing(models.Model): 10 | EMPLOYMENT_TYPES = [ 11 | ('full_time', 'Full Time'), 12 | ('part_time', 'Part Time'), 13 | ('education', 'Education'), 14 | ('contract', 'Contract'), 15 | ] 16 | 17 | EXPERIENCE_LEVELS = [ 18 | ('entry_level', 'Entry Level'), 19 | ('mid_level', 'Mid Level'), 20 | ('senior_level', 'Senior Level'), 21 | ] 22 | 23 | company = models.ForeignKey('companies.Company', related_name='job_listings_companies', on_delete=models.CASCADE, db_index=True) 24 | title = models.CharField(max_length=255) 25 | description = models.TextField() 26 | attachments = GenericRelation('activity.Attachment') 27 | categories = models.ManyToManyField('activity.Category', related_name='job_listings_categories') 28 | location = models.CharField(max_length=255) 29 | posted_date = models.DateTimeField(auto_now_add=True) 30 | closing_date = models.DateTimeField() 31 | is_active = models.BooleanField(default=True) 32 | salary = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) 33 | requirements = models.TextField(blank=True) 34 | responsibilities = models.TextField(blank=True) 35 | employment_type = models.CharField( 36 | max_length=50, 37 | choices=EMPLOYMENT_TYPES, 38 | blank=True 39 | ) 40 | experience_level = models.CharField( 41 | max_length=50, 42 | choices=EXPERIENCE_LEVELS, 43 | blank=True 44 | ) 45 | skills_required = models.ManyToManyField('profiles.Skill', related_name='required_jobs', blank=True, db_index=True) 46 | applications = models.ManyToManyField('JobApplication', related_name='applications_job_listings', blank=True, db_index=True) 47 | notifications = models.ManyToManyField('JobNotification', related_name='notifications_job_listings', blank=True, db_index=True) 48 | shares = models.ManyToManyField('activity.Share', related_name='shared_job_listings', blank=True) 49 | tags = TaggableManager() 50 | 51 | def __str__(self): 52 | return self.title 53 | 54 | class JobApplication(models.Model): 55 | STATUS_CHOICES = [ 56 | ('applied', 'Applied'), 57 | ('reviewed', 'Reviewed'), 58 | ('interview', 'Interview'), 59 | ('offered', 'Offered'), 60 | ('rejected', 'Rejected'), 61 | ] 62 | job_listing = models.ForeignKey(JobListing, related_name='job_applications', on_delete=models.CASCADE, db_index=True) 63 | applicant = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='applications', on_delete=models.CASCADE, db_index=True) 64 | resume = models.FileField(upload_to='resumes/') 65 | attachments = GenericRelation('activity.Attachment') 66 | cover_letter = models.TextField(blank=True) 67 | applied_date = models.DateTimeField(auto_now_add=True) 68 | status = models.CharField( 69 | max_length=20, 70 | choices=STATUS_CHOICES, 71 | default='applied' 72 | ) 73 | shares = models.ManyToManyField('activity.Share', related_name='shared_applications', blank=True) 74 | 75 | def __str__(self): 76 | return f'{self.applicant.username} applied for {self.job_listing.title}' 77 | 78 | class JobNotification(models.Model): 79 | job_listing = models.ForeignKey(JobListing, related_name='job_notifications', on_delete=models.CASCADE, db_index=True) 80 | user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notifications', on_delete=models.CASCADE, db_index=True) 81 | created_at = models.DateTimeField(auto_now_add=True) 82 | read = models.BooleanField(default=False) 83 | shares = models.ManyToManyField('activity.Share', related_name='shared_notifications', blank=True) 84 | 85 | def __str__(self): 86 | return f'{self.user.username} received a notification for {self.job_listing.title}' -------------------------------------------------------------------------------- /jobs/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save, post_delete 2 | from django.dispatch import receiver 3 | from .models import JobListing, JobApplication, JobNotification 4 | from notifications.models import Notification 5 | 6 | # Signal to send notifications when a new job listing is posted 7 | @receiver(post_save, sender=JobListing) 8 | def send_job_listing_notification(sender, instance, created, **kwargs): 9 | if created: 10 | followers = instance.company.followers.all() 11 | notification_message = f"A new job listing '{instance.title}' is posted by {instance.company.name}." 12 | for follower in followers: 13 | Notification.objects.create(user=follower, message=notification_message) 14 | 15 | # Signal to send notifications when a user applies for a job 16 | @receiver(post_save, sender=JobApplication) 17 | def send_job_application_notification(sender, instance, created, **kwargs): 18 | if created: 19 | job_listing = instance.job_listing 20 | company = job_listing.company 21 | notification_message = f"You have a new job application for the position '{job_listing.title}' from {instance.applicant.username}." 22 | Notification.objects.create(user=company.user, message=notification_message) 23 | 24 | # Signal to send notifications when a job application status is updated 25 | @receiver(post_save, sender=JobApplication) 26 | def send_job_application_status_notification(sender, instance, **kwargs): 27 | if instance.status in ['accepted', 'rejected']: 28 | applicant = instance.applicant 29 | notification_message = f"Your job application status for the position '{instance.job_listing.title}' is updated to {instance.status}." 30 | Notification.objects.create(user=applicant, message=notification_message) 31 | 32 | # Signal to send notifications when a new job notification is sent 33 | @receiver(post_save, sender=JobNotification) 34 | def send_job_notification(sender, instance, created, **kwargs): 35 | if created: 36 | recipients = instance.recipients.all() 37 | notification_message = f"You have a new job notification: {instance.message}." 38 | for recipient in recipients: 39 | Notification.objects.create(user=recipient, message=notification_message) 40 | 41 | # Signal to update job listing count when a job listing is deleted 42 | @receiver(post_delete, sender=JobListing) 43 | def update_job_listing_count(sender, instance, **kwargs): 44 | company = instance.company 45 | company.job_listing_count -= 1 46 | company.save() -------------------------------------------------------------------------------- /jobs/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /jobs/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /messaging/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/__init__.py -------------------------------------------------------------------------------- /messaging/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /messaging/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /messaging/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /messaging/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /messaging/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /messaging/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import ChatRoom, Message 3 | 4 | @admin.register(ChatRoom) 5 | class ChatRoomAdmin(admin.ModelAdmin): 6 | list_display = ('roomId', 'name', 'created_at') 7 | search_fields = ('roomId', 'name') 8 | filter_horizontal = ('members',) # Allows for easier selection of members 9 | 10 | @admin.register(Message) 11 | class MessageAdmin(admin.ModelAdmin): 12 | list_display = ('sender', 'chat', 'content', 'timestamp', 'is_read') 13 | list_filter = ('chat', 'sender', 'timestamp', 'is_read', 'message_type') 14 | search_fields = ('sender__username', 'chat__roomId', 'content') 15 | date_hierarchy = 'timestamp' 16 | filter_horizontal = ('attachments', 'reactions', 'shares') # Allows for easier selection of related objects 17 | raw_id_fields = ('parent_message',) # Provides a raw ID field for parent_message for efficient lookup 18 | 19 | def get_queryset(self, request): 20 | queryset = super().get_queryset(request) 21 | return queryset.select_related('sender', 'chat') 22 | 23 | def formfield_for_foreignkey(self, db_field, request, **kwargs): 24 | if db_field.name == 'sender': 25 | kwargs['queryset'] = db_field.remote_field.model.objects.all() 26 | return super().formfield_for_foreignkey(db_field, request, **kwargs) 27 | 28 | def formfield_for_manytomany(self, db_field, request, **kwargs): 29 | if db_field.name in ('attachments', 'reactions', 'shares'): 30 | kwargs['queryset'] = db_field.remote_field.model.objects.all() 31 | return super().formfield_for_manytomany(db_field, request, **kwargs) 32 | 33 | def save_related(self, request, form, formsets, change): 34 | super().save_related(request, form, formsets, change) 35 | # Custom logic or actions after saving related objects 36 | 37 | admin.site.site_header = 'LMS Administration' # Customize admin site header 38 | -------------------------------------------------------------------------------- /messaging/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MessagingConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "messaging" 7 | 8 | def ready(self): 9 | import messaging.signals -------------------------------------------------------------------------------- /messaging/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import shortuuidfield.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | initial = True 9 | 10 | dependencies = [] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name="ChatRoom", 15 | fields=[ 16 | ( 17 | "id", 18 | models.BigAutoField( 19 | auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name="ID", 23 | ), 24 | ), 25 | ( 26 | "roomId", 27 | shortuuidfield.fields.ShortUUIDField( 28 | blank=True, editable=False, max_length=22 29 | ), 30 | ), 31 | ("name", models.CharField(blank=True, max_length=255, null=True)), 32 | ("created_at", models.DateTimeField(auto_now_add=True)), 33 | ], 34 | ), 35 | migrations.CreateModel( 36 | name="Message", 37 | fields=[ 38 | ( 39 | "id", 40 | models.BigAutoField( 41 | auto_created=True, 42 | primary_key=True, 43 | serialize=False, 44 | verbose_name="ID", 45 | ), 46 | ), 47 | ("content", models.TextField()), 48 | ("timestamp", models.DateTimeField(auto_now_add=True)), 49 | ("is_read", models.BooleanField(default=False)), 50 | ( 51 | "message_type", 52 | models.CharField( 53 | choices=[ 54 | ("text", "Text Message"), 55 | ("image", "Image Message"), 56 | ("video", "Video Message"), 57 | ("audio", "Audio Message"), 58 | ("file", "File Attachment"), 59 | ], 60 | default="text", 61 | max_length=20, 62 | ), 63 | ), 64 | ("is_edited", models.BooleanField(default=False)), 65 | ("is_deleted", models.BooleanField(default=False)), 66 | ], 67 | ), 68 | ] 69 | -------------------------------------------------------------------------------- /messaging/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | initial = True 10 | 11 | dependencies = [ 12 | ("activity", "0002_initial"), 13 | ("messaging", "0001_initial"), 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name="chatroom", 20 | name="members", 21 | field=models.ManyToManyField( 22 | db_index=True, related_name="chatrooms", to=settings.AUTH_USER_MODEL 23 | ), 24 | ), 25 | migrations.AddField( 26 | model_name="message", 27 | name="attachments", 28 | field=models.ManyToManyField( 29 | blank=True, 30 | db_index=True, 31 | related_name="message_attachments", 32 | to="activity.attachment", 33 | ), 34 | ), 35 | migrations.AddField( 36 | model_name="message", 37 | name="chat", 38 | field=models.ForeignKey( 39 | on_delete=django.db.models.deletion.CASCADE, to="messaging.chatroom" 40 | ), 41 | ), 42 | migrations.AddField( 43 | model_name="message", 44 | name="parent_message", 45 | field=models.ForeignKey( 46 | blank=True, 47 | null=True, 48 | on_delete=django.db.models.deletion.CASCADE, 49 | to="messaging.message", 50 | ), 51 | ), 52 | migrations.AddField( 53 | model_name="message", 54 | name="reactions", 55 | field=models.ManyToManyField( 56 | db_index=True, related_name="message_reactions", to="activity.reaction" 57 | ), 58 | ), 59 | migrations.AddField( 60 | model_name="message", 61 | name="sender", 62 | field=models.ForeignKey( 63 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL 64 | ), 65 | ), 66 | migrations.AddField( 67 | model_name="message", 68 | name="shares", 69 | field=models.ManyToManyField( 70 | blank=True, related_name="message_shares", to="activity.share" 71 | ), 72 | ), 73 | ] 74 | -------------------------------------------------------------------------------- /messaging/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/migrations/__init__.py -------------------------------------------------------------------------------- /messaging/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /messaging/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /messaging/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/messaging/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /messaging/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from shortuuidfield import ShortUUIDField 3 | # from posts.models import Post, Comment 4 | # from jobs.models import JobListing 5 | # from groups.models import Group 6 | from django.contrib.contenttypes.fields import GenericForeignKey 7 | from django.contrib.contenttypes.models import ContentType 8 | # from activity.models import Attachment, Reaction, Share, Tag 9 | from django.conf import settings 10 | 11 | class ChatRoom(models.Model): 12 | roomId = ShortUUIDField() 13 | members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='chatrooms', db_index=True) 14 | name = models.CharField(max_length=255, null=True, blank=True) 15 | created_at = models.DateTimeField(auto_now_add=True) 16 | 17 | def __str__(self): 18 | return self.name if self.name else self.roomId 19 | 20 | class Message(models.Model): 21 | TEXT = 'text' 22 | IMAGE = 'image' 23 | VIDEO = 'video' 24 | AUDIO = 'audio' 25 | FILE = 'file' 26 | 27 | MESSAGE_TYPE_CHOICES = [ 28 | (TEXT, 'Text Message'), 29 | (IMAGE, 'Image Message'), 30 | (VIDEO, 'Video Message'), 31 | (AUDIO, 'Audio Message'), 32 | (FILE, 'File Attachment'), 33 | ] 34 | 35 | chat = models.ForeignKey(ChatRoom, on_delete=models.CASCADE, db_index=True) 36 | sender = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, db_index=True) 37 | content = models.TextField() 38 | timestamp = models.DateTimeField(auto_now_add=True) 39 | is_read = models.BooleanField(default=False) 40 | message_type = models.CharField(max_length=20, choices=MESSAGE_TYPE_CHOICES, default=TEXT) 41 | attachments = models.ManyToManyField('activity.Attachment', related_name='message_attachments', blank=True, db_index=True) 42 | parent_message = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True) 43 | is_edited = models.BooleanField(default=False) 44 | is_deleted = models.BooleanField(default=False) 45 | reactions = models.ManyToManyField('activity.Reaction', related_name='message_reactions', db_index=True) 46 | shares = models.ManyToManyField('activity.Share', related_name='message_shares', blank=True) 47 | 48 | 49 | 50 | def __str__(self): 51 | return f"{self.sender.user.username}: {self.content[:20]}" 52 | 53 | 54 | 55 | # class ChatRoomNotification(models.Model): 56 | # chat = models.ForeignKey(ChatRoom, on_delete=models.CASCADE) 57 | # user = models.ForeignKey(UserProfile, on_delete=models.CASCADE) 58 | # created_at = models.DateTimeField(auto_now_add=True) 59 | # read = models.BooleanField(default=False) 60 | 61 | # def __str__(self): 62 | # return f'{self.user.user.username} received a notification for {self.chat.name}' -------------------------------------------------------------------------------- /messaging/routing.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | from . import consumers 3 | 4 | websocket_urlpatterns = [ 5 | re_path(r'ws/chat/(?P\w+)/$', consumers.ChatConsumer.as_asgi()), 6 | ] -------------------------------------------------------------------------------- /messaging/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save, post_delete 2 | from django.dispatch import receiver 3 | from .models import Message, ChatRoom 4 | from notifications.models import Notification 5 | from activity.models import Reaction, Share 6 | 7 | 8 | # Signal to send notifications when a new message is sent 9 | @receiver(post_save, sender=Message) 10 | def send_message_notification(sender, instance, created, **kwargs): 11 | if created: 12 | recipients = instance.recipients.all() 13 | sender = instance.sender 14 | notification_message = f"You have a new message from {sender.username}." 15 | for recipient in recipients: 16 | Notification.objects.create(user=recipient, message=notification_message) 17 | 18 | # Signal to send notifications when a user is mentioned in a message 19 | @receiver(post_save, sender=Message) 20 | def send_mention_notification(sender, instance, created, **kwargs): 21 | if created: 22 | mentioned_users = instance.mentioned_users.all() 23 | sender = instance.sender 24 | notification_message = f"You were mentioned in a message by {sender.username}." 25 | for user in mentioned_users: 26 | Notification.objects.create(user=user, message=notification_message) 27 | 28 | # Signal to update chat room details when a new message is sent 29 | @receiver(post_save, sender=Message) 30 | def update_chat_room(sender, instance, created, **kwargs): 31 | if created: 32 | chat_room = instance.chat_room 33 | chat_room.last_message = instance 34 | chat_room.save() 35 | 36 | # Signal to send notifications when a new chat room is created 37 | @receiver(post_save, sender=ChatRoom) 38 | def send_chat_room_notification(sender, instance, created, **kwargs): 39 | if created: 40 | participants = instance.participants.all() 41 | notification_message = f"You have been added to a new chat room." 42 | for participant in participants: 43 | Notification.objects.create(user=participant, message=notification_message) 44 | 45 | # Signal to update message count when a message is deleted 46 | @receiver(post_delete, sender=Message) 47 | def update_message_count(sender, instance, **kwargs): 48 | chat_room = instance.chat_room 49 | chat_room.message_count -= 1 50 | chat_room.save() 51 | 52 | # Signal to send notifications when a message is reacted to 53 | @receiver(post_save, sender=Reaction) 54 | def send_reaction_notification(sender, instance, created, **kwargs): 55 | if created: 56 | reacted_message = instance.message 57 | recipients = reacted_message.recipients.exclude(id=instance.user.id) 58 | sender = instance.user 59 | notification_message = f"{sender.username} reacted to your message." 60 | for recipient in recipients: 61 | Notification.objects.create(user=recipient, message=notification_message) 62 | 63 | # Signal to update reaction count when a new reaction is added 64 | @receiver(post_save, sender=Reaction) 65 | def update_reaction_count(sender, instance, created, **kwargs): 66 | if created: 67 | reacted_message = instance.message 68 | reacted_message.reaction_count += 1 69 | reacted_message.save() -------------------------------------------------------------------------------- /messaging/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /messaging/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /notifications/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/__init__.py -------------------------------------------------------------------------------- /notifications/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/__pycache__/forms.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/__pycache__/forms.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/__pycache__/services.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/__pycache__/services.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Notification, NotificationTemplate, NotificationSettings, NotificationReadStatus, NotificationType 3 | from .forms import NotificationTypeForm 4 | @admin.register(NotificationType) 5 | class NotificationTypeAdmin(admin.ModelAdmin): 6 | form = NotificationTypeForm 7 | list_display = ('type_name',) 8 | search_fields = ('type_name',) 9 | 10 | @admin.register(NotificationTemplate) 11 | class NotificationTemplateAdmin(admin.ModelAdmin): 12 | list_display = ('notification_type',) 13 | search_fields = ('notification_type__type_name',) 14 | 15 | @admin.register(NotificationSettings) 16 | class NotificationSettingsAdmin(admin.ModelAdmin): 17 | list_display = ('user', 'notification_type', 'is_enabled') 18 | list_filter = ('notification_type', 'is_enabled') 19 | search_fields = ('user__username', 'notification_type__type_name') 20 | 21 | @admin.register(Notification) 22 | class NotificationAdmin(admin.ModelAdmin): 23 | list_display = ('recipient', 'notification_type', 'content_preview', 'timestamp', 'is_read') 24 | list_filter = ('notification_type', 'is_read', 'timestamp') 25 | search_fields = ('recipient__user__username', 'notification_type__type_name') 26 | 27 | def content_preview(self, obj): 28 | return obj.content[:50] + '...' if len(obj.content) > 50 else obj.content 29 | 30 | content_preview.short_description = 'Content' 31 | 32 | @admin.register(NotificationReadStatus) 33 | class NotificationReadStatusAdmin(admin.ModelAdmin): 34 | list_display = ('user', 'notification', 'is_read', 'read_at') 35 | list_filter = ('is_read',) 36 | search_fields = ('user__user__username', 'notification__notification_type__type_name') 37 | 38 | def get_queryset(self, request): 39 | queryset = super().get_queryset(request) 40 | return queryset.select_related('user', 'notification') 41 | 42 | def notification_type(self, obj): 43 | return obj.notification.notification_type.type_name 44 | 45 | notification_type.short_description = 'Notification Type' 46 | -------------------------------------------------------------------------------- /notifications/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NotificationsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "notifications" 7 | 8 | def ready(self): 9 | import notifications.signals -------------------------------------------------------------------------------- /notifications/consumers.py: -------------------------------------------------------------------------------- 1 | import json 2 | from channels.generic.websocket import WebsocketConsumer 3 | from asgiref.sync import async_to_sync 4 | 5 | class NotificationConsumer(WebsocketConsumer): 6 | def connect(self): 7 | self.group_name = f"notifications_{self.scope['user'].username}" 8 | async_to_sync(self.channel_layer.group_add)(self.group_name, self.channel_name) 9 | self.accept() 10 | 11 | def disconnect(self, close_code): 12 | async_to_sync(self.channel_layer.group_discard)(self.group_name, self.channel_name) 13 | 14 | def send_notification(self, event): 15 | notification = event['notification'] 16 | self.send(text_data=json.dumps({ 17 | 'type': notification['type'], 18 | 'content': notification['content'], 19 | 'url': notification['url'], 20 | 'timestamp': notification['timestamp'], 21 | })) -------------------------------------------------------------------------------- /notifications/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from .models import NotificationType 3 | 4 | class NotificationTypeForm(forms.ModelForm): 5 | predefined_types = forms.ChoiceField( 6 | choices=[(type_name, type_name) for type_name in NotificationType.PREDEFINED_TYPES], 7 | required=False, 8 | label='Predefined Types' 9 | ) 10 | custom_type = forms.CharField( 11 | max_length=100, 12 | required=False, 13 | label='Custom Type' 14 | ) 15 | 16 | class Meta: 17 | model = NotificationType 18 | fields = ['predefined_types', 'custom_type'] 19 | 20 | def clean(self): 21 | cleaned_data = super().clean() 22 | predefined_type = cleaned_data.get('predefined_types') 23 | custom_type = cleaned_data.get('custom_type') 24 | 25 | if not predefined_type and not custom_type: 26 | raise forms.ValidationError('You must select a predefined type or enter a custom type.') 27 | 28 | if predefined_type and custom_type: 29 | raise forms.ValidationError('You can only select one type: either a predefined type or a custom type.') 30 | 31 | return cleaned_data 32 | 33 | def save(self, commit=True): 34 | predefined_type = self.cleaned_data.get('predefined_types') 35 | custom_type = self.cleaned_data.get('custom_type') 36 | 37 | if predefined_type: 38 | self.instance.type_name = predefined_type 39 | elif custom_type: 40 | self.instance.type_name = custom_type 41 | 42 | return super().save(commit=commit) -------------------------------------------------------------------------------- /notifications/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | initial = True 9 | 10 | dependencies = [ 11 | ("contenttypes", "0002_remove_content_type_name"), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name="NotificationReadStatus", 17 | fields=[ 18 | ( 19 | "id", 20 | models.BigAutoField( 21 | auto_created=True, 22 | primary_key=True, 23 | serialize=False, 24 | verbose_name="ID", 25 | ), 26 | ), 27 | ("is_read", models.BooleanField(default=False)), 28 | ("read_at", models.DateTimeField(blank=True, null=True)), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name="NotificationSettings", 33 | fields=[ 34 | ( 35 | "id", 36 | models.BigAutoField( 37 | auto_created=True, 38 | primary_key=True, 39 | serialize=False, 40 | verbose_name="ID", 41 | ), 42 | ), 43 | ("is_enabled", models.BooleanField(default=True)), 44 | ], 45 | ), 46 | migrations.CreateModel( 47 | name="NotificationTemplate", 48 | fields=[ 49 | ( 50 | "id", 51 | models.BigAutoField( 52 | auto_created=True, 53 | primary_key=True, 54 | serialize=False, 55 | verbose_name="ID", 56 | ), 57 | ), 58 | ("template", models.TextField()), 59 | ], 60 | ), 61 | migrations.CreateModel( 62 | name="NotificationType", 63 | fields=[ 64 | ( 65 | "id", 66 | models.BigAutoField( 67 | auto_created=True, 68 | primary_key=True, 69 | serialize=False, 70 | verbose_name="ID", 71 | ), 72 | ), 73 | ("type_name", models.CharField(max_length=50, unique=True)), 74 | ], 75 | ), 76 | migrations.CreateModel( 77 | name="Notification", 78 | fields=[ 79 | ( 80 | "id", 81 | models.BigAutoField( 82 | auto_created=True, 83 | primary_key=True, 84 | serialize=False, 85 | verbose_name="ID", 86 | ), 87 | ), 88 | ("object_id", models.PositiveIntegerField()), 89 | ("content", models.TextField()), 90 | ("url", models.URLField()), 91 | ("timestamp", models.DateTimeField(auto_now_add=True)), 92 | ("is_read", models.BooleanField(default=False)), 93 | ("priority", models.IntegerField(default=0)), 94 | ("created_at", models.DateTimeField(auto_now_add=True)), 95 | ("updated_at", models.DateTimeField(auto_now=True)), 96 | ( 97 | "content_type", 98 | models.ForeignKey( 99 | on_delete=django.db.models.deletion.CASCADE, 100 | to="contenttypes.contenttype", 101 | ), 102 | ), 103 | ], 104 | ), 105 | ] 106 | -------------------------------------------------------------------------------- /notifications/migrations/0002_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | initial = True 10 | 11 | dependencies = [ 12 | ("activity", "0002_initial"), 13 | ("notifications", "0001_initial"), 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name="notification", 20 | name="recipient", 21 | field=models.ForeignKey( 22 | on_delete=django.db.models.deletion.CASCADE, 23 | related_name="notifications_recipient", 24 | to=settings.AUTH_USER_MODEL, 25 | ), 26 | ), 27 | migrations.AddField( 28 | model_name="notification", 29 | name="shares", 30 | field=models.ManyToManyField( 31 | blank=True, related_name="notifications_shares", to="activity.share" 32 | ), 33 | ), 34 | migrations.AddField( 35 | model_name="notificationreadstatus", 36 | name="notification", 37 | field=models.ForeignKey( 38 | on_delete=django.db.models.deletion.CASCADE, 39 | to="notifications.notification", 40 | ), 41 | ), 42 | migrations.AddField( 43 | model_name="notificationreadstatus", 44 | name="user", 45 | field=models.ForeignKey( 46 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL 47 | ), 48 | ), 49 | migrations.AddField( 50 | model_name="notificationsettings", 51 | name="user", 52 | field=models.OneToOneField( 53 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL 54 | ), 55 | ), 56 | migrations.AddField( 57 | model_name="notificationtemplate", 58 | name="notification_type", 59 | field=models.ForeignKey( 60 | on_delete=django.db.models.deletion.CASCADE, 61 | to="notifications.notificationtype", 62 | ), 63 | ), 64 | migrations.AddField( 65 | model_name="notificationsettings", 66 | name="notification_type", 67 | field=models.ForeignKey( 68 | on_delete=django.db.models.deletion.CASCADE, 69 | to="notifications.notificationtype", 70 | ), 71 | ), 72 | migrations.AddField( 73 | model_name="notification", 74 | name="notification_type", 75 | field=models.ForeignKey( 76 | on_delete=django.db.models.deletion.CASCADE, 77 | to="notifications.notificationtype", 78 | ), 79 | ), 80 | ] 81 | -------------------------------------------------------------------------------- /notifications/migrations/0003_alter_notification_recipient.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-14 02:19 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [ 10 | ("notifications", "0002_initial"), 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name="notification", 17 | name="recipient", 18 | field=models.ForeignKey( 19 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /notifications/migrations/0004_alter_notificationtype_type_name.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-14 02:34 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("notifications", "0003_alter_notification_recipient"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="notificationtype", 14 | name="type_name", 15 | field=models.CharField(max_length=100, unique=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /notifications/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/migrations/__init__.py -------------------------------------------------------------------------------- /notifications/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/migrations/__pycache__/0003_alter_notification_recipient.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/migrations/__pycache__/0003_alter_notification_recipient.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/migrations/__pycache__/0004_alter_notificationtype_type_name.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/migrations/__pycache__/0004_alter_notificationtype_type_name.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/notifications/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /notifications/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.contenttypes.models import ContentType 3 | from django.contrib.contenttypes.fields import GenericForeignKey 4 | from activity.models import Reaction, Share 5 | from django.conf import settings 6 | 7 | NOTIFICATION_TYPE = [ 8 | ('job_application', 'Job Application'), 9 | ('job_listing', 'Job Listing'), 10 | ('message', 'Message'), 11 | ('follow', 'Follow'), 12 | ('connection', 'Connection'), 13 | ('reaction', 'Reaction'), 14 | ('post', 'Posts'), 15 | ('share', 'Share'), 16 | ('tag', 'Tag'), 17 | ('comment', 'Comment'), 18 | ('endorsement', 'Endorsement'), 19 | ('skill', 'Skill'), 20 | ('experience', 'Experience'), 21 | ('education', 'Education'), 22 | ('group', 'Group'), 23 | ] 24 | 25 | class NotificationType(models.Model): 26 | PREDEFINED_TYPES = ['type1', 'type2'] 27 | 28 | type_name = models.CharField(max_length=100, unique=True) 29 | 30 | def __str__(self): 31 | return self.type_name 32 | 33 | class Notification(models.Model): 34 | recipient = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 35 | content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) 36 | object_id = models.PositiveIntegerField() 37 | content_object = GenericForeignKey('content_type', 'object_id') 38 | content = models.TextField() 39 | url = models.URLField() 40 | timestamp = models.DateTimeField(auto_now_add=True) 41 | is_read = models.BooleanField(default=False) 42 | notification_type = models.ForeignKey(NotificationType, on_delete=models.CASCADE) 43 | shares = models.ManyToManyField(Share, related_name='notifications_shares', blank=True) 44 | priority = models.IntegerField(default=0) 45 | created_at = models.DateTimeField(auto_now_add=True) 46 | updated_at = models.DateTimeField(auto_now=True) 47 | 48 | def __str__(self): 49 | return f"{self.notification_type.type_name} Notification for {self.recipient.username}" 50 | 51 | class NotificationTemplate(models.Model): 52 | notification_type = models.ForeignKey(NotificationType, on_delete=models.CASCADE) 53 | template = models.TextField() 54 | 55 | def __str__(self): 56 | return self.notification_type.type_name 57 | 58 | class NotificationSettings(models.Model): 59 | user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 60 | notification_type = models.ForeignKey(NotificationType, on_delete=models.CASCADE) 61 | is_enabled = models.BooleanField(default=True) 62 | 63 | def __str__(self): 64 | return f"{self.user.user.username}'s Notification Settings" 65 | 66 | class NotificationReadStatus(models.Model): 67 | user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 68 | notification = models.ForeignKey(Notification, on_delete=models.CASCADE) 69 | is_read = models.BooleanField(default=False) 70 | read_at = models.DateTimeField(null=True, blank=True) 71 | 72 | def __str__(self): 73 | return f"{self.user.user.username} read status for {self.notification}" 74 | -------------------------------------------------------------------------------- /notifications/routing.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | from .consumers import NotificationConsumer 3 | 4 | websocket_urlpatterns = [ 5 | re_path(r'ws/notifications/$', NotificationConsumer.as_asgi()), 6 | ] -------------------------------------------------------------------------------- /notifications/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save 2 | from django.dispatch import receiver 3 | from .models import Notification 4 | from profiles.models import UserProfile 5 | from .services import NotificationService 6 | 7 | @receiver(post_save, sender=Notification) 8 | def send_notification(sender, instance, created, **kwargs): 9 | if created: 10 | NotificationService.send_notification(instance) 11 | 12 | @receiver(post_save, sender=UserProfile) 13 | def create_default_notification_settings(sender, instance, created, **kwargs): 14 | if created: 15 | NotificationService.create_default_notification_settings(instance) -------------------------------------------------------------------------------- /notifications/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /notifications/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /posts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/__init__.py -------------------------------------------------------------------------------- /posts/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /posts/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /posts/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /posts/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /posts/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django import forms 3 | from taggit.forms import TagWidget 4 | from django.contrib.contenttypes.admin import GenericTabularInline 5 | from .models import Post, Comment 6 | from activity.models import Attachment 7 | 8 | # Inline for GenericRelation Attachment 9 | class AttachmentInline(GenericTabularInline): 10 | model = Attachment 11 | extra = 1 12 | 13 | # Inline for replies (child comments) 14 | class ReplyInline(admin.TabularInline): # or admin.StackedInline if you prefer 15 | model = Comment 16 | fk_name = 'parent_comment' 17 | fields = ('author', 'content', 'visibility', 'created_at') # Adjust fields as necessary 18 | readonly_fields = ('created_at',) 19 | extra = 1 20 | 21 | def get_formset(self, request, obj=None, **kwargs): 22 | # Override get_formset to set the parent comment dynamically 23 | formset = super().get_formset(request, obj, **kwargs) 24 | formset.parent_obj = obj # Store the parent comment object in the formset 25 | return formset 26 | 27 | def get_queryset(self, request): 28 | # Override get_queryset to filter only direct replies to the current comment 29 | queryset = super().get_queryset(request) 30 | parent_obj = getattr(self.formset, 'parent_obj', None) 31 | if parent_obj: 32 | return queryset.filter(parent_comment=parent_obj) 33 | else: 34 | return queryset.none() 35 | 36 | # Custom Admin Form for Post 37 | class PostAdminForm(forms.ModelForm): 38 | class Meta: 39 | model = Post 40 | fields = '__all__' 41 | widgets = { 42 | 'tags': TagWidget, 43 | } 44 | 45 | @admin.register(Post) 46 | class PostAdmin(admin.ModelAdmin): 47 | form = PostAdminForm 48 | list_display = ('id', 'author', 'content_preview', 'group', 'visibility', 'created_at') 49 | list_filter = ('visibility', 'created_at', 'author') 50 | search_fields = ('content', 'author__username', 'group__name') 51 | date_hierarchy = 'created_at' 52 | readonly_fields = ('created_at', 'updated_at') 53 | fieldsets = ( 54 | (None, { 55 | 'fields': ('author', 'group', 'content', 'visibility', 'categories') 56 | }), 57 | ('Advanced Options', { 58 | 'fields': ('likes', 'reactions', 'comments', 'shares'), 59 | 'classes': ('collapse',), 60 | }), 61 | ('Timestamps', { 62 | 'fields': ('created_at', 'updated_at'), 63 | 'classes': ('collapse',), 64 | }), 65 | ) 66 | inlines = [AttachmentInline] 67 | 68 | def content_preview(self, obj): 69 | return obj.content[:50] + '...' if len(obj.content) > 50 else obj.content 70 | content_preview.short_description = 'Content' 71 | 72 | @admin.register(Comment) 73 | class CommentAdmin(admin.ModelAdmin): 74 | list_display = ('id', 'author', 'post', 'content_preview', 'visibility', 'created_at') 75 | list_filter = ('visibility', 'created_at', 'author') 76 | search_fields = ('content', 'author__username', 'post__content') 77 | date_hierarchy = 'created_at' 78 | filter_horizontal = ('reactions',) 79 | readonly_fields = ('created_at', 'updated_at') 80 | fieldsets = ( 81 | (None, { 82 | 'fields': ('post', 'author', 'content', 'visibility') 83 | }), 84 | ('Advanced Options', { 85 | 'fields': ('reactions',), 86 | 'classes': ('collapse',), 87 | }), 88 | ('Timestamps', { 89 | 'fields': ('created_at', 'updated_at'), 90 | 'classes': ('collapse',), 91 | }), 92 | ) 93 | inlines = [AttachmentInline, ReplyInline] # Include ReplyInline here 94 | 95 | def content_preview(self, obj): 96 | return obj.content[:50] + '...' if len(obj.content) > 50 else obj.content 97 | content_preview.short_description = 'Content' -------------------------------------------------------------------------------- /posts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PostsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "posts" 7 | -------------------------------------------------------------------------------- /posts/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-13 20:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | initial = True 8 | 9 | dependencies = [] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name="Comment", 14 | fields=[ 15 | ( 16 | "id", 17 | models.BigAutoField( 18 | auto_created=True, 19 | primary_key=True, 20 | serialize=False, 21 | verbose_name="ID", 22 | ), 23 | ), 24 | ("content", models.TextField(max_length=2000)), 25 | ( 26 | "visibility", 27 | models.CharField( 28 | choices=[("public", "Public"), ("private", "Private")], 29 | default="public", 30 | max_length=20, 31 | ), 32 | ), 33 | ("created_at", models.DateTimeField(auto_now_add=True)), 34 | ("updated_at", models.DateTimeField(auto_now=True)), 35 | ], 36 | ), 37 | migrations.CreateModel( 38 | name="Post", 39 | fields=[ 40 | ( 41 | "id", 42 | models.BigAutoField( 43 | auto_created=True, 44 | primary_key=True, 45 | serialize=False, 46 | verbose_name="ID", 47 | ), 48 | ), 49 | ("content", models.TextField(max_length=5000)), 50 | ( 51 | "visibility", 52 | models.CharField( 53 | choices=[("public", "Public"), ("private", "Private")], 54 | default="public", 55 | max_length=20, 56 | ), 57 | ), 58 | ("created_at", models.DateTimeField(auto_now_add=True)), 59 | ("updated_at", models.DateTimeField(auto_now=True)), 60 | ], 61 | ), 62 | ] 63 | -------------------------------------------------------------------------------- /posts/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/migrations/__init__.py -------------------------------------------------------------------------------- /posts/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /posts/migrations/__pycache__/0002_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/migrations/__pycache__/0002_initial.cpython-312.pyc -------------------------------------------------------------------------------- /posts/migrations/__pycache__/0003_alter_comment_content_alter_post_content_and_more.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/migrations/__pycache__/0003_alter_comment_content_alter_post_content_and_more.cpython-312.pyc -------------------------------------------------------------------------------- /posts/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/posts/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /posts/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.conf import settings 3 | # from groups.models import Group 4 | from activity.models import Attachment 5 | from django.contrib.contenttypes.fields import GenericRelation 6 | from taggit.managers import TaggableManager 7 | 8 | class Post(models.Model): 9 | author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, db_index=True) 10 | group = models.ForeignKey('groups.Group', on_delete=models.CASCADE, related_name='posts', null=True, blank=True, db_index=True) 11 | content = models.TextField(max_length=5000) 12 | attachments = GenericRelation(Attachment) 13 | visibility = models.CharField(max_length=20, choices=[('public', 'Public'), ('private', 'Private')], default='public') 14 | categories = models.ManyToManyField('activity.Category', related_name='posts_categories') 15 | created_at = models.DateTimeField(auto_now_add=True) 16 | updated_at = models.DateTimeField(auto_now=True) 17 | likes = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='liked_posts', blank=True, db_index=True) 18 | reactions = models.ManyToManyField('activity.Reaction', related_name='post_reactions', blank=True, db_index=True) 19 | comments = models.ManyToManyField('Comment', related_name='post_comments', blank=True, db_index=True) 20 | shares = models.ManyToManyField('activity.Share', related_name='post_shares', blank=True, db_index=True) 21 | tags = TaggableManager() 22 | 23 | def __str__(self): 24 | return self.content[:20] 25 | 26 | class Comment(models.Model): 27 | post = models.ForeignKey(Post, related_name='comments_posts', on_delete=models.CASCADE, db_index=True) 28 | author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, db_index=True) 29 | content = models.TextField(max_length=2000) 30 | attachments = GenericRelation(Attachment) 31 | visibility = models.CharField(max_length=20, choices=[('public', 'Public'), ('private', 'Private')], default='public') 32 | created_at = models.DateTimeField(auto_now_add=True) 33 | updated_at = models.DateTimeField(auto_now=True) 34 | parent_comment = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='replies', db_index=True) 35 | reactions = models.ManyToManyField('activity.Reaction', related_name='comment_reactions', blank=True, db_index=True) 36 | 37 | def __str__(self): 38 | return self.content[:20] 39 | 40 | 41 | -------------------------------------------------------------------------------- /posts/signals.py: -------------------------------------------------------------------------------- 1 | # posts/signals.py 2 | from django.db.models.signals import post_save, post_delete 3 | from django.dispatch import receiver 4 | from .models import Post, Comment 5 | from activity.models import Reaction, Tag, Share 6 | 7 | # Signal to update post count for a user when a new post is created 8 | @receiver(post_save, sender=Post) 9 | def update_post_count(sender, instance, created, **kwargs): 10 | if created: 11 | user_profile = instance.author.userprofile 12 | user_profile.post_count += 1 13 | user_profile.save() 14 | 15 | # Signal to update post count for a user when a post is deleted 16 | @receiver(post_delete, sender=Post) 17 | def decrease_post_count(sender, instance, **kwargs): 18 | user_profile = instance.author.userprofile 19 | if user_profile.post_count > 0: 20 | user_profile.post_count -= 1 21 | user_profile.save() 22 | 23 | # Signal to update comment count for a post when a new comment is created 24 | @receiver(post_save, sender=Comment) 25 | def update_comment_count(sender, instance, created, **kwargs): 26 | if created: 27 | post = instance.post 28 | post.comment_count += 1 29 | post.save() 30 | 31 | # Signal to update comment count for a post when a comment is deleted 32 | @receiver(post_delete, sender=Comment) 33 | def decrease_comment_count(sender, instance, **kwargs): 34 | post = instance.post 35 | if post.comment_count > 0: 36 | post.comment_count -= 1 37 | post.save() 38 | 39 | # Signal to update reaction count for a post when a new reaction is created 40 | @receiver(post_save, sender=Reaction) 41 | def update_reaction_count(sender, instance, created, **kwargs): 42 | if created: 43 | post = instance.post 44 | post.reaction_count += 1 45 | post.save() 46 | 47 | # Signal to update reaction count for a post when a reaction is deleted 48 | @receiver(post_delete, sender=Reaction) 49 | def decrease_reaction_count(sender, instance, **kwargs): 50 | post = instance.post 51 | if post.reaction_count > 0: 52 | post.reaction_count -= 1 53 | post.save() 54 | 55 | # Signal to update share count for a post when a new share is created 56 | @receiver(post_save, sender=Share) 57 | def update_share_count(sender, instance, created, **kwargs): 58 | if created: 59 | post = instance.post 60 | post.share_count += 1 61 | post.save() 62 | 63 | # Signal to update share count for a post when a share is deleted 64 | @receiver(post_delete, sender=Share) 65 | def decrease_share_count(sender, instance, **kwargs): 66 | post = instance.post 67 | if post.share_count > 0: 68 | post.share_count -= 1 69 | post.save() 70 | 71 | # Signal to handle tagging of users in comments 72 | @receiver(post_save, sender=Comment) 73 | def handle_comment_tagging(sender, instance, created, **kwargs): 74 | if created: 75 | # Your logic to handle tagging of users in comments 76 | pass 77 | 78 | # Signal to handle tagging of posts with tags 79 | @receiver(post_save, sender=Tag) 80 | def handle_post_tagging(sender, instance, created, **kwargs): 81 | if created: 82 | # Your logic to handle tagging of posts with tags 83 | pass -------------------------------------------------------------------------------- /posts/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /posts/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /profiles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/profiles/__init__.py -------------------------------------------------------------------------------- /profiles/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/profiles/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /profiles/__pycache__/admin.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/profiles/__pycache__/admin.cpython-312.pyc -------------------------------------------------------------------------------- /profiles/__pycache__/apps.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/profiles/__pycache__/apps.cpython-312.pyc -------------------------------------------------------------------------------- /profiles/__pycache__/models.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/profiles/__pycache__/models.cpython-312.pyc -------------------------------------------------------------------------------- /profiles/__pycache__/signals.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/profiles/__pycache__/signals.cpython-312.pyc -------------------------------------------------------------------------------- /profiles/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ProfilesConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "profiles" 7 | 8 | def ready(self): 9 | import profiles.signals -------------------------------------------------------------------------------- /profiles/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/profiles/migrations/__init__.py -------------------------------------------------------------------------------- /profiles/migrations/__pycache__/0001_initial.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/profiles/migrations/__pycache__/0001_initial.cpython-312.pyc -------------------------------------------------------------------------------- /profiles/migrations/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/profiles/migrations/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /profiles/routing.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import consumers 3 | 4 | websocket_urlpatterns = [ 5 | path('ws/profile/', consumers.ProfileConsumer.as_asgi()), 6 | ] -------------------------------------------------------------------------------- /profiles/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /profiles/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/project/__init__.py -------------------------------------------------------------------------------- /project/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/project/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /project/__pycache__/settings.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/project/__pycache__/settings.cpython-312.pyc -------------------------------------------------------------------------------- /project/__pycache__/urls.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/project/__pycache__/urls.cpython-312.pyc -------------------------------------------------------------------------------- /project/__pycache__/wsgi.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/project/__pycache__/wsgi.cpython-312.pyc -------------------------------------------------------------------------------- /project/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for project project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | from django.core.asgi import get_asgi_application 12 | from channels.routing import ProtocolTypeRouter, URLRouter 13 | from channels.auth import AuthMiddlewareStack 14 | import project.routing 15 | 16 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings') 17 | 18 | application = ProtocolTypeRouter({ 19 | "http": get_asgi_application(), 20 | "websocket": AuthMiddlewareStack( 21 | URLRouter( 22 | project.routing.application 23 | ) 24 | ), 25 | }) 26 | 27 | -------------------------------------------------------------------------------- /project/routing.py: -------------------------------------------------------------------------------- 1 | # project/routing.py 2 | from channels.routing import ProtocolTypeRouter, URLRouter 3 | from channels.auth import AuthMiddlewareStack 4 | from django.urls import path 5 | from activity.consumers import ActivityConsumer, ReactionConsumer, ShareConsumer 6 | from profiles.consumers import ProfileConsumer 7 | from messaging.consumers import ChatConsumer 8 | from notifications.consumers import NotificationConsumer 9 | from posts.consumers import PostConsumer, CommentConsumer 10 | from jobs.consumers import JobListingConsumer, JobApplicationConsumer 11 | from groups.consumers import GroupConsumer, GroupMembershipConsumer 12 | from followers.consumers import FollowerConsumer, FollowRequestConsumer 13 | from events.consumers import EventConsumer 14 | from courses.consumers import CourseConsumer, CourseEnrollmentConsumer, CourseCompletionConsumer 15 | from connections.consumers import ConnectionConsumer, ConnectionRequestConsumer, RecommendationConsumer 16 | from companies.consumers import CompanyConsumer, CompanyUpdateConsumer 17 | from certifications.consumers import CertificationConsumer 18 | 19 | application = ProtocolTypeRouter({ 20 | "websocket": AuthMiddlewareStack( 21 | URLRouter([ 22 | path("ws/profiles/", ProfileConsumer.as_asgi()), 23 | path("ws/messaging/", ChatConsumer.as_asgi()), 24 | path("ws/notifications/$", NotificationConsumer.as_asgi()), 25 | path("ws/posts/", PostConsumer.as_asgi()), 26 | path("ws/comments/", CommentConsumer.as_asgi()), 27 | path("ws/reactions/", ReactionConsumer.as_asgi()), 28 | path("ws/shares/", ShareConsumer.as_asgi()), 29 | path("ws/jobs/listings/", JobListingConsumer.as_asgi()), 30 | path("ws/jobs/applications/", JobApplicationConsumer.as_asgi()), 31 | path("ws/groups/", GroupConsumer.as_asgi()), 32 | path("ws/groups/memberships/", GroupMembershipConsumer.as_asgi()), 33 | path("ws/followers/", FollowerConsumer.as_asgi()), 34 | path("ws/follow_requests/", FollowRequestConsumer.as_asgi()), 35 | path("ws/events/", EventConsumer.as_asgi()), 36 | path("ws/courses/", CourseConsumer.as_asgi()), 37 | path("ws/courses/enrollments/", CourseEnrollmentConsumer.as_asgi()), 38 | path("ws/courses/completions/", CourseCompletionConsumer.as_asgi()), 39 | path("ws/connections/", ConnectionConsumer.as_asgi()), 40 | path("ws/connection_requests/", ConnectionRequestConsumer.as_asgi()), 41 | path("ws/recommendations/", RecommendationConsumer.as_asgi()), 42 | path("ws/companies/", CompanyConsumer.as_asgi()), 43 | path("ws/company_updates/", CompanyUpdateConsumer.as_asgi()), 44 | path("ws/certifications/", CertificationConsumer.as_asgi()), 45 | path("ws/activity/", ActivityConsumer.as_asgi()), 46 | ]) 47 | ), 48 | }) -------------------------------------------------------------------------------- /project/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for project project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/5.0/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.contrib import admin 18 | from django.urls import path, include 19 | from django.conf import settings 20 | from django.conf.urls.static import static 21 | 22 | from rest_framework import permissions 23 | from drf_yasg.views import get_schema_view 24 | from drf_yasg import openapi 25 | 26 | schema_view = get_schema_view( 27 | openapi.Info( 28 | title="API Documentation", 29 | default_version='v1', 30 | description="API documentation for the follower system", 31 | ), 32 | public=True, 33 | permission_classes=(permissions.AllowAny,), 34 | ) 35 | 36 | urlpatterns = [ 37 | path("admin/", admin.site.urls), 38 | 39 | 40 | path('api/auth/', include('dj_rest_auth.urls')), 41 | path('api/auth/registration/', include('dj_rest_auth.registration.urls')), 42 | path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), 43 | path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), 44 | ] 45 | 46 | 47 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 48 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -------------------------------------------------------------------------------- /project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for project 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/5.0/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", "project.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/requirements.txt -------------------------------------------------------------------------------- /settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/settings/__init__.py -------------------------------------------------------------------------------- /settings/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import SettingType, UserSetting, GlobalSetting 3 | from profiles.models import UserProfile 4 | 5 | @admin.register(SettingType) 6 | class SettingTypeAdmin(admin.ModelAdmin): 7 | list_display = ('name', 'description') 8 | search_fields = ('name', 'description') 9 | list_per_page = 20 10 | ordering = ('name',) 11 | 12 | @admin.register(UserSetting) 13 | class UserSettingAdmin(admin.ModelAdmin): 14 | list_display = ('user', 'setting_type', 'value') 15 | list_filter = ('setting_type',) 16 | search_fields = ('user__user__username', 'setting_type__name') 17 | list_per_page = 20 18 | autocomplete_fields = ['user'] 19 | ordering = ('user__user__username', 'setting_type__name') 20 | 21 | @admin.register(GlobalSetting) 22 | class GlobalSettingAdmin(admin.ModelAdmin): 23 | list_display = ('setting_type', 'value') 24 | search_fields = ('setting_type__name',) 25 | list_per_page = 20 26 | ordering = ('setting_type__name',) 27 | 28 | def has_add_permission(self, request): 29 | return False 30 | 31 | def has_delete_permission(self, request, obj=None): 32 | return False 33 | -------------------------------------------------------------------------------- /settings/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SettingsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "settings" 7 | 8 | def ready(self): 9 | import settings.signals 10 | -------------------------------------------------------------------------------- /settings/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdullahBakir97/Django--LMS--Learning-Management-System/3b50cdfa9fecf93630d158927140c0f90c563279/settings/migrations/__init__.py -------------------------------------------------------------------------------- /settings/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from profiles.models import UserProfile 3 | 4 | class SettingType(models.Model): 5 | name = models.CharField(max_length=100) 6 | description = models.TextField() 7 | 8 | def __str__(self): 9 | return self.name 10 | 11 | class UserSetting(models.Model): 12 | user = models.ForeignKey(UserProfile, on_delete=models.CASCADE) 13 | setting_type = models.ForeignKey(SettingType, on_delete=models.CASCADE) 14 | value = models.TextField() 15 | 16 | class Meta: 17 | unique_together = ['user', 'setting_type'] 18 | 19 | def __str__(self): 20 | return f"{self.user}'s {self.setting_type} setting" 21 | 22 | def save(self, *args, **kwargs): 23 | # Add custom validation logic before saving 24 | super(UserSetting, self).save(*args, **kwargs) 25 | 26 | class GlobalSetting(models.Model): 27 | setting_type = models.ForeignKey(SettingType, on_delete=models.CASCADE) 28 | value = models.TextField() 29 | 30 | class Meta: 31 | unique_together = ['setting_type'] 32 | 33 | def __str__(self): 34 | return str(self.setting_type) 35 | 36 | def save(self, *args, **kwargs): 37 | # Add custom validation logic before saving 38 | super(GlobalSetting, self).save(*args, **kwargs) -------------------------------------------------------------------------------- /settings/services.py: -------------------------------------------------------------------------------- 1 | from .models import UserSetting, GlobalSetting, SettingType 2 | 3 | class UserSettingService: 4 | @staticmethod 5 | def get_user_setting(user, setting_type): 6 | return UserSetting.objects.filter(user=user, setting_type=setting_type).first() 7 | 8 | @staticmethod 9 | def update_user_setting(user, setting_type, value): 10 | user_setting, created = UserSetting.objects.get_or_create(user=user, setting_type=setting_type) 11 | user_setting.value = value 12 | user_setting.save() 13 | return user_setting 14 | 15 | class GlobalSettingService: 16 | @staticmethod 17 | def get_global_setting(setting_type): 18 | return GlobalSetting.objects.filter(setting_type=setting_type).first() 19 | 20 | @staticmethod 21 | def update_global_setting(setting_type, value): 22 | global_setting, created = GlobalSetting.objects.get_or_create(setting_type=setting_type) 23 | global_setting.value = value 24 | global_setting.save() 25 | return global_setting 26 | 27 | class SettingTypeService: 28 | @staticmethod 29 | def get_setting_type(name): 30 | return SettingType.objects.filter(name=name).first() 31 | 32 | @staticmethod 33 | def create_setting_type(name, description): 34 | setting_type, created = SettingType.objects.get_or_create(name=name, description=description) 35 | return setting_type -------------------------------------------------------------------------------- /settings/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save 2 | from django.dispatch import receiver 3 | from profiles.models import UserProfile 4 | from .models import UserSetting, GlobalSetting, SettingType 5 | 6 | @receiver(post_save, sender=UserProfile) 7 | def create_user_settings(sender, instance, created, **kwargs): 8 | if created: 9 | # Create default user settings for a new user 10 | default_setting_type = SettingType.objects.get_or_create(name='default_user_setting', description='Default User Setting')[0] 11 | UserSetting.objects.create(user=instance, setting_type=default_setting_type, value='default_value') 12 | 13 | @receiver(post_save, sender=SettingType) 14 | def create_global_settings(sender, instance, created, **kwargs): 15 | if created: 16 | # Create global settings based on new setting types 17 | GlobalSetting.objects.get_or_create(setting_type=instance, value='default_value') -------------------------------------------------------------------------------- /settings/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /settings/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | --------------------------------------------------------------------------------