add chat box
This commit is contained in:
parent
15b0e06a4f
commit
af61e8a8e8
15 changed files with 175 additions and 6 deletions
0
chat_box/__init__.py
Normal file
0
chat_box/__init__.py
Normal file
3
chat_box/admin.py
Normal file
3
chat_box/admin.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
5
chat_box/apps.py
Normal file
5
chat_box/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ChatBoxConfig(AppConfig):
|
||||||
|
name = 'chat_box'
|
47
chat_box/consumers.py
Normal file
47
chat_box/consumers.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from channels.generic.websocket import AsyncWebsocketConsumer
|
||||||
|
|
||||||
|
|
||||||
|
class ChatConsumer(AsyncWebsocketConsumer):
|
||||||
|
async def connect(self):
|
||||||
|
self.room_name = 'common'
|
||||||
|
self.room_group_name = 'chat_%s' % self.room_name
|
||||||
|
|
||||||
|
# Join room group
|
||||||
|
await self.channel_layer.group_add(
|
||||||
|
self.room_group_name,
|
||||||
|
self.channel_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.accept()
|
||||||
|
|
||||||
|
async def disconnect(self, close_code):
|
||||||
|
# Leave room group
|
||||||
|
await self.channel_layer.group_discard(
|
||||||
|
self.room_group_name,
|
||||||
|
self.channel_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Receive message from WebSocket
|
||||||
|
async def receive(self, text_data):
|
||||||
|
text_data_json = json.loads(text_data)
|
||||||
|
message = text_data_json['message']
|
||||||
|
|
||||||
|
# Send message to room group
|
||||||
|
await self.channel_layer.group_send(
|
||||||
|
self.room_group_name,
|
||||||
|
{
|
||||||
|
'type': 'chat_message',
|
||||||
|
'message': message,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Receive message from room group
|
||||||
|
async def chat_message(self, event):
|
||||||
|
message = event['message']
|
||||||
|
|
||||||
|
# Send message to WebSocket
|
||||||
|
await self.send(text_data=json.dumps({
|
||||||
|
'message': message,
|
||||||
|
}))
|
0
chat_box/migrations/__init__.py
Normal file
0
chat_box/migrations/__init__.py
Normal file
3
chat_box/models.py
Normal file
3
chat_box/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
7
chat_box/routing.py
Normal file
7
chat_box/routing.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from django.urls import re_path
|
||||||
|
|
||||||
|
from . import consumers
|
||||||
|
|
||||||
|
websocket_urlpatterns = [
|
||||||
|
re_path(r'ws/chat/', consumers.ChatConsumer),
|
||||||
|
]
|
3
chat_box/tests.py
Normal file
3
chat_box/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
8
chat_box/views.py
Normal file
8
chat_box/views.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
|
||||||
|
def chat(request):
|
||||||
|
return render(request, 'chat/chat.html', {
|
||||||
|
'title': _('Chat Box'),
|
||||||
|
})
|
13
dmoj/routing.py
Normal file
13
dmoj/routing.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import chat_box.routing
|
||||||
|
from channels.auth import AuthMiddlewareStack
|
||||||
|
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||||
|
|
||||||
|
|
||||||
|
application = ProtocolTypeRouter({
|
||||||
|
# (http->django views is added by default)
|
||||||
|
'websocket': AuthMiddlewareStack(
|
||||||
|
URLRouter(
|
||||||
|
chat_box.routing.websocket_urlpatterns,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
})
|
|
@ -236,6 +236,8 @@ INSTALLED_APPS += (
|
||||||
'statici18n',
|
'statici18n',
|
||||||
'impersonate',
|
'impersonate',
|
||||||
'django_jinja',
|
'django_jinja',
|
||||||
|
'chat_box',
|
||||||
|
'channels',
|
||||||
)
|
)
|
||||||
|
|
||||||
MIDDLEWARE = (
|
MIDDLEWARE = (
|
||||||
|
@ -500,3 +502,13 @@ TESTCASE_VISIBLE_LENGTH = 60
|
||||||
|
|
||||||
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10240
|
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10240
|
||||||
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440
|
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440
|
||||||
|
|
||||||
|
ASGI_APPLICATION = 'dmoj.routing.application'
|
||||||
|
CHANNEL_LAYERS = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
||||||
|
'CONFIG': {
|
||||||
|
"hosts": [('0.0.0.0', 6379)],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
15
dmoj/urls.py
15
dmoj/urls.py
|
@ -1,3 +1,4 @@
|
||||||
|
from chat_box.views import chat
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
@ -14,10 +15,9 @@ from judge.feed import AtomBlogFeed, AtomCommentFeed, AtomProblemFeed, BlogFeed,
|
||||||
from judge.forms import CustomAuthenticationForm
|
from judge.forms import CustomAuthenticationForm
|
||||||
from judge.sitemap import BlogPostSitemap, ContestSitemap, HomePageSitemap, OrganizationSitemap, ProblemSitemap, \
|
from judge.sitemap import BlogPostSitemap, ContestSitemap, HomePageSitemap, OrganizationSitemap, ProblemSitemap, \
|
||||||
SolutionSitemap, UrlSitemap, UserSitemap
|
SolutionSitemap, UrlSitemap, UserSitemap
|
||||||
from judge.views import TitledTemplateView, api, blog, comment, contests, language, license, mailgun, organization, \
|
from judge.views import TitledTemplateView, about, api, blog, comment, contests, language, license, mailgun, \
|
||||||
preview, problem, problem_manage, ranked_submission, register, stats, status, submission, tasks, ticket, totp, \
|
organization, preview, problem, problem_manage, ranked_submission, register, stats, status, submission, tasks, \
|
||||||
user, widgets
|
ticket, totp, user, widgets
|
||||||
from judge.views.about import about, custom_checker_sample
|
|
||||||
from judge.views.problem_data import ProblemDataView, ProblemSubmissionDiff, \
|
from judge.views.problem_data import ProblemDataView, ProblemSubmissionDiff, \
|
||||||
problem_data_file, problem_init_view
|
problem_data_file, problem_init_view
|
||||||
from judge.views.register import ActivationView, RegistrationView
|
from judge.views.register import ActivationView, RegistrationView
|
||||||
|
@ -25,6 +25,7 @@ from judge.views.select2 import AssigneeSelect2View, CommentSelect2View, Contest
|
||||||
ContestUserSearchSelect2View, OrganizationSelect2View, ProblemSelect2View, TicketUserSelect2View, \
|
ContestUserSearchSelect2View, OrganizationSelect2View, ProblemSelect2View, TicketUserSelect2View, \
|
||||||
UserSearchSelect2View, UserSelect2View
|
UserSearchSelect2View, UserSelect2View
|
||||||
|
|
||||||
|
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
register_patterns = [
|
register_patterns = [
|
||||||
|
@ -362,9 +363,11 @@ urlpatterns = [
|
||||||
url(r'^progress$', tasks.demo_progress),
|
url(r'^progress$', tasks.demo_progress),
|
||||||
])),
|
])),
|
||||||
|
|
||||||
url(r'^about/', about, name='about'),
|
url(r'^about/', about.about, name='about'),
|
||||||
|
|
||||||
url(r'^custom_checker_sample', custom_checker_sample, name='custom_checker_sample'),
|
url(r'^custom_checker_sample/', about.custom_checker_sample, name='custom_checker_sample'),
|
||||||
|
|
||||||
|
url(r'chat/', chat, name='chat'),
|
||||||
]
|
]
|
||||||
|
|
||||||
favicon_paths = ['apple-touch-icon-180x180.png', 'apple-touch-icon-114x114.png', 'android-chrome-72x72.png',
|
favicon_paths = ['apple-touch-icon-180x180.png', 'apple-touch-icon-114x114.png', 'android-chrome-72x72.png',
|
||||||
|
|
21
judge/migrations/0100_auto_20200127_0059.py
Normal file
21
judge/migrations/0100_auto_20200127_0059.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Django 2.2.9 on 2020-01-27 00:59
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import judge.models.problem_data
|
||||||
|
import judge.utils.problem_data
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('judge', '0099_custom_checker'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='problemdata',
|
||||||
|
name='custom_checker',
|
||||||
|
field=models.FileField(blank=True, null=True, storage=judge.utils.problem_data.ProblemDataStorage(), upload_to=judge.models.problem_data.problem_directory_file, validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['py'])], verbose_name='custom checker file'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -30,3 +30,7 @@ packaging
|
||||||
celery
|
celery
|
||||||
-e git://github.com/DMOJ/ansi2html.git#egg=ansi2html
|
-e git://github.com/DMOJ/ansi2html.git#egg=ansi2html
|
||||||
sqlparse
|
sqlparse
|
||||||
|
channels
|
||||||
|
channels-redis
|
||||||
|
docker
|
||||||
|
|
||||||
|
|
40
templates/chat/chat.html
Normal file
40
templates/chat/chat.html
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block js_media %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
var chatSocket = new WebSocket(
|
||||||
|
'ws://' + window.location.host +
|
||||||
|
'/ws/chat/');
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
chatSocket.onmessage = function(e) {
|
||||||
|
var data = JSON.parse(e.data);
|
||||||
|
var message = data['message'];
|
||||||
|
$('#chat-log').append(message + '\n');
|
||||||
|
};
|
||||||
|
chatSocket.onclose = function(e) {
|
||||||
|
console.error('Chat socket closed unexpectedly');
|
||||||
|
};
|
||||||
|
$('#chat-message-input').focus();
|
||||||
|
$('#chat-message-input').keyup(function(e) {
|
||||||
|
if (e.keyCode === 13) { // enter, return
|
||||||
|
$('#chat-message-submit').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#chat-message-submit").click(function() {
|
||||||
|
var message = "{{ request.user }}: " + $('input#chat-message-input').val();
|
||||||
|
chatSocket.send(JSON.stringify({
|
||||||
|
'message': message,
|
||||||
|
}));
|
||||||
|
$('input#chat-message-input').val('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock js_media %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<textarea id="chat-log" cols="100" rows="20"></textarea><br/>
|
||||||
|
<input id="chat-message-input" type="text" size="100"/><br/>
|
||||||
|
<button id="chat-message-submit"> Send </button>
|
||||||
|
{% endblock body %}
|
Loading…
Reference in a new issue