diff --git a/.gitignore b/.gitignore index 0756e40..8c90457 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ db.sqlite3 .vscode/ env/ +static/ +*.pyc diff --git a/cal/forms.py b/cal/forms.py new file mode 100644 index 0000000..7d3991f --- /dev/null +++ b/cal/forms.py @@ -0,0 +1,18 @@ +from django.forms import ModelForm, DateInput +from cal.models import Event + +class EventForm(ModelForm): + class Meta: + model = Event + # datetime-local is a HTML5 input type, format to make date time show on fields + widgets = { + 'start_time': DateInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%dT%H:%M'), + 'end_time': DateInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%dT%H:%M'), + } + fields = '__all__' + + def __init__(self, *args, **kwargs): + super(EventForm, self).__init__(*args, **kwargs) + # input_formats parses HTML5 datetime-local input to datetime field + self.fields['start_time'].input_formats = ('%Y-%m-%dT%H:%M',) + self.fields['end_time'].input_formats = ('%Y-%m-%dT%H:%M',) diff --git a/cal/models.py b/cal/models.py index 8636940..f01fe44 100644 --- a/cal/models.py +++ b/cal/models.py @@ -1,10 +1,13 @@ from django.db import models - -# Create your models here. - +from django.urls import reverse class Event(models.Model): title = models.CharField(max_length=200) description = models.TextField() start_time = models.DateTimeField() end_time = models.DateTimeField() + + @property + def get_html_url(self): + url = reverse('cal:event_edit', args=(self.id,)) + return f' {self.title} ' diff --git a/cal/static/cal/css/styles.css b/cal/static/cal/css/styles.css index 4d0c879..18b76b3 100644 --- a/cal/static/cal/css/styles.css +++ b/cal/static/cal/css/styles.css @@ -1,28 +1,73 @@ -.calendar { +/* Common styles */ +body { + font-family: "ptsans", "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.left { + float: left; +} + +.right { + float: right; +} + +.btn { + outline: none; + color: black; + background-color: transparent; + box-shadow: 0 0 0 0; + margin-right: 3px; +} + +.clearfix { + margin: 15px; +} + +.form { + margin: auto; +} + +.form input, .form select, .form textarea { + border-radius: 5px; + border: 1px solid #17a2b8; + outline: none; + background: none; + padding: 5px; width: 100%; +} + +/* App styles */ +.title { + text-align: center; + margin: 10px; +} + +.calendar { + width: 98%; + margin: auto; font-size: 13px; } -.month { - font-size: 25px; -} - -tr, td { +.calendar tr, .calendar td { border: 1px solid black; } -th { +.calendar th { padding: 10px; text-align: center; font-size: 18px; } -td { +.calendar td { width: 200px; height: 150px; padding: 20px 0px 0px 5px; } +.month { + font-size: 25px; +} + .date { font-size: 16px; } diff --git a/cal/templates/cal/base.html b/cal/templates/cal/base.html index 87bbc73..6efa760 100644 --- a/cal/templates/cal/base.html +++ b/cal/templates/cal/base.html @@ -16,6 +16,9 @@ +

{% block title %}{% endblock %}

+
+ {% block content %} {% endblock %} diff --git a/cal/templates/cal/calendar.html b/cal/templates/cal/calendar.html index 0a104b0..28f574f 100644 --- a/cal/templates/cal/calendar.html +++ b/cal/templates/cal/calendar.html @@ -1,5 +1,15 @@ {% extends 'cal/base.html' %} +{% block title %} +Calendar +{% endblock %} + {% block content %} +
+ Previous Month + Next Month + New Event +
+ {{ calendar }} {% endblock %} \ No newline at end of file diff --git a/cal/templates/cal/event.html b/cal/templates/cal/event.html new file mode 100644 index 0000000..d688151 --- /dev/null +++ b/cal/templates/cal/event.html @@ -0,0 +1,34 @@ +{% extends 'cal/base.html' %} + +{% block title %} +Event +{% endblock %} + +{% block content %} +
+ Calendar +
+ +{% if form.errors %} + {% for field in form %} + {% for error in field.errors %} +
+ {{ field.label }} {{ error|escape }} +
+ {% endfor %} + {% endfor %} + {% for error in form.non_field_errors %} +
+ {{ field.label }} {{ error|escape }} +
+ {% endfor %} +{% endif %} + +
+ {% csrf_token %} + + {{ form }} + +
+
+{% endblock %} \ No newline at end of file diff --git a/cal/urls.py b/cal/urls.py index 9c29758..1a45861 100644 --- a/cal/urls.py +++ b/cal/urls.py @@ -5,4 +5,6 @@ app_name = 'cal' urlpatterns = [ url(r'^index/$', views.index, name='index'), url(r'^calendar/$', views.CalendarView.as_view(), name='calendar'), + url(r'^event/new/$', views.event, name='event_new'), + url(r'^event/edit/(?P\d+)/$', views.event, name='event_edit'), ] diff --git a/cal/utils.py b/cal/utils.py index 3bd1fa6..0a5e4a6 100644 --- a/cal/utils.py +++ b/cal/utils.py @@ -14,7 +14,7 @@ class Calendar(HTMLCalendar): events_per_day = events.filter(start_time__day=day) d = '' for event in events_per_day: - d += f'
  • {event.title}
  • ' + d += f'
  • {event.get_html_url}
  • ' if day != 0: return f"{day}" diff --git a/cal/views.py b/cal/views.py index bc862ca..5250112 100644 --- a/cal/views.py +++ b/cal/views.py @@ -1,11 +1,14 @@ -from datetime import datetime -from django.shortcuts import render -from django.http import HttpResponse +from datetime import datetime, timedelta, date +from django.shortcuts import render, get_object_or_404 +from django.http import HttpResponse, HttpResponseRedirect from django.views import generic +from django.urls import reverse from django.utils.safestring import mark_safe +import calendar from .models import * from .utils import Calendar +from .forms import EventForm def index(request): return HttpResponse('hello') @@ -16,14 +19,42 @@ class CalendarView(generic.ListView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - d = get_date(self.request.GET.get('day', None)) + d = get_date(self.request.GET.get('month', None)) cal = Calendar(d.year, d.month) html_cal = cal.formatmonth(withyear=True) context['calendar'] = mark_safe(html_cal) + context['prev_month'] = prev_month(d) + context['next_month'] = next_month(d) return context -def get_date(req_day): - if req_day: - year, month = (int(x) for x in req_day.split('-')) +def get_date(req_month): + if req_month: + year, month = (int(x) for x in req_month.split('-')) return date(year, month, day=1) - return datetime.today() \ No newline at end of file + return datetime.today() + +def prev_month(d): + first = d.replace(day=1) + prev_month = first - timedelta(days=1) + month = 'month=' + str(prev_month.year) + '-' + str(prev_month.month) + return month + +def next_month(d): + days_in_month = calendar.monthrange(d.year, d.month)[1] + last = d.replace(day=days_in_month) + next_month = last + timedelta(days=1) + month = 'month=' + str(next_month.year) + '-' + str(next_month.month) + return month + +def event(request, event_id=None): + instance = Event() + if event_id: + instance = get_object_or_404(Event, pk=event_id) + else: + instance = Event() + + form = EventForm(request.POST or None, instance=instance) + if request.POST and form.is_valid(): + form.save() + return HttpResponseRedirect(reverse('cal:calendar')) + return render(request, 'cal/event.html', {'form': form}) \ No newline at end of file diff --git a/djangocalendar/settings.py b/djangocalendar/settings.py index af200ef..14e8aac 100644 --- a/djangocalendar/settings.py +++ b/djangocalendar/settings.py @@ -118,4 +118,7 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ -STATIC_URL = '/static/' +# Physical system path where the static files are stored. +STATIC_ROOT = os.path.join(BASE_DIR, 'static').replace('\\', '/') +# URL that your STATIC files will be accessible through the browser. +STATIC_URL = '/static/' \ No newline at end of file diff --git a/images/calendar_v2.0.png b/images/calendar_v2.0.png new file mode 100644 index 0000000..1ed211e Binary files /dev/null and b/images/calendar_v2.0.png differ diff --git a/images/calendar_v2.0_form_edit.png b/images/calendar_v2.0_form_edit.png new file mode 100644 index 0000000..74a5e63 Binary files /dev/null and b/images/calendar_v2.0_form_edit.png differ diff --git a/images/calendar_v2.0_form_new.png b/images/calendar_v2.0_form_new.png new file mode 100644 index 0000000..31629ec Binary files /dev/null and b/images/calendar_v2.0_form_new.png differ