Skip to content

Commit

Permalink
WIP 4
Browse files Browse the repository at this point in the history
  • Loading branch information
vCra committed Mar 4, 2019
1 parent 37547f9 commit cb29eae
Show file tree
Hide file tree
Showing 20 changed files with 258 additions and 28 deletions.
1 change: 1 addition & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,4 @@

MOOSHAK_ENDPOINT = "http://localhost:8081/mooshak/api"

OPEN_URLS = ["/admin/login/"]
2 changes: 1 addition & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var pathsConfig = function (appName) {
coreuiSass: vendorsRoot + '/@coreui/coreui/scss',
font_awesomeSass: vendorsRoot + '/font-awesome/scss',
vendorsJs: [
vendorsRoot + 'jquery/dist/jquery.slim.js',
vendorsRoot + 'jquery/dist/jquery.js',
vendorsRoot + 'popper.js/dist/umd/popper.js',
vendorsRoot + 'bootstrap/dist/js/bootstrap.js',
],
Expand Down
1 change: 1 addition & 0 deletions local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ services:
- local_postgres_data_backups:/backups
env_file:
- ./.envs/.local/.postgres

17 changes: 14 additions & 3 deletions programdom/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer

from programdom.models import WorkshopSession


class StudentWaitingConsumer(WebsocketConsumer):

Expand Down Expand Up @@ -42,15 +44,16 @@ def problem_ready(self, event):

class WorkshopControlConsumer(WebsocketConsumer):

workshop_code = None

def connect(self):
"""
Called when a connection is attempted
:return:
"""
session = self.scope["session"]
# TODO: Check if this Account is actually allowed to interact with this workshop
self.workshop_code = self.scope['url_route']['kwargs']['id']

self.workshop_code = session.get("current_workshop_id", None)
if self.workshop_code:
self.accept()
else:
Expand All @@ -68,4 +71,12 @@ def receive(self, text_data=None, bytes_data=None):

def change_problem(self, text_data: dict):
async_to_sync(self.channel_layer.group_send)(f"wait_workshop_{self.workshop_code}", {"type": "problem.ready", "problem": text_data.get("problem_id")})
print("test")

def workshop_toggle(self, text_data: dict):
workshop_id = text_data.get("workshop_id")
workshop = WorkshopSession.objects.get(id=workshop_id)
if text_data.get("workshop_state"):
workshop.start()
else:
workshop.end()
self.send(text_data=json.dumps(text_data))
23 changes: 23 additions & 0 deletions programdom/migrations/0002_auto_20190303_1919.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 2.1.7 on 2019-03-03 19:19

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('programdom', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='workshopsession',
name='title',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='workshopsession',
name='problems',
field=models.ManyToManyField(blank=True, to='programdom.Problem'),
),
]
23 changes: 23 additions & 0 deletions programdom/migrations/0003_auto_20190303_2032.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 2.1.7 on 2019-03-03 20:32

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('programdom', '0002_auto_20190303_1919'),
]

operations = [
migrations.AddField(
model_name='problem',
name='skeleton',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='problem',
name='title',
field=models.CharField(blank=True, max_length=255, null=True),
),
]
21 changes: 21 additions & 0 deletions programdom/migrations/0004_workshopsession_owner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 2.1.7 on 2019-03-03 21:15

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('programdom', '0003_auto_20190303_2032'),
]

operations = [
migrations.AddField(
model_name='workshopsession',
name='owner',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]
26 changes: 19 additions & 7 deletions programdom/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import random
import string

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.postgres.fields import JSONField
Expand All @@ -15,36 +17,46 @@ class Problem(models.Model):
A single problem
"""
mooshak_id = models.CharField(max_length=255)
title = models.CharField(max_length=255, blank=True, null=True)
skeleton = models.TextField(blank=True, null=True)

def __str__(self):
return self.title

def set_active(self, workshop_code):
"""
Sets the current problem as active for the current workshop. Additionally, it will broadcast the change to all clients.
:param workshop_code:
:return:
"""

class WorkshopSession(models.Model):
"""
A workshop is a single session
"""
code = models.CharField(max_length=8, null=True)
problems = models.ManyToManyField(Problem, blank=True)
title = models.CharField(max_length=255, blank=True, null=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True)

@property
def active(self):
return self.code is not None

def end(self):
"""
"""s
Ends the session, by removing the session code
"""
self.code = None
self.save()

def start(self):
"""
Sets the session as running, by setting a code
"""
self.code = "".join(random.choices(string.ascii_uppercase + string.digits, k=8))
self.save()

def get_absolute_url(self):
return reverse('workshop_detail', kwargs={'pk': self.id})

def __str__(self):
return self.title


class Submission(models.Model):
"""
Expand Down
1 change: 1 addition & 0 deletions programdom/problems/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
urlpatterns = [
path("", View.as_view(), name="problem_base"),
path("<int:pk>/", ProblemStudentView.as_view(), name="problem_student"),
path("new/", ProblemStudentView.as_view(), name="problem_new"),
]
2 changes: 1 addition & 1 deletion programdom/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"""
websocket_urlpatterns = [
path("waiting/", StudentWaitingConsumer),
path("manage_workshop/", WorkshopControlConsumer)
path("manage_workshop/<int:id>/", WorkshopControlConsumer)
]
12 changes: 10 additions & 2 deletions programdom/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,16 @@
<ul class="nav navbar-nav d-md-down-none">
<li class="nav-item px-3">
<a class="nav-link" href="#">Home</a>
<li class="nav-item px-3">
</i><a class="nav-link" href="#">{{ user }}</a>
<li class="nav-item dropdown px-3">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ user }}
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{% url 'users:logout' %}">Logout</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
</ul>

Expand Down
48 changes: 46 additions & 2 deletions programdom/templates/programdom/workshop/detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,49 @@
{% endblock breadcrumbs %}

{% block content %}
<h2>{{ object }}</h2>
{% endblock content %}
<div class="row">
<div class="col">
<h3>{{ object}} <span class="badge badge-dark ">In Progress</span></h3>
</div>
<div class="col">
<div class="btn-group float-right" role="group" aria-label="Workshop Buttons">
<a class="btn btn-primary" href="{% url 'problem_new' %}">Edit Problems</a>
<a class="btn btn-primary" href="{% url 'workshop_present' object.id %}">Present</a>
</div>
</div>
</div>
<div class="row">
<div class="nav flex-column nav-pills p-2" id="v-pills-tab" role="tablist" aria-orientation="vertical">
{% for item in object.problems.all %}
<a class="nav-link {% if forloop.first %}active{% endif %}" id="v-pills-{{ item.id }}-tab" data-toggle="pill" href="#v-pills-{{ item.id }}" role="tab" aria-controls="v-pills-{{ item.id }}" aria-selected="{% if forloop.first %}true{% else %}false{% endif %}">{{ item }}</a>
{% endfor %}
</div>
<div class="tab-content flex-grow-1 p-1" id="v-pills-tabContent">
{% for item in object.problems.all %}
<div class="tab-pane fade {% if forloop.first %}show active{% endif %} " id="v-pills-{{ item.id }}" role="tabpanel" aria-labelledby="v-pills-{{ item.id }}-tab">
<div class="row">
<div class="col">
<h4>{{ item.title }}</h4>
</div>
<div class="col">
<div class="btn-group float-right " role="group" aria-label="problem_buttons">
<button type="button" class="btn btn-primary btn-sm">Edit Problem
</button>
<button type="button" class="btn btn-primary btn-sm">Remove Problem
</button>
</div>
</div>
</div>
<div class="row">
<code>
<pre>{{ item.skeleton | linenumbers | linebreaks }}</pre>
</code>
</div>
</div>
{% endfor %}
</div>
</div>
<footer>
{% if object.code %}<p class="small">This workshop currently has code {{ object.code }}</p>{% endif %}
</footer>
{% endblock content %}
75 changes: 75 additions & 0 deletions programdom/templates/programdom/workshop/present.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{% extends "base.html" %}
{% block breadcrumbs %}
<li class="breadcrumb-item"><a href="{% url 'home' %}">Home</a></li>
<li class="breadcrumb-item"><a href="{% url 'workshop_list' %}">Workshops</a></li>
<li class="breadcrumb-item "><a href="{% url 'workshop_detail' object.id %}">{{ object }}</a></li>
<li class="breadcrumb-item active">Present</li>
{% endblock breadcrumbs %}
{% block content %}
<div class="row">
<div class="col">
<h3>{{ object}} <span class="badge badge-dark {% if not object.active %}d-none{% endif %}" id="badge_in_progress">In Progress</span></h3>
</div>
<div class="col">
<div class="btn-group float-right" role="group" aria-label="Workshop Buttons">
{% if not object.active %}
<input type="hidden" id="input_toggle_value" value="true">
<button class="btn btn-success" id="btn_workshop_toggle">Begin Workshop</button>
{% else %}
<input type="hidden" id="input_toggle_value" value="false">
<button class="btn btn-danger" id="btn_workshop_toggle">End Workshop</button>
{% endif %}
</div>
</div>
</div>
<div class="row">
</div>
{% endblock %}
{% block javascript %}
{{ block.super }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js">
</script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet">

<script>
var chatSocket = new WebSocket(
'ws://' + window.location.host +
'/ws/manage_workshop/'+ {{ object.id }} + '/');

chatSocket.onmessage = function(e) {
var data = JSON.parse(e.data)
var action = data["action"];

if (action === "workshop_toggle"){
if (data.workshop_state) {
toastr.info("This workshop has now started");
$("#btn_workshop_toggle").removeClass("btn-success").addClass("btn-danger").text("End Workshop");
$("#input_toggle_value").val("false");
$("#badge_in_progress").removeClass("d-none")
} else {
toastr.info("This workshop has now ended");
$("#btn_workshop_toggle").removeClass("btn-danger").addClass("btn-success").text("Begin Workshop");
$("#input_toggle_value").val("true")
$("#badge_in_progress").addClass("d-none")
}
}
};

chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};


$("#btn_workshop_toggle").click(function(){
chatSocket.send(JSON.stringify({
"action": "workshop_toggle",
"workshop_id": {{ object.id }},
"workshop_state": $("#input_toggle_value").val() === 'true'
}))

})



</script>
{% endblock %}
8 changes: 4 additions & 4 deletions programdom/templates/programdom/workshop/waiting.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
<div class="card">
<div class="card-body">
<h4 class="card-title">Please Wait</h4>
<p class="card-text">They are currently no problems in progress. This page will update when this changes</p>

<p class="card-text">
They are currently no problems in progress. This page will update when this changes
</p>
<div class="progress">
<div class="progress-bar progress-bar-animated progress-bar-striped" style="width:100%"></div>
</div>
Expand Down Expand Up @@ -36,6 +37,5 @@ <h4 class="card-title">Please Wait</h4>
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};

</script>
{% endblock javascript %}
{% endblock javascript %}
2 changes: 2 additions & 0 deletions programdom/templates/programdom/workshop/workshop_code.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ <h1>Login</h1>
<div>
<h2>Welcome to Programdom</h2>
<p>Programdom is a fun tool to automatically check your code during workshops</p>
<a class="" href="{% url "admin:login"%}">Admin Login</a>

</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion programdom/workshops/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __init__(self, *args, **kwargs):

class Meta:
model = WorkshopSession
fields = []
fields = ['title']


class WorkshopSessionEntryForm(forms.Form):
Expand Down
Loading

0 comments on commit cb29eae

Please sign in to comment.