From 88c4fa2c87138d9f4707ad7193662953f9de725f Mon Sep 17 00:00:00 2001 From: Weitian LI Date: Tue, 24 Jun 2014 23:09:43 +0800 Subject: Added schedule.html template for app 'schedule'. * added 'schedule.views.ScheduleView' * added 'schedule.html' template, with 'schedule.css' and 'schedule.js' * added 'schedule.urls', updated django_skaschool.urls * added tools/templatetags/dict_utils.py * converted schedule models to south-based * removed original 'page/templates/page/program.html', related urls * updated navbar.html --- schedule/migrations/0001_initial.py | 76 +++++++++++++++++++++++++ schedule/migrations/__init__.py | 0 schedule/models.py | 4 ++ schedule/static/css/schedule.css | 41 ++++++++++++++ schedule/static/js/schedule.js | 58 +++++++++++++++++++ schedule/templates/schedule/schedule.html | 93 +++++++++++++++++++++++++++++++ schedule/urls.py | 18 ++++++ schedule/views.py | 51 ++++++++++++++++- 8 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 schedule/migrations/0001_initial.py create mode 100644 schedule/migrations/__init__.py create mode 100644 schedule/static/css/schedule.css create mode 100644 schedule/static/js/schedule.js create mode 100644 schedule/templates/schedule/schedule.html create mode 100644 schedule/urls.py (limited to 'schedule') 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 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 %} + +{% endblock %} + +{% block content %} +
+

2014 SKA 暑期学校日程安排

+
+
+ 会场地址:学术活动中心(上海交通大学闵行校区) +
+ {% if event_empty %} +
+ 日程安排尚未确定,请等待我们的后续通知…… +
+ {% endif %} + + {% for day in day_all %} +
+
+ {{ day }} +
+
+ {% with event_day=event_dict|dictkey:day %} + {% for event in event_day %} +
+
+

{{ event.time_start|time:'H:i' }} – {{ event.time_end|time:'H:i' }}

+
+
+

+ {{ event.title }}
+ {{ event.person }} +

+ {% if event.contents %} +
+
+ + 显示详情 +
+
+ {{ event.contents|my_markdown }} +
+
+ {% endif %} + {% if event.attachments.all %} +
+
+ + 显示附件 +
+
+ {% for attachment in event.attachments.all %} +

{{ attachment.title }} – {{ attachment.description }}

+ {% endfor %} {# attachment #} +
+
+ {% endif %} {# attachments #} +
+
+ {% endfor %} {# event #} + {% endwith %} {# event_day #} +
+
+ {% endfor %} {# day #} + +
+

注:
+ 1. 我们会根据实际情况及时更新日程安排。
+ 2. 上课讲义及其他相关资料会及时上传和更新。 +

+
+

Last updated: {{ updated_at }}

+
+
+{% endblock %} + +{% block js_extra %} + +{% 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. -- cgit v1.2.2