├── .gitignore ├── LICENSE ├── README.md ├── channels-rapid.sublime-project ├── channels-rapid.sublime-workspace ├── requirements.txt └── src ├── .DS_Store ├── cfehome ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── chat ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── templates │ ├── .DS_Store │ └── chat │ │ ├── inbox.html │ │ └── thread.html ├── tests.py ├── urls.py └── views.py ├── manage.py └── templates ├── base.html ├── css.html ├── home.html └── js.html /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | include/ 3 | pip-selfcheck.json 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # SageMath parsed files 86 | *.sage.py 87 | 88 | # Environments 89 | .env 90 | .venv 91 | env/ 92 | venv/ 93 | ENV/ 94 | env.bak/ 95 | venv.bak/ 96 | 97 | # Spyder project settings 98 | .spyderproject 99 | .spyproject 100 | 101 | # Rope project settings 102 | .ropeproject 103 | 104 | # mkdocs documentation 105 | /site 106 | 107 | # mypy 108 | .mypy_cache/ 109 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Coding For Entrepreneurs 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rapid-ChatXChannels 2 | A rapid fire tutorial and introduction of Django Channels. To get more in-depth check out the full course https://kirr.co/badl8e 3 | 4 | 5 | YouTube Video: __coming soon__ 6 | 7 | 8 | 9 | ### Recommended Start 10 | ```bash 11 | 12 | $ cd path/to/your/dev/folder 13 | $ mkdir channels-rapid 14 | $ cd channels-rapid 15 | $ git clone https://github.com/codingforentrepreneurs/Rapid-ChatXChannels . 16 | $ git reset a9a2c42052c87fd2eb5acdc417729f9359a1e087 --hard 17 | $ git remote remove origin 18 | $ virtualenv -p python3 . 19 | $ source bin/activate 20 | (channels-rapid) $ pip install -r requirements.txt 21 | (channels-rapid) $ cd src 22 | (channels-rapid) $ python manage.py migrate 23 | (channels-rapid) $ python manage.py createsuperuser 24 | ... do the creation 25 | (channels-rapid) $ python manage.py createsuperuser 26 | ... create second super user 27 | ``` 28 | 29 | 30 | ### Install Redis 31 | 1. Download Redis 32 | - Using [Homebrew](http://brew.sh): 33 | ``` 34 | brew install redis 35 | 36 | brew services start redis 37 | ``` 38 | Brew permission errors? Try `sudo chown -R "$USER":admin /usr/local` 39 | 40 | - Direct [Download](http://redis.io/download) 41 | 42 | 2. Open & Test Redis: 43 | - open Terminal 44 | 45 | - **redis-cli ping** 46 | ``` 47 | $ redis-cli ping 48 | PONG 49 | ``` 50 | 51 | - **redis-server** 52 | ``` 53 | $ redis-server 54 | 86750:C 08 Nov 08:17:21.431 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf 55 | 86750:M 08 Nov 08:17:21.433 * Increased maximum number of open files to 10032 (it was originally set to 256). 56 | _._ 57 | _.-``__ ''-._ 58 | _.-`` `. `_. ''-._ Redis 3.2.5 (00000000/0) 64 bit 59 | .-`` .-```. ```\/ _.,_ ''-._ 60 | ( ' , .-` | `, ) Running in standalone mode 61 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 62 | | `-._ `._ / _.-' | PID: 86750 63 | `-._ `-._ `-./ _.-' _.-' 64 | |`-._`-._ `-.__.-' _.-'_.-'| 65 | | `-._`-._ _.-'_.-' | http://redis.io 66 | `-._ `-._`-.__.-'_.-' _.-' 67 | |`-._`-._ `-.__.-' _.-'_.-'| 68 | | `-._`-._ _.-'_.-' | 69 | `-._ `-._`-.__.-'_.-' _.-' 70 | `-._ `-.__.-' _.-' 71 | `-._ _.-' 72 | `-.__.-' 73 | 74 | 86750:M 08 Nov 08:17:21.434 # Server started, Redis version 3.2.5 75 | 86750:M 08 Nov 08:17:21.434 * The server is now ready to accept connections on port 6379 76 | 77 | ``` 78 | **Close Redis** with `control` + `c` to quit -------------------------------------------------------------------------------- /channels-rapid.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": "." 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /channels-rapid.sublime-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "auto_complete": 3 | { 4 | "selected_items": 5 | [ 6 | [ 7 | "templa", 8 | "template" 9 | ], 10 | [ 11 | "home", 12 | "home_view" 13 | ], 14 | [ 15 | "posts", 16 | "Posts" 17 | ], 18 | [ 19 | "get_or", 20 | "get_or_new" 21 | ], 22 | [ 23 | "other", 24 | "other_username" 25 | ], 26 | [ 27 | "get", 28 | "get_thread" 29 | ], 30 | [ 31 | "msg", 32 | "msgData" 33 | ], 34 | [ 35 | "mes", 36 | "message_data" 37 | ], 38 | [ 39 | "room", 40 | "room_group_name" 41 | ], 42 | [ 43 | "chan", 44 | "channel_layer" 45 | ], 46 | [ 47 | "roo", 48 | "room_group_name" 49 | ], 50 | [ 51 | "tex", 52 | "text_data" 53 | ], 54 | [ 55 | "message", 56 | "messageText" 57 | ], 58 | [ 59 | "BA", 60 | "BASE_DIR" 61 | ], 62 | [ 63 | "POst", 64 | "PostManager" 65 | ], 66 | [ 67 | "get_", 68 | "get_recent_status" 69 | ], 70 | [ 71 | "Stat", 72 | "Status" 73 | ], 74 | [ 75 | "ser", 76 | "serializer_class" 77 | ], 78 | [ 79 | "file", 80 | "file_data" 81 | ], 82 | [ 83 | "us", 84 | "username" 85 | ], 86 | [ 87 | "valid", 88 | "validated_data" 89 | ], 90 | [ 91 | "pass", 92 | "password2" 93 | ], 94 | [ 95 | "User", 96 | "UserRegisterSerializer" 97 | ], 98 | [ 99 | "se", 100 | "serializers" 101 | ], 102 | [ 103 | "pas", 104 | "password2" 105 | ], 106 | [ 107 | "aut", 108 | "authenticated" 109 | ], 110 | [ 111 | "res", 112 | "rest_framework" 113 | ], 114 | [ 115 | "t", 116 | "timezone" 117 | ], 118 | [ 119 | "jw", 120 | "jwt_response_payload_handler" 121 | ], 122 | [ 123 | "json", 124 | "json_str" 125 | ], 126 | [ 127 | "b", 128 | "b64encode" 129 | ], 130 | [ 131 | "per", 132 | "permissions" 133 | ], 134 | [ 135 | "au", 136 | "authentication_classes" 137 | ], 138 | [ 139 | "cont", 140 | "Content-Type" 141 | ], 142 | [ 143 | "pos", 144 | "post_data" 145 | ], 146 | [ 147 | "post", 148 | "post_method" 149 | ], 150 | [ 151 | "content", 152 | "content-type" 153 | ], 154 | [ 155 | "po", 156 | "post_data" 157 | ], 158 | [ 159 | "img", 160 | "image_path" 161 | ], 162 | [ 163 | "do", 164 | "do_img" 165 | ], 166 | [ 167 | "bod", 168 | "body_" 169 | ], 170 | [ 171 | "query", 172 | "query_get" 173 | ], 174 | [ 175 | "quer", 176 | "query_body" 177 | ], 178 | [ 179 | "gene", 180 | "generics" 181 | ], 182 | [ 183 | "sa", 184 | "serializer" 185 | ], 186 | [ 187 | "s", 188 | "serializers" 189 | ], 190 | [ 191 | "clea", 192 | "cleaned_data" 193 | ], 194 | [ 195 | "St", 196 | "StatusForm" 197 | ], 198 | [ 199 | "staut", 200 | "StatusForm" 201 | ], 202 | [ 203 | "passed", 204 | "passed_id" 205 | ], 206 | [ 207 | "de", 208 | "deleted_" 209 | ], 210 | [ 211 | "sav", 212 | "saved_data" 213 | ], 214 | [ 215 | "is", 216 | "is_json" 217 | ], 218 | [ 219 | "form", 220 | "form_data" 221 | ], 222 | [ 223 | "err", 224 | "error_data" 225 | ], 226 | [ 227 | "Updat", 228 | "UpdateModel" 229 | ], 230 | [ 231 | "Up", 232 | "UpdateModel" 233 | ], 234 | [ 235 | "Mo", 236 | "UpdateModelForm" 237 | ], 238 | [ 239 | "crf", 240 | "csrf_exempt" 241 | ], 242 | [ 243 | "new", 244 | "new_data" 245 | ], 246 | [ 247 | "st", 248 | "status_code" 249 | ], 250 | [ 251 | "lis", 252 | "list_values" 253 | ], 254 | [ 255 | "js", 256 | "json" 257 | ], 258 | [ 259 | "render", 260 | "render_to_json_response" 261 | ], 262 | [ 263 | "con", 264 | "context" 265 | ], 266 | [ 267 | "re", 268 | "render" 269 | ], 270 | [ 271 | "Char", 272 | "ChargeManager" 273 | ], 274 | [ 275 | "stri", 276 | "stripe_charge" 277 | ], 278 | [ 279 | "strip", 280 | "stripe_charge" 281 | ], 282 | [ 283 | "card", 284 | "card_obj" 285 | ], 286 | [ 287 | "or", 288 | "other_ids" 289 | ], 290 | [ 291 | "sess", 292 | "session_key" 293 | ], 294 | [ 295 | "Login", 296 | "LoginForm" 297 | ], 298 | [ 299 | "full", 300 | "full_name" 301 | ], 302 | [ 303 | "is_", 304 | "is_staff" 305 | ], 306 | [ 307 | "user", 308 | "user_obj" 309 | ], 310 | [ 311 | "auto_", 312 | "auto_now_add" 313 | ], 314 | [ 315 | "add", 316 | "address_line_2" 317 | ], 318 | [ 319 | "line", 320 | "line1" 321 | ], 322 | [ 323 | "ship", 324 | "shipping_address" 325 | ], 326 | [ 327 | "bil", 328 | "billing_address_id" 329 | ], 330 | [ 331 | "Bil", 332 | "BillingProfile" 333 | ], 334 | [ 335 | "Bill", 336 | "BillingProfileManager" 337 | ], 338 | [ 339 | "obj", 340 | "order_obj" 341 | ], 342 | [ 343 | "order", 344 | "order_obj" 345 | ], 346 | [ 347 | "billing", 348 | "billing_profile" 349 | ], 350 | [ 351 | "car", 352 | "cart_removal_qs" 353 | ], 354 | [ 355 | "gues", 356 | "guest_email_id" 357 | ], 358 | [ 359 | "cl", 360 | "cleaned_data" 361 | ], 362 | [ 363 | "bill", 364 | "billing_profile" 365 | ], 366 | [ 367 | "next", 368 | "next_post" 369 | ], 370 | [ 371 | "nu", 372 | "num1" 373 | ], 374 | [ 375 | "cart", 376 | "cart_obj" 377 | ], 378 | [ 379 | "ran", 380 | "random_string_generator" 381 | ], 382 | [ 383 | "pr", 384 | "product_id" 385 | ], 386 | [ 387 | "Pr", 388 | "ProductDetailSlugView" 389 | ], 390 | [ 391 | "prd", 392 | "product_id" 393 | ], 394 | [ 395 | "Car", 396 | "CartManager" 397 | ], 398 | [ 399 | "tag", 400 | "tag_set" 401 | ], 402 | [ 403 | "drop", 404 | "dropdown-menu" 405 | ], 406 | [ 407 | "nav", 408 | "navbar" 409 | ], 410 | [ 411 | "fea", 412 | "featured" 413 | ], 414 | [ 415 | "conta", 416 | "contact_form" 417 | ], 418 | [ 419 | "mar", 420 | "margin-bottom" 421 | ], 422 | [ 423 | "en", 424 | "environ" 425 | ], 426 | [ 427 | "image", 428 | "image_path" 429 | ], 430 | [ 431 | "in", 432 | "instanceof\tkeyword" 433 | ], 434 | [ 435 | "Vid", 436 | "VideoManager" 437 | ], 438 | [ 439 | "Vide", 440 | "VideoQuerySet" 441 | ], 442 | [ 443 | "q", 444 | "query" 445 | ], 446 | [ 447 | "u", 448 | "unique_slug_generator" 449 | ], 450 | [ 451 | "col-", 452 | "col-sm-12" 453 | ], 454 | [ 455 | "h", 456 | "homeImageList\tproperty" 457 | ], 458 | [ 459 | "r", 460 | "routeSub\tproperty" 461 | ], 462 | [ 463 | "Av", 464 | "ActivatedRoute\talias" 465 | ], 466 | [ 467 | "p", 468 | "push\tmethod" 469 | ], 470 | [ 471 | "max", 472 | "max-height" 473 | ], 474 | [ 475 | "margin-", 476 | "margin-bottom" 477 | ], 478 | [ 479 | "min", 480 | "min-height" 481 | ], 482 | [ 483 | "by", 484 | "bypassSecurityTrustResourceUrl\tmethod" 485 | ], 486 | [ 487 | "Pip", 488 | "PipeTransform\talias" 489 | ], 490 | [ 491 | "imp", 492 | "implements\tkeyword" 493 | ], 494 | [ 495 | "cons", 496 | "console\tvar" 497 | ], 498 | [ 499 | "rou", 500 | "RouterModule\talias" 501 | ], 502 | [ 503 | "Rou", 504 | "RouterModule\talias" 505 | ], 506 | [ 507 | "fu", 508 | "function\tkeyword" 509 | ], 510 | [ 511 | "bas", 512 | "basil3\tlet" 513 | ], 514 | [ 515 | "login", 516 | "loginUrl" 517 | ] 518 | ] 519 | }, 520 | "buffers": 521 | [ 522 | { 523 | "settings": 524 | { 525 | "buffer_size": 0, 526 | "line_ending": "Unix" 527 | } 528 | } 529 | ], 530 | "build_system": "", 531 | "build_system_choices": 532 | [ 533 | ], 534 | "build_varint": "", 535 | "command_palette": 536 | { 537 | "height": 87.0, 538 | "last_filter": "", 539 | "selected_items": 540 | [ 541 | [ 542 | "install", 543 | "Package Control: Install Package" 544 | ], 545 | [ 546 | "set", 547 | "Set Syntax: TypeScriptReact" 548 | ], 549 | [ 550 | "mark", 551 | "Markdown Preview: Preview in Browser" 552 | ], 553 | [ 554 | "instal", 555 | "Package Control: Install Package" 556 | ], 557 | [ 558 | "ins", 559 | "Package Control: Install Package" 560 | ], 561 | [ 562 | "pack", 563 | "Package Control: Install Package" 564 | ], 565 | [ 566 | "mar", 567 | "Markdown Preview: Preview in Browser" 568 | ], 569 | [ 570 | "markd", 571 | "Markdown Preview: Preview in Browser" 572 | ], 573 | [ 574 | "markdown", 575 | "Markdown Preview: Preview in Browser" 576 | ], 577 | [ 578 | "", 579 | "Convert Case: Lower Case" 580 | ], 581 | [ 582 | "package", 583 | "Package Control: List Packages" 584 | ] 585 | ], 586 | "width": 485.0 587 | }, 588 | "console": 589 | { 590 | "height": 156.0, 591 | "history": 592 | [ 593 | ] 594 | }, 595 | "distraction_free": 596 | { 597 | "menu_visible": true, 598 | "show_minimap": false, 599 | "show_open_files": false, 600 | "show_tabs": false, 601 | "side_bar_visible": false, 602 | "status_bar_visible": false 603 | }, 604 | "expanded_folders": 605 | [ 606 | "/Users/cfe/Dev/channels-rapid" 607 | ], 608 | "file_history": 609 | [ 610 | "/Users/cfe/Dev/trydjango/src/templates/product/detail.html", 611 | "/Users/cfe/Dev/trydjango/src/templates/products/product_detail.html", 612 | "/Users/cfe/Dev/trydjango/src/pages/views.py", 613 | "/Users/cfe/Dev/trydjango/src/products/views.py", 614 | "/Users/cfe/Dev/trydjango/src/trydjango/settings.py", 615 | "/Users/cfe/Dev/trydjango/src/templates/home.html", 616 | "/Users/cfe/Dev/trydjango/src/templates/about.html", 617 | "/Users/cfe/Dev/trydjango/src/templates/base.html", 618 | "/Users/cfe/Dev/trydjango/src/templates/navbar.html", 619 | "/Users/cfe/Dev/trydjango/src/templates/contact.html", 620 | "/Users/cfe/Dev/trydjango/src/trydjango/urls.py", 621 | "/Users/cfe/Dev/trydjango/src/products/admin.py", 622 | "/Users/cfe/Dev/trydjango/src/products/models.py", 623 | "/Users/cfe/Dev/trydjango/.gitignore", 624 | "/Users/cfe/Dev/trydjango/trydjango.sublime-project", 625 | "/Volumes/CFE_2018/CFE Projects/Current/Try Reactjs/Artwork/YouTube/description.txt", 626 | "/Volumes/CFE_2018/CFE Projects/Current/Try Reactjs/Artwork/YouTube/tags.txt", 627 | "/Users/cfe/Dev/try-reactjs/README.md", 628 | "/Users/cfe/Dev/try-reactjs/src/App.js", 629 | "/Users/cfe/Dev/try-reactjs/package.json", 630 | "/Users/cfe/Dev/channels/src/chat/consumers.py", 631 | "/Users/cfe/Dev/channels/src/cfehome/asgi.py", 632 | "/Users/cfe/Dev/channels/src/cfehome/asgi_production.py", 633 | "/Users/cfe/Dev/channels/src/cfehome/settings.py", 634 | "/Users/cfe/Dev/channels/src/cfehome/production.py", 635 | "/Users/cfe/Dev/channels/src/cfehome/settings/__init__.py", 636 | "/Users/cfe/Dev/channels/src/.gitignore", 637 | "/Users/cfe/Dev/channels/src/cfehome/settings/base.py", 638 | "/Users/cfe/Dev/channels/src/cfehome/settings/local.py", 639 | "/Users/cfe/Dev/channels/src/cfehome/settings/production.py", 640 | "/Users/cfe/Dev/channels/src/templates/js.html", 641 | "/Users/cfe/Dev/channels/src/chat/templates/chat/thread.html", 642 | "/Users/cfe/Dev/channels/readme.md", 643 | "/Users/cfe/Dev/channels/src/cfehome/wsgi.py", 644 | "/Users/cfe/Dev/channels/src/manage.py", 645 | "/Users/cfe/Dev/channels/src/Procfile", 646 | "/Volumes/CFE_2018/CFE Projects/Current/Chat x Channels/Blog/Django-Channels-To-Production.md", 647 | "/Users/cfe/Dev/channels/src/cfehome/routing.py", 648 | "/Users/cfe/Dev/channels/src/templates/base.html", 649 | "/Users/cfe/Dev/channels/src/chat/utils.py", 650 | "/Users/cfe/Dev/channels/src/chat/models.py", 651 | "/Users/cfe/Dev/channels/src/chat/views.py", 652 | "/Users/cfe/Dev/channels/src/cfehome/asgi2.py", 653 | "/Users/cfe/Dev/channels/src/chat/js-tests/console.tests.js", 654 | "/Users/cfe/Dev/channels/src/chat/forms.py", 655 | "/Users/cfe/Dev/channels/src/templates/home.html", 656 | "/Users/cfe/Dev/channels/.gitignore", 657 | "/Users/cfe/Dev/channels/parse_git_log.py", 658 | "/Users/cfe/Dev/channels/src/templates/css.html", 659 | "/Users/cfe/Dev/channels/src/chat/templates/chat/inbox.html", 660 | "/Users/cfe/Dev/channels/src/chat/urls.py", 661 | "/Users/cfe/Dev/channels/src/cfehome/urls.py", 662 | "/Users/cfe/Dev/channels/src/chat/admin.py", 663 | "/Users/cfe/Dev/cfehome/src/search/views.py", 664 | "/Users/cfe/Dev/cfehome/src/courses/models.py", 665 | "/Users/cfe/Dev/cfehome/src/cfehome/urls.py", 666 | "/Users/cfe/Dev/cfehome/src/profiles/admin.py", 667 | "/Users/cfe/Dev/cfehome/src/blog/models.py", 668 | "/Users/cfe/Dev/cfehome/src/blog/admin.py", 669 | "/Users/cfe/Dev/cfehome/src/templates/base.html", 670 | "/Users/cfe/Dev/cfehome/src/profiles/models.py", 671 | "/Users/cfe/Dev/cfehome/src/cfehome/settings/base.py", 672 | "/Users/cfe/Dev/cfehome/src/cfehome/settings/local.py", 673 | "/Users/cfe/Dev/cfehome/src/cfehome/settings/production.py", 674 | "/Users/cfe/Dev/cfehome/src/courses/admin.py", 675 | "/Users/cfe/Dev/cfehome/src/search/templates/search/view.html", 676 | "/Users/cfe/Dropbox/CFE Projects/tests/client/cfe-rest-client/package.json", 677 | "/Users/cfe/Dropbox/CFE Projects/tests/src/cfeapi/settings.py", 678 | "/Users/cfe/Dropbox/CFE Projects/tests/client/cfe-rest-client/src/app/status-create/status-create.component.html", 679 | "/Users/cfe/Dropbox/CFE Projects/tests/client/cfe-rest-client/src/app/status-create/status-create.component.ts", 680 | "/Users/cfe/Dropbox/CFE Projects/tests/client/cfe-rest-client/src/app/app-routing.module.ts", 681 | "/Users/cfe/Dropbox/CFE Projects/tests/client/cfe-rest-client/src/app/status-detail/status-detail.component.ts", 682 | "/Volumes/CFE/CFE Projects/Current/Rest API/Sections/3 - Django Rest Framework API/Tests/test_rest_api.py", 683 | "/Users/cfe/Dev/restapi/src/accounts/api/permissions.py", 684 | "/Users/cfe/Dev/restapi/src/status/api/views.py", 685 | "/Users/cfe/Dev/restapi/src/status/api/serializers.py", 686 | "/Users/cfe/Dev/restapi/src/status/api/urls.py", 687 | "/Users/cfe/Dev/restapi/src/status/models.py", 688 | "/Users/cfe/Dev/restapi/scripts/cfe_rest_framework_api.py", 689 | "/Users/cfe/Dev/restapi/src/accounts/api/views.py", 690 | "/Users/cfe/Dev/restapi/src/accounts/api/serializers.py", 691 | "/Users/cfe/Dev/restapi/src/accounts/api/user/__init__.py", 692 | "/Users/cfe/Dev/restapi/src/accounts/api/user/urls.py", 693 | "/Users/cfe/Dev/restapi/src/accounts/api/user/serializers.py", 694 | "/Users/cfe/Dev/restapi/src/accounts/api/urls.py", 695 | "/Users/cfe/Dev/restapi/src/cfeapi/urls.py", 696 | "/Users/cfe/Dev/restapi/src/accounts/api/user/views.py", 697 | "/Users/cfe/Dev/restapi/src/cfeapi/restconf/pagination.py", 698 | "/Users/cfe/Dev/restapi/src/accounts/api/urls_user.py", 699 | "/Users/cfe/Dev/restapi/README.md", 700 | "/Users/cfe/Dev/restapi/parsed_log.md", 701 | "/Users/cfe/Dev/restapi/src/cfeapi/settings.py", 702 | "/Users/cfe/Dev/restapi/src/accounts/api/__init__.py", 703 | "/Users/cfe/Dev/restapi/src/accounts/api/utils.py", 704 | "/Users/cfe/Dev/restapi/src/cfeapi/restconf/__init__.py", 705 | "/Users/cfe/Dev/restapi/scripts/cfe_pure_api.py", 706 | "/Users/cfe/Dev/restapi/.gitignore", 707 | "/Users/cfe/Dev/restapi/static-server/media-root/status/readme.txt", 708 | "/Users/cfe/Dev/restapi/src/updates/api/views.py", 709 | "/Users/cfe/Dev/restapi/src/status/api/shell_examples.py", 710 | "/Users/cfe/Dev/restapi/src/status/forms.py", 711 | "/Users/cfe/Dev/restapi/src/status/admin.py", 712 | "/Users/cfe/Dev/restapi/src/status/api/__init__.py", 713 | "/Users/cfe/Dev/restapi/src/updates/views.py", 714 | "/Users/cfe/Dev/restapi/src/status/serializers.py", 715 | "/Users/cfe/Dev/restapi/src/updates/models.py", 716 | "/Users/cfe/Dev/restapi/src/updates/forms.py", 717 | "/Users/cfe/Dev/restapi/LICENSE", 718 | "/Users/cfe/Dev/restapi/src/updates/api/utils.py", 719 | "/Users/cfe/Dev/restapi/src/updates/api/urls.py", 720 | "/Users/cfe/Dev/restapi/src/updates/api/mixins.py", 721 | "/Users/cfe/Dev/restapi/src/cfeapi/mixins.py", 722 | "/Users/cfe/Dev/restapi/lib/python3.6/site-packages/django/http/response.py", 723 | "/Users/cfe/Dev/restapi/scripts/api_detail.py", 724 | "/Users/cfe/Dev/restapi/src/updates/admin.py", 725 | "/Users/cfe/Dev/restapi/src/updates/api/__init__.py", 726 | "/Users/cfe/Documents/notes_delete.js", 727 | "/Users/cfe/Dropbox/CFE Projects/Blog/Articles/Custom-Analytics-with-Django.md", 728 | "/Users/cfe/Desktop/notes_delete/stripe_notes.py", 729 | "/Users/cfe/Desktop/notes_delete/code.py", 730 | "/Users/cfe/Dev/ecommerce/src/accounts/forms.py", 731 | "/Users/cfe/Dev/ecommerce/src/accounts/admin.py", 732 | "/Users/cfe/Dev/ecommerce/src/ecommerce/urls.py", 733 | "/Users/cfe/Dev/ecommerce/src/ecommerce/settings.py", 734 | "/Users/cfe/Dev/ecommerce/src/accounts/models.py", 735 | "/Users/cfe/Dev/ecommerce/src/accounts/views.py", 736 | "/Users/cfe/Dev/ecommerce/README.md", 737 | "/Users/cfe/Dev/ecommerce/parsed_log.md" 738 | ], 739 | "find": 740 | { 741 | "height": 80.0 742 | }, 743 | "find_in_files": 744 | { 745 | "height": 232.0, 746 | "where_history": 747 | [ 748 | ] 749 | }, 750 | "find_state": 751 | { 752 | "case_sensitive": false, 753 | "find_history": 754 | [ 755 | ], 756 | "highlight": false, 757 | "in_selection": false, 758 | "preserve_case": false, 759 | "regex": false, 760 | "replace_history": 761 | [ 762 | ], 763 | "reverse": false, 764 | "show_context": false, 765 | "use_buffer2": false, 766 | "whole_word": false, 767 | "wrap": true 768 | }, 769 | "groups": 770 | [ 771 | { 772 | "selected": 0, 773 | "sheets": 774 | [ 775 | { 776 | "buffer": 0, 777 | "semi_transient": false, 778 | "settings": 779 | { 780 | "buffer_size": 0, 781 | "regions": 782 | { 783 | }, 784 | "selection": 785 | [ 786 | [ 787 | 0, 788 | 0 789 | ] 790 | ], 791 | "settings": 792 | { 793 | "syntax": "Packages/Text/Plain text.tmLanguage" 794 | }, 795 | "translation.x": 0.0, 796 | "translation.y": 0.0, 797 | "zoom_level": 1.0 798 | }, 799 | "stack_index": 0, 800 | "type": "text" 801 | } 802 | ] 803 | } 804 | ], 805 | "incremental_find": 806 | { 807 | "height": 80.0 808 | }, 809 | "input": 810 | { 811 | "height": 80.0 812 | }, 813 | "layout": 814 | { 815 | "cells": 816 | [ 817 | [ 818 | 0, 819 | 0, 820 | 1, 821 | 1 822 | ] 823 | ], 824 | "cols": 825 | [ 826 | 0.0, 827 | 1.0 828 | ], 829 | "rows": 830 | [ 831 | 0.0, 832 | 1.0 833 | ] 834 | }, 835 | "menu_visible": true, 836 | "output.doc": 837 | { 838 | "height": 0.0 839 | }, 840 | "output.find_results": 841 | { 842 | "height": 508.0 843 | }, 844 | "pinned_build_system": "", 845 | "project": "channels-rapid.sublime-project", 846 | "replace": 847 | { 848 | "height": 156.0 849 | }, 850 | "save_all_on_build": true, 851 | "select_file": 852 | { 853 | "height": 0.0, 854 | "last_filter": "", 855 | "selected_items": 856 | [ 857 | [ 858 | "email", 859 | "templates/team/email_instructions.html" 860 | ] 861 | ], 862 | "width": 0.0 863 | }, 864 | "select_project": 865 | { 866 | "height": 0.0, 867 | "last_filter": "", 868 | "selected_items": 869 | [ 870 | ], 871 | "width": 0.0 872 | }, 873 | "select_symbol": 874 | { 875 | "height": 0.0, 876 | "last_filter": "", 877 | "selected_items": 878 | [ 879 | ], 880 | "width": 0.0 881 | }, 882 | "selected_group": 0, 883 | "settings": 884 | { 885 | }, 886 | "show_minimap": false, 887 | "show_open_files": true, 888 | "show_tabs": true, 889 | "side_bar_visible": true, 890 | "side_bar_width": 241.0, 891 | "status_bar_visible": true, 892 | "template_settings": 893 | { 894 | } 895 | } 896 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==2.0.7 2 | pytz==2018.5 3 | redis==2.10.6 4 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Rapid-ChatXChannels/3ca9f8edc3382709add573f5982998e87b5448b7/src/.DS_Store -------------------------------------------------------------------------------- /src/cfehome/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Rapid-ChatXChannels/3ca9f8edc3382709add573f5982998e87b5448b7/src/cfehome/__init__.py -------------------------------------------------------------------------------- /src/cfehome/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for cfehome project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.0.7. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'em-9$ln6^a0z!s2pbo=mu*l$cgnqgsyd_z21f-%2d(_h7*wu^0' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ['*'] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'chat', 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | 'django.middleware.security.SecurityMiddleware', 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | ] 52 | 53 | ROOT_URLCONF = 'cfehome.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'cfehome.wsgi.application' 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 76 | 77 | DATABASES = { 78 | 'default': { 79 | 'ENGINE': 'django.db.backends.sqlite3', 80 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 81 | } 82 | } 83 | 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 91 | }, 92 | { 93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 94 | }, 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 100 | }, 101 | ] 102 | 103 | 104 | # Internationalization 105 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ 106 | 107 | LANGUAGE_CODE = 'en-us' 108 | 109 | TIME_ZONE = 'UTC' 110 | 111 | USE_I18N = True 112 | 113 | USE_L10N = True 114 | 115 | USE_TZ = True 116 | 117 | 118 | # Static files (CSS, JavaScript, Images) 119 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ 120 | 121 | STATIC_URL = '/static/' 122 | -------------------------------------------------------------------------------- /src/cfehome/urls.py: -------------------------------------------------------------------------------- 1 | """cfehome URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | from django.views.generic import TemplateView 19 | 20 | urlpatterns = [ 21 | path('', TemplateView.as_view(template_name="home.html")), 22 | path('admin/', admin.site.urls), 23 | path('messages/', include('chat.urls')), 24 | ] 25 | -------------------------------------------------------------------------------- /src/cfehome/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for cfehome 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/2.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", "cfehome.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /src/chat/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Rapid-ChatXChannels/3ca9f8edc3382709add573f5982998e87b5448b7/src/chat/__init__.py -------------------------------------------------------------------------------- /src/chat/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | 4 | from .models import Thread, ChatMessage 5 | 6 | class ChatMessage(admin.TabularInline): 7 | model = ChatMessage 8 | 9 | class ThreadAdmin(admin.ModelAdmin): 10 | inlines = [ChatMessage] 11 | class Meta: 12 | model = Thread 13 | 14 | 15 | admin.site.register(Thread, ThreadAdmin) 16 | -------------------------------------------------------------------------------- /src/chat/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ChatConfig(AppConfig): 5 | name = 'chat' 6 | -------------------------------------------------------------------------------- /src/chat/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | 4 | class ComposeForm(forms.Form): 5 | message = forms.CharField( 6 | widget=forms.TextInput( 7 | attrs={"class": "form-control"} 8 | ) 9 | ) -------------------------------------------------------------------------------- /src/chat/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.7 on 2018-07-06 18:43 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='ChatMessage', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('message', models.TextField()), 22 | ('timestamp', models.DateTimeField(auto_now_add=True)), 23 | ], 24 | ), 25 | migrations.CreateModel( 26 | name='Thread', 27 | fields=[ 28 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 29 | ('updated', models.DateTimeField(auto_now=True)), 30 | ('timestamp', models.DateTimeField(auto_now_add=True)), 31 | ('first', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chat_thread_first', to=settings.AUTH_USER_MODEL)), 32 | ('second', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chat_thread_second', to=settings.AUTH_USER_MODEL)), 33 | ], 34 | ), 35 | migrations.AddField( 36 | model_name='chatmessage', 37 | name='thread', 38 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='chat.Thread'), 39 | ), 40 | migrations.AddField( 41 | model_name='chatmessage', 42 | name='user', 43 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='sender'), 44 | ), 45 | ] 46 | -------------------------------------------------------------------------------- /src/chat/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Rapid-ChatXChannels/3ca9f8edc3382709add573f5982998e87b5448b7/src/chat/migrations/__init__.py -------------------------------------------------------------------------------- /src/chat/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from django.conf import settings 4 | from django.db import models 5 | from django.db.models import Q 6 | 7 | 8 | class ThreadManager(models.Manager): 9 | def by_user(self, user): 10 | qlookup = Q(first=user) | Q(second=user) 11 | qlookup2 = Q(first=user) & Q(second=user) 12 | qs = self.get_queryset().filter(qlookup).exclude(qlookup2).distinct() 13 | return qs 14 | 15 | def get_or_new(self, user, other_username): # get_or_create 16 | username = user.username 17 | if username == other_username: 18 | return None 19 | qlookup1 = Q(first__username=username) & Q(second__username=other_username) 20 | qlookup2 = Q(first__username=other_username) & Q(second__username=username) 21 | qs = self.get_queryset().filter(qlookup1 | qlookup2).distinct() 22 | if qs.count() == 1: 23 | return qs.first(), False 24 | elif qs.count() > 1: 25 | return qs.order_by('timestamp').first(), False 26 | else: 27 | Klass = user.__class__ 28 | user2 = Klass.objects.get(username=other_username) 29 | if user != user2: 30 | obj = self.model( 31 | first=user, 32 | second=user2 33 | ) 34 | obj.save() 35 | return obj, True 36 | return None, False 37 | 38 | 39 | class Thread(models.Model): 40 | first = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_first') 41 | second = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_second') 42 | updated = models.DateTimeField(auto_now=True) 43 | timestamp = models.DateTimeField(auto_now_add=True) 44 | 45 | objects = ThreadManager() 46 | 47 | @property 48 | def room_group_name(self): 49 | return f'chat_{self.id}' 50 | 51 | def broadcast(self, msg=None): 52 | if msg is not None: 53 | broadcast_msg_to_chat(msg, group_name=self.room_group_name, user='admin') 54 | return True 55 | return False 56 | 57 | 58 | class ChatMessage(models.Model): 59 | thread = models.ForeignKey(Thread, null=True, blank=True, on_delete=models.SET_NULL) 60 | user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='sender', on_delete=models.CASCADE) 61 | message = models.TextField() 62 | timestamp = models.DateTimeField(auto_now_add=True) -------------------------------------------------------------------------------- /src/chat/templates/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingforentrepreneurs/Rapid-ChatXChannels/3ca9f8edc3382709add573f5982998e87b5448b7/src/chat/templates/.DS_Store -------------------------------------------------------------------------------- /src/chat/templates/chat/inbox.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | 5 | 6 | {% endblock %} -------------------------------------------------------------------------------- /src/chat/templates/chat/thread.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |

Thread for {% if user != object.first %}{{ object.first }}{% else %}{{ object.second }}{% endif %}

5 | 12 | 13 |
{% csrf_token %} 14 | {{form.as_p }} 15 | 16 |
17 | 18 | {% endblock %} 19 | 20 | {% block script %} 21 | 22 | 26 | {% endblock %} -------------------------------------------------------------------------------- /src/chat/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /src/chat/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, re_path 2 | 3 | 4 | from .views import ThreadView, InboxView 5 | 6 | app_name = 'chat' 7 | urlpatterns = [ 8 | path("", InboxView.as_view()), 9 | re_path(r"^(?P[\w.@+-]+)", ThreadView.as_view()), 10 | ] 11 | -------------------------------------------------------------------------------- /src/chat/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import LoginRequiredMixin 2 | from django.http import Http404, HttpResponseForbidden 3 | from django.shortcuts import render 4 | from django.urls import reverse 5 | from django.views.generic.edit import FormMixin 6 | 7 | from django.views.generic import DetailView, ListView 8 | 9 | from .forms import ComposeForm 10 | from .models import Thread, ChatMessage 11 | 12 | 13 | class InboxView(LoginRequiredMixin, ListView): 14 | template_name = 'chat/inbox.html' 15 | def get_queryset(self): 16 | return Thread.objects.by_user(self.request.user) 17 | 18 | 19 | class ThreadView(LoginRequiredMixin, FormMixin, DetailView): 20 | template_name = 'chat/thread.html' 21 | form_class = ComposeForm 22 | success_url = './' 23 | 24 | def get_queryset(self): 25 | return Thread.objects.by_user(self.request.user) 26 | 27 | def get_object(self): 28 | other_username = self.kwargs.get("username") 29 | obj, created = Thread.objects.get_or_new(self.request.user, other_username) 30 | if obj == None: 31 | raise Http404 32 | return obj 33 | 34 | def get_context_data(self, **kwargs): 35 | context = super().get_context_data(**kwargs) 36 | context['form'] = self.get_form() 37 | return context 38 | 39 | def post(self, request, *args, **kwargs): 40 | if not request.user.is_authenticated: 41 | return HttpResponseForbidden() 42 | self.object = self.get_object() 43 | form = self.get_form() 44 | if form.is_valid(): 45 | return self.form_valid(form) 46 | else: 47 | return self.form_invalid(form) 48 | 49 | def form_valid(self, form): 50 | thread = self.get_object() 51 | user = self.request.user 52 | message = form.cleaned_data.get("message") 53 | ChatMessage.objects.create(user=user, thread=thread, message=message) 54 | return super().form_valid(form) 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cfehome.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /src/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Chat x Channels Rapidly 8 | {% include 'css.html' %} 9 | 10 | 11 | 12 |
13 |
14 |
15 |

Chat x Channels Rapidly

16 |
17 |
18 | {% block content %} 19 | {% endblock %} 20 |
21 | 22 | 23 | {% include 'js.html' %} 24 | {% block script %} 25 | {% endblock %} 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/templates/css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | 5 |

Welcome Home

6 | {% endblock %} -------------------------------------------------------------------------------- /src/templates/js.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | --------------------------------------------------------------------------------