aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--django_skaschool/urls.py2
-rw-r--r--page/templates/page/program.html20
-rw-r--r--page/urls.py4
-rw-r--r--schedule/migrations/0001_initial.py76
-rw-r--r--schedule/migrations/__init__.py0
-rw-r--r--schedule/models.py4
-rw-r--r--schedule/static/css/schedule.css41
-rw-r--r--schedule/static/js/schedule.js58
-rw-r--r--schedule/templates/schedule/schedule.html93
-rw-r--r--schedule/urls.py18
-rw-r--r--schedule/views.py51
-rw-r--r--templates/navbar.html2
-rw-r--r--tools/templatetags/dict_utils.py19
13 files changed, 362 insertions, 26 deletions
diff --git a/django_skaschool/urls.py b/django_skaschool/urls.py
index 60a298a..7b9ac2f 100644
--- a/django_skaschool/urls.py
+++ b/django_skaschool/urls.py
@@ -20,6 +20,8 @@ urlpatterns = patterns('',
url(r'^$', IndexView.as_view(), name='index'),
# app 'page'
url(r'^page/', include('page.urls')),
+ # app 'schedule'
+ url(r'^schedule/', include('schedule.urls')),
# app 'notice'
url(r'^notice/', include('notice.urls')),
# app 'archive'
diff --git a/page/templates/page/program.html b/page/templates/page/program.html
deleted file mode 100644
index c0b68d7..0000000
--- a/page/templates/page/program.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends 'base.html' %}
-{% load staticfiles %}
-{% load url from future %}
-{% load bootstrap3 %}
-
-{# program template #}
-
-{% block pagetitle %}日程{% endblock %}
-
-{% block content %}
- <div class="container">
- <h2>2014 SKA 暑期学校日程安排</h2>
- <br>
- <div class="alert alert-info">
- 日程安排尚未确定,请等待后续通知和更新……
- </div>
- </div>
-{% endblock %}
-
-{# vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #}
diff --git a/page/urls.py b/page/urls.py
index 79ae696..0f72034 100644
--- a/page/urls.py
+++ b/page/urls.py
@@ -14,10 +14,6 @@ urlpatterns = patterns('',
url(r'^introduction/$',
TemplateView.as_view(template_name='page/introduction.html'),
name='introduction'),
- # program page
- url(r'^program/$',
- TemplateView.as_view(template_name='page/program.html'),
- name='program'),
# committee page
url(r'^committee/$',
TemplateView.as_view(template_name='page/committee.html'),
diff --git a/schedule/migrations/0001_initial.py b/schedule/migrations/0001_initial.py
new file mode 100644
index 0000000..3e14bd9
--- /dev/null
+++ b/schedule/migrations/0001_initial.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'Event'
+ db.create_table(u'schedule_event', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=100)),
+ ('person', self.gf('django.db.models.fields.CharField')(max_length=50)),
+ ('date', self.gf('django.db.models.fields.DateField')()),
+ ('time_start', self.gf('django.db.models.fields.TimeField')()),
+ ('time_end', self.gf('django.db.models.fields.TimeField')()),
+ ('contents', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('updated_at', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, auto_now_add=True, blank=True)),
+ ))
+ db.send_create_signal(u'schedule', ['Event'])
+
+ # Adding model 'EventAttachment'
+ db.create_table(u'schedule_eventattachment', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=100)),
+ ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('attachment', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+ ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
+ ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ('updated_at', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, auto_now_add=True, blank=True)),
+ ))
+ db.send_create_signal(u'schedule', ['EventAttachment'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'Event'
+ db.delete_table(u'schedule_event')
+
+ # Deleting model 'EventAttachment'
+ db.delete_table(u'schedule_eventattachment')
+
+
+ models = {
+ u'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ u'schedule.event': {
+ 'Meta': {'ordering': "['date', 'time_start']", 'object_name': 'Event'},
+ 'contents': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'date': ('django.db.models.fields.DateField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'time_end': ('django.db.models.fields.TimeField', [], {}),
+ 'time_start': ('django.db.models.fields.TimeField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'})
+ },
+ u'schedule.eventattachment': {
+ 'Meta': {'object_name': 'EventAttachment'},
+ 'attachment': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+ 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['schedule'] \ No newline at end of file
diff --git a/schedule/migrations/__init__.py b/schedule/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/schedule/migrations/__init__.py
diff --git a/schedule/models.py b/schedule/models.py
index 37ed3f4..aee7018 100644
--- a/schedule/models.py
+++ b/schedule/models.py
@@ -24,6 +24,8 @@ class Event(models.Model):
time_end = models.TimeField(_("End time"))
contents = models.TextField(_("Contents"), blank=True,
help_text=_("Markdown syntax supported"))
+ updated_at = models.DateTimeField(_("Updated time"),
+ auto_now=True, auto_now_add=True)
attachments = generic.GenericRelation('EventAttachment')
class Meta:
@@ -49,6 +51,8 @@ class EventAttachment(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey("content_type", "object_id")
+ updated_at = models.DateTimeField(_("Updated time"),
+ auto_now=True, auto_now_add=True)
class Meta:
verbose_name = _('Event attachment')
diff --git a/schedule/static/css/schedule.css b/schedule/static/css/schedule.css
new file mode 100644
index 0000000..82cdccd
--- /dev/null
+++ b/schedule/static/css/schedule.css
@@ -0,0 +1,41 @@
+/*
+ * schedule.css
+ * used with 'schedule.html' page
+ *
+ * 2014/06/24
+ */
+
+/* bootstrap panel */
+.panel .panel-heading {
+ font-size: 180%;
+ font-weight: bold;
+}
+
+/* event-contents */
+.event-contents p {
+ margin: 0px 0px 5px;
+}
+.event-contents .panel-heading {
+ font-size: 100%;
+ font-weight: normal;
+ padding: 5px 5px;
+ cursor: pointer;
+}
+.event-contents .panel-body {
+ padding: 5px 5px;
+}
+
+/* event-attachments */
+.event-attachments p {
+ margin: 0px 0px 5px;
+}
+.event-attachments .panel-heading {
+ font-size: 100%;
+ font-weight: normal;
+ padding: 5px 5px;
+ cursor: pointer;
+}
+.event-attachments .panel-body {
+ padding: 5px 5px;
+}
+
diff --git a/schedule/static/js/schedule.js b/schedule/static/js/schedule.js
new file mode 100644
index 0000000..3714b19
--- /dev/null
+++ b/schedule/static/js/schedule.js
@@ -0,0 +1,58 @@
+/*
+ * schedule.js
+ * used with 'schedule.html' page
+ *
+ * 2014/06/24
+ */
+
+/* animated slidedown and slideup div */
+$(document).ready(function() {
+ /* hide() all .event-contents and .event-attchments body */
+ $(".event-contents .panel-body").hide().removeClass('shown').addClass('hidden');
+ $(".event-attachments .panel-body").hide().removeClass('shown').addClass('hidden');
+
+ /* click .event-contents heading to toggle its body contents */
+ $(".event-contents .panel-heading").on("click", function(e) {
+ e.stopPropagation();
+ ec_body = $(this).siblings(".panel-body");
+ if ($(ec_body).hasClass('hidden')) {
+ $(ec_body).show().removeClass('hidden').addClass('shown');
+ // update heading icon and text
+ span_icon = $(this).children("span.glyphicon");
+ $(span_icon).removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
+ span_action = $(this).children("span.action");
+ $(span_action).html('隐藏');
+ }
+ else {
+ $(ec_body).hide().removeClass('shown').addClass('hidden');
+ // update heading icon and text
+ span_icon = $(this).children("span.glyphicon");
+ $(span_icon).removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
+ span_action = $(this).children("span.action");
+ $(span_action).html('显示');
+ }
+ });
+
+ /* click .event-attachments heading to toggle its body attachments */
+ $(".event-attachments .panel-heading").on("click", function(e) {
+ e.stopPropagation();
+ ec_body = $(this).siblings(".panel-body");
+ if ($(ec_body).hasClass('hidden')) {
+ $(ec_body).show().removeClass('hidden').addClass('shown');
+ // update heading icon and text
+ span_icon = $(this).children("span.glyphicon");
+ $(span_icon).removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
+ span_action = $(this).children("span.action");
+ $(span_action).html('隐藏');
+ }
+ else {
+ $(ec_body).hide().removeClass('shown').addClass('hidden');
+ // update heading icon and text
+ span_icon = $(this).children("span.glyphicon");
+ $(span_icon).removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
+ span_action = $(this).children("span.action");
+ $(span_action).html('显示');
+ }
+ });
+});
+
diff --git a/schedule/templates/schedule/schedule.html b/schedule/templates/schedule/schedule.html
new file mode 100644
index 0000000..ee9b9c4
--- /dev/null
+++ b/schedule/templates/schedule/schedule.html
@@ -0,0 +1,93 @@
+{% extends 'base.html' %}
+{% load staticfiles %}
+{% load url from future %}
+{% load bootstrap3 %}
+{% load dict_utils %}
+{% load my_markdown %}
+
+{# program template #}
+
+{% block pagetitle %}日程{% endblock %}
+
+{% block css_extra %}
+ <link href="{% static 'css/schedule.css' %}" rel="stylesheet">
+{% endblock %}
+
+{% block content %}
+ <div class="container">
+ <h2>2014 SKA 暑期学校日程安排</h2>
+ <br>
+ <div class="alert alert-info">
+ <strong>会场地址</strong>:学术活动中心(上海交通大学闵行校区)
+ </div>
+ {% if event_empty %}
+ <div class="alert alert-warning">
+ 日程安排尚未确定,请等待我们的后续通知……
+ </div>
+ {% endif %}
+
+ {% for day in day_all %}
+ <div class="panel panel-info">
+ <div class="panel-heading">
+ {{ day }}
+ </div>
+ <div class="panel-body">
+ {% with event_day=event_dict|dictkey:day %}
+ {% for event in event_day %}
+ <div class="row event">
+ <div class="col-md-2 event-time">
+ <p><strong>{{ event.time_start|time:'H:i' }} &ndash; {{ event.time_end|time:'H:i' }}</strong></p>
+ </div> <!-- event-time -->
+ <div class="col-md-10 event-body">
+ <p>
+ <strong>{{ event.title }}</strong><br>
+ <em>{{ event.person }}</em>
+ </p>
+ {% if event.contents %}
+ <div class="panel panel-default event-contents">
+ <div class="panel-heading">
+ <span class="glyphicon glyphicon-chevron-down"></span>
+ <span class="action">显示</span>详情
+ </div>
+ <div class="panel-body">
+ {{ event.contents|my_markdown }}
+ </div>
+ </div>
+ {% endif %}
+ {% if event.attachments.all %}
+ <div class="panel panel-default event-attachments">
+ <div class="panel-heading">
+ <span class="glyphicon glyphicon-chevron-down"></span>
+ <span class="action">显示</span>附件
+ </div>
+ <div class="panel-body">
+ {% for attachment in event.attachments.all %}
+ <p><a href="{{ attachment.attachment.url }}">{{ attachment.title }}</a> &ndash; {{ attachment.description }}</p>
+ {% endfor %} {# attachment #}
+ </div> <!-- panel-body -->
+ </div> <!-- event-attachments -->
+ {% endif %} {# attachments #}
+ </div> <!-- event-body -->
+ </div> <!-- event -->
+ {% endfor %} {# event #}
+ {% endwith %} {# event_day #}
+ </div> <!-- panel-body -->
+ </div> <!-- panel -->
+ {% endfor %} {# day #}
+
+ <br>
+ <p>注:<br>
+ 1. 我们会根据实际情况及时更新日程安排。<br>
+ 2. 上课讲义及其他相关资料会及时上传和更新。
+ </p>
+ <br>
+ <p>Last updated: {{ updated_at }}</p>
+ <br>
+ </div>
+{% endblock %}
+
+{% block js_extra %}
+ <script src="{% static 'js/schedule.js' %}" type="text/javascript"></script>
+{% endblock %}
+
+{# vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #}
diff --git a/schedule/urls.py b/schedule/urls.py
new file mode 100644
index 0000000..b7bd185
--- /dev/null
+++ b/schedule/urls.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+#
+# urls.py for app 'page'
+#
+
+from django.conf.urls import patterns, include, url
+from django.views.generic.base import TemplateView
+
+from schedule.views import ScheduleView
+
+
+urlpatterns = patterns('',
+ # program page
+ url(r'^all/$',
+ ScheduleView.as_view(),
+ name='schedule_all'),
+)
+
diff --git a/schedule/views.py b/schedule/views.py
index 91ea44a..ad4bbc2 100644
--- a/schedule/views.py
+++ b/schedule/views.py
@@ -1,3 +1,52 @@
+# -*- coding: utf-8 -*-
+
from django.shortcuts import render
+from django.views.generic.base import TemplateView
+
+from schedule.models import Event, EventAttachment
+
+import re
+import datetime
+
+
+class ScheduleView(TemplateView):
+ """
+ class-based view to show events with their attachments
+ generate the schedule page
+ """
+ template_name = 'schedule/schedule.html'
+
+ def get_context_data(self, **kwargs):
+ context = super(type(self), self).get_context_data(**kwargs)
+ event_all_qs = Event.objects.all().order_by('date')
+ if event_all_qs:
+ event_empty = False
+ # get all the dates
+ day_all = ['%s'%e.date.isoformat() for e in event_all_qs]
+ # deduplicate while preserver order
+ tmplist = []
+ for e in day_all:
+ if e not in tmplist:
+ tmplist.append(e)
+ day_all[:] = tmplist
+ # make a dict to group events by date
+ event_dict = {}
+ for d in day_all:
+ day = datetime.date(*map(int, re.split('[^\d]', d)))
+ event_day = event_all_qs.filter(date=day).order_by('time_start')
+ event_dict[d] = event_day
+ # get time of last update
+ event_last_updated = event_all_qs.order_by('-updated_at')[0]
+ updated_at = event_last_updated.updated_at.isoformat()
+ else:
+ event_empty = True
+ event_dict = {}
+ day_all = []
+ updated_at = 'N/A'
+ #
+ context['event_empty'] = event_empty
+ context['event_dict'] = event_dict
+ context['day_all'] = day_all
+ context['updated_at'] = updated_at
+ return context
-# Create your views here.
diff --git a/templates/navbar.html b/templates/navbar.html
index fe0fffc..9546f71 100644
--- a/templates/navbar.html
+++ b/templates/navbar.html
@@ -18,7 +18,7 @@
<ul class="nav navbar-nav">
<li><a href="{% url 'introduction' %}">介绍</a></li>
<li><a href="{% url 'list_notice' %}">通知</a></li>
- <li><a href="{% url 'program' %}">日程</a></li>
+ <li><a href="{% url 'schedule_all' %}">日程</a></li>
<li><a href="{% url 'committee' %}">组委会</a></li>
<li><a href="{% url 'sponsor' %}">赞助方</a></li>
<li><a href="{% url 'archive_all' %}">资料下载</a></li>
diff --git a/tools/templatetags/dict_utils.py b/tools/templatetags/dict_utils.py
new file mode 100644
index 0000000..5a81c79
--- /dev/null
+++ b/tools/templatetags/dict_utils.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+#
+# utilities to deal with dictionary in template
+#
+
+from django import template
+
+
+register = template.Library()
+
+@register.filter
+def dictkey(d, key):
+ try:
+ value = d[key]
+ except KeyError:
+ from django.conf import settings
+ value = settings.TEMPLATE_STRING_IF_INVALID
+ return value
+