Skip to content

Commit

Permalink
Unidirectional feedback (#197)
Browse files Browse the repository at this point in the history
* UnidirectionalFeedback database and frontend impl

* Add FEEDBACK_ENDPOINT & FEEDBACK_KEY

* Only allows feedbacking when users could submit flags

* Add feedback <a> at hub

* Add "custom" logger

* Use custom logger in ChallengeFeedbackURLView

* Update feedback wording

* Update by review suggestions

* Allow setting feedback content without code change
  • Loading branch information
taoky authored Oct 26, 2024
1 parent c9e2f09 commit aa3a873
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 4 deletions.
2 changes: 2 additions & 0 deletions conf/local_settings.py.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ EXTERNAL_LOGINS = {
'use_smtp': False,
}
}
FEEDBACK_ENDPOINT = 'https://example.com/bot/' # or None
FEEDBACK_KEY = '1145141919811'
14 changes: 14 additions & 0 deletions conf/settings/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
'request': {
'format': '%(asctime)s %(ip)s %(userid)s %(levelname)s %(message)s',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'filters': {
'add_user_info': {
Expand All @@ -39,6 +43,11 @@
'handlers': ['request'],
'propagate': False,
},
'custom': {
'handlers': ['console'],
'level': 'DEBUG',
'propagate': False,
}
},
'handlers': {
'databaselog': {
Expand All @@ -52,6 +61,11 @@
'class': 'logging.StreamHandler',
'formatter': 'request',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple',
}
},
}

Expand Down
14 changes: 14 additions & 0 deletions conf/settings/hackergame.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
'request': {
'format': '%(asctime)s %(ip)s %(userid)s %(levelname)s %(message)s',
},
'verbose': {
'format': '{levelname} {asctime} [{name}] {message}',
'style': '{',
},
},
'filters': {
'add_user_info': {
Expand All @@ -58,6 +62,11 @@
'class': 'logging.StreamHandler',
'formatter': 'request',
},
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
'django': {
Expand All @@ -74,6 +83,11 @@
'handlers': ['request'],
'propagate': False,
},
'custom': {
'handlers': ['console'],
'level': 'INFO',
'propagate': False,
},
},
}

Expand Down
16 changes: 15 additions & 1 deletion conf/settings/hgtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@
},
'request': {
'format': '%(asctime)s %(ip)s %(userid)s %(levelname)s %(message)s',
}
},
'verbose': {
'format': '{levelname} {asctime} [{name}] {message}',
'style': '{',
},
},
'filters': {
'add_user_info': {
Expand All @@ -58,6 +62,11 @@
'class': 'logging.StreamHandler',
'formatter': 'request',
},
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
'django': {
Expand All @@ -74,6 +83,11 @@
'handlers': ['request'],
'propagate': False,
},
'custom': {
'handlers': ['console'],
'level': 'INFO',
'propagate': False,
},
},
}

Expand Down
4 changes: 2 additions & 2 deletions frontend/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from server.terms.models import Terms
from server.trigger.models import Trigger
from server.user.models import User
from .models import Page, Account, Code, AccountLog, SpecialProfileUsedRecord, Qa, Credits
from .models import Page, Account, Code, AccountLog, SpecialProfileUsedRecord, Qa, Credits, UnidirectionalFeedback, Feedback

admin.site.register([Page, Account, Code, Qa, Credits, SpecialProfileUsedRecord])
admin.site.register([Page, Account, Code, Qa, Credits, SpecialProfileUsedRecord, UnidirectionalFeedback, Feedback])


class PermissionListFilter(admin.SimpleListFilter):
Expand Down
50 changes: 50 additions & 0 deletions frontend/migrations/0010_unidirectionalfeedback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 4.2.16 on 2024-10-24 15:52

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),
("frontend", "0009_alter_accountlog_account"),
]

operations = [
migrations.CreateModel(
name="UnidirectionalFeedback",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"challenge_id",
models.IntegerField(help_text="该反馈对应 Challenge 的 ID"),
),
(
"contents",
models.TextField(help_text="反馈内容,最长 1024", max_length=1024),
),
(
"submit_datetime",
models.DateTimeField(auto_now_add=True, help_text="反馈时间"),
),
(
"user",
models.ForeignKey(
help_text="反馈用户",
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]
33 changes: 33 additions & 0 deletions frontend/migrations/0011_feedback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 4.2.16 on 2024-10-25 06:43

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("frontend", "0010_unidirectionalfeedback"),
]

operations = [
migrations.CreateModel(
name="Feedback",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"content",
models.TextField(
blank=True, help_text="会被放入 <code>div</code> 的 HTML"
),
),
],
),
]
21 changes: 21 additions & 0 deletions frontend/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,24 @@ class Credits(models.Model):
@classmethod
def get(cls):
return cls.objects.get_or_create()[0]


class Feedback(models.Model):
content = models.TextField(blank=True, help_text='会被放入 <code>div</code> 的 HTML')

@classmethod
def get(cls):
return cls.objects.get_or_create()[0]


class UnidirectionalFeedback(models.Model):
"""
User could submit feedback for a specific challenge.
"""
challenge_id = models.IntegerField(help_text="该反馈对应 Challenge 的 ID")
user = models.ForeignKey(get_user_model(), models.CASCADE, help_text="反馈用户")
contents = models.TextField(max_length=1024, help_text="反馈内容,最长 1024")
submit_datetime = models.DateTimeField(auto_now_add=True, help_text="反馈时间")

def __str__(self) -> str:
return f"{self.user} 对题目 {self.challenge_id} 的反馈"
58 changes: 58 additions & 0 deletions frontend/templates/challenge_feedback.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{% extends 'base.html' %}
{% load static %}

{% block js %}
{{ block.super }}
<script src="{% static 'vue.min.js' %}"></script>
<script src="{% static 'axios.min.js' %}"></script>
<script>
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
</script>
{% endblock %}

{% block content %}

<div id="app">
<h1>提交对题目 {{challenge_name}} 的反馈</h1>
<div>{{ feedback.content|safe }}</div>
{% verbatim %}
<div v-if="!too_frequent">
{% endverbatim %}
<form method="post" class="pure-form pure-form-stacked">
<fieldset>
{% csrf_token %}
<textarea id="contents" class="pure-input-1" name="contents" rows="20" maxlength="1024" required>{{ contents }}</textarea>
<div class="pure-u-1" style="text-align: right;">
<button type="submit" class="pure-button pure-button-primary">提交!</button>
</div>
</fieldset>
</form>
{% verbatim %}
</div>
<div v-else>
<p>你对该题目上一次提交反馈在 {{ human_latest_submit() }},需要等待提交后一小时方可再次提交。</p>
</div>
</div>
{% endverbatim %}
{{ too_frequent|json_script:'too-frequent' }}
{{ latest_submit|json_script:'latest-submit' }}
<script>
app = new Vue({
el: '#app',
data: {
too_frequent: JSON.parse(document.getElementById('too-frequent').textContent),
latest_submit: JSON.parse(document.getElementById('latest-submit').textContent),
},
methods: {
human_latest_submit() {
if (!this.latest_submit) {
return ""
}
const date = new Date(this.latest_submit)
return date.toLocaleString()
}
}
});
</script>
{% endblock %}
4 changes: 4 additions & 0 deletions frontend/templates/hub.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ <h1>{{ opened.name }}</h1>
<input type="text" name="flag" class="pure-input-rounded" maxlength="200" autocomplete="off" :placeholder="opened.prompt">
<button type="submit" class="pure-button pure-input-rounded">提交</button>
</form>
<a :href="get_feedback_url(opened)">需要提交反馈?</a>
</div>
<div class="main-body" v-else v-html="page_content"></div>
</div>
Expand Down Expand Up @@ -259,6 +260,9 @@ <h1>{{ opened.name }}</h1>
get_subchallenge_count(challenge, flag_index) {
let count = (this.clear_count.flags.find(i => i.challenge === challenge.pk && i.flag === flag_index) || {count: 0}).count;
return count;
},
get_feedback_url(challenge) {
return `/challenge/${opened.pk}/feedback/`
}
},
updated: function () {
Expand Down
1 change: 1 addition & 0 deletions frontend/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
path('error/', views.ErrorView.as_view()),
path('data/core.json', views.CoreDataView.as_view(), name='coredata'),
path('challenge/<int:challenge_id>/', views.ChallengeURLView.as_view(), name='challenge_url'),
path('challenge/<int:challenge_id>/feedback/', views.ChallengeFeedbackURLView.as_view(), name='challenge_feedback_url'),
path('score/', views.ScoreView.as_view(), name='score'),

path('profile/ustc/', views.UstcProfileView.as_view(), name='ustcprofile'),
Expand Down
Loading

0 comments on commit aa3a873

Please sign in to comment.