from django.conf import settings
from django.core.urlresolvers import reverse, reverse_lazy
from django.shortcuts import render
from django.template.loader import get_template
from django.views.generic import TemplateView

from workshops.forms import (
    SWCEventRequestForm,
    DCEventRequestForm,
    EventSubmitForm,
    DCSelfOrganizedEventRequestForm,
    TrainingRequestForm,
    ProfileUpdateRequestForm,
)
from workshops.models import (
    EventRequest,
    EventSubmission as EventSubmissionModel,
    DCSelfOrganizedEventRequest as DCSelfOrganizedEventRequestModel,
)
from workshops.util import (
    login_not_required,
    LoginNotRequiredMixin,
)
from workshops.base_views import (
    AMYCreateView,
    EmailSendMixin,
)


class SWCEventRequest(LoginNotRequiredMixin, EmailSendMixin, AMYCreateView):
    model = EventRequest
    form_class = SWCEventRequestForm
    page_title = 'Request a Software Carpentry Workshop'
    template_name = 'forms/workshop_swc_request.html'
    success_url = reverse_lazy('swc_workshop_request_confirm')
    email_fail_silently = False
    email_kwargs = {
        'to': settings.REQUEST_NOTIFICATIONS_RECIPIENTS,
        'reply_to': None,
    }

    def get_success_message(self, *args, **kwargs):
        """Don't display a success message."""
        return ''

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = self.page_title
        return context

    def get_subject(self):
        subject = (
            '[{tag}] New workshop request: {affiliation}, {country}'
        ).format(
            tag=self.object.workshop_type.upper(),
            country=self.object.country.name,
            affiliation=self.object.affiliation,
        )
        return subject

    def get_body(self):
        link = self.object.get_absolute_url()
        link_domain = settings.SITE_URL

        body_txt = get_template(
            'mailing/eventrequest.txt'
        ).render({
            'object': self.object,
            'link': link,
            'link_domain': link_domain,
        })

        body_html = get_template(
            'mailing/eventrequest.html'
        ).render({
            'object': self.object,
            'link': link,
            'link_domain': link_domain,
        })
        return body_txt, body_html

    def form_valid(self, form):
        """Send email to admins if the form is valid."""
        data = form.cleaned_data
        self.email_kwargs['reply_to'] = (data['email'], )
        result = super().form_valid(form)
        return result


class SWCEventRequestConfirm(LoginNotRequiredMixin, TemplateView):
    """Display confirmation of received workshop request."""
    template_name = 'forms/workshop_swc_request_confirm.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Thank you for requesting a workshop'
        return context


class DCEventRequest(SWCEventRequest):
    form_class = DCEventRequestForm
    page_title = 'Request a Data Carpentry Workshop'
    template_name = 'forms/workshop_dc_request.html'
    success_url = reverse_lazy('dc_workshop_request_confirm')


class DCEventRequestConfirm(SWCEventRequestConfirm):
    """Display confirmation of received workshop request."""
    template_name = 'forms/workshop_dc_request_confirm.html'


@login_not_required
def profileupdaterequest_create(request):
    """
    Profile update request form. Accessible to all users (no login required).

    This one is used when instructors want to change their information.
    """
    form = ProfileUpdateRequestForm()
    page_title = 'Update Instructor Profile'

    if request.method == 'POST':
        form = ProfileUpdateRequestForm(request.POST)

        if form.is_valid():
            form.save()

            # TODO: email notification?

            context = {
                'title': 'Thank you for updating your instructor profile',
            }
            return render(request,
                          'forms/profileupdate_confirm.html',
                          context)
        else:
            messages.error(request, 'Fix errors below.')

    context = {
        'title': page_title,
        'form': form,
    }
    return render(request, 'forms/profileupdate.html', context)


# This form is disabled as per @maneesha's request
# class EventSubmission(LoginNotRequiredMixin, EmailSendMixin,
#                       AMYCreateView):
class EventSubmission(LoginNotRequiredMixin, TemplateView):
    """Display form for submitting existing workshops."""
    model = EventSubmissionModel
    form_class = EventSubmitForm
    template_name = 'forms/event_submit.html'
    success_url = reverse_lazy('event_submission_confirm')
    email_fail_silently = False
    email_kwargs = {
        'to': settings.REQUEST_NOTIFICATIONS_RECIPIENTS,
    }

    def get_success_message(self, *args, **kwargs):
        """Don't display a success message."""
        return ''

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Tell us about your workshop'
        return context

    def get_subject(self):
        return ('New workshop submission from {}'
                .format(self.object.contact_name))

    def get_body(self):
        link = self.object.get_absolute_url()
        link_domain = settings.SITE_URL
        body_txt = get_template('mailing/event_submission.txt') \
            .render({
                'object': self.object,
                'link': link,
                'link_domain': link_domain,
            })
        body_html = get_template('mailing/event_submission.html') \
            .render({
                'object': self.object,
                'link': link,
                'link_domain': link_domain,
            })
        return body_txt, body_html


class EventSubmissionConfirm(LoginNotRequiredMixin, TemplateView):
    """Display confirmation of received workshop submission."""
    template_name = 'forms/event_submission_confirm.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Thanks for your submission'
        return context


class DCSelfOrganizedEventRequest(LoginNotRequiredMixin, EmailSendMixin,
                                  AMYCreateView):
    "Display form for requesting self-organized workshops for Data Carpentry."
    model = DCSelfOrganizedEventRequestModel
    form_class = DCSelfOrganizedEventRequestForm
    # we're reusing DC templates for normal workshop requests
    template_name = 'forms/workshop_dc_request.html'
    success_url = reverse_lazy('dc_workshop_selforganized_request_confirm')
    email_fail_silently = False
    email_kwargs = {
        'to': settings.REQUEST_NOTIFICATIONS_RECIPIENTS,
    }

    def get_success_message(self, *args, **kwargs):
        """Don't display a success message."""
        return ''

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Register a self-organized Data Carpentry workshop'
        return context

    def get_subject(self):
        return ('DC: new self-organized workshop request from {} @ {}'
                .format(self.object.name, self.object.organization))

    def get_body(self):
        link = self.object.get_absolute_url()
        link_domain = settings.SITE_URL
        body_txt = get_template('mailing/dc_self_organized.txt') \
            .render({
                'object': self.object,
                'link': link,
                'link_domain': link_domain,
            })
        body_html = get_template('mailing/dc_self_organized.html') \
            .render({
                'object': self.object,
                'link': link,
                'link_domain': link_domain,
            })
        return body_txt, body_html


class DCSelfOrganizedEventRequestConfirm(LoginNotRequiredMixin, TemplateView):
    """Display confirmation of a received self-organized workshop request."""
    # we're reusing DC templates for normal workshop requests
    template_name = 'forms/workshop_dc_request_confirm.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Thanks for your submission'
        return context


@login_not_required
def trainingrequest_create(request):
    """A form to let all users (no login required) to request Instructor
    Training."""

    form = TrainingRequestForm()
    page_title = 'Apply for Instructor Training'

    if request.method == 'POST':
        form = TrainingRequestForm(request.POST)

        if form.is_valid():
            form.save()

            # TODO: email notification?

            context = {
                'title': 'Thank you for applying for an instructor training.',
            }
            return render(request,
                          'forms/trainingrequest_confirm.html',
                          context)
        else:
            messages.error(request, 'Fix errors below.')

    context = {
        'title': page_title,
        'form': form,
    }
    return render(request, 'forms/trainingrequest.html', context)

extforms/views.py
from django.conf import settings
from django.contrib import messages
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import render
from django.template.loader import get_template
from django.views.generic import TemplateView

from workshops.forms import (
    SWCEventRequestForm,
    DCEventRequestForm,
    EventSubmitForm,
    DCSelfOrganizedEventRequestForm,
    TrainingRequestForm,
    ProfileUpdateRequestForm,
)
from workshops.models import (
    EventRequest,
    EventSubmission as EventSubmissionModel,
    DCSelfOrganizedEventRequest as DCSelfOrganizedEventRequestModel,
)
from workshops.util import (
    login_not_required,
    LoginNotRequiredMixin,
)
from workshops.base_views import (
    AMYCreateView,
    EmailSendMixin,
)


class SWCEventRequest(LoginNotRequiredMixin, EmailSendMixin, AMYCreateView):
    model = EventRequest
    form_class = SWCEventRequestForm
    page_title = 'Request a Software Carpentry Workshop'
    template_name = 'forms/workshop_swc_request.html'
    success_url = reverse_lazy('swc_workshop_request_confirm')
    email_fail_silently = False
    email_kwargs = {
        'to': settings.REQUEST_NOTIFICATIONS_RECIPIENTS,
        'reply_to': None,
    }

    def get_success_message(self, *args, **kwargs):
        """Don't display a success message."""
        return ''

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = self.page_title
        return context

    def get_subject(self):
        subject = (
            '[{tag}] New workshop request: {affiliation}, {country}'
        ).format(
            tag=self.object.workshop_type.upper(),
            country=self.object.country.name,
            affiliation=self.object.affiliation,
        )
        return subject

    def get_body(self):
        link = self.object.get_absolute_url()
        link_domain = settings.SITE_URL

        body_txt = get_template(
            'mailing/eventrequest.txt'
        ).render({
            'object': self.object,
            'link': link,
            'link_domain': link_domain,
        })

        body_html = get_template(
            'mailing/eventrequest.html'
        ).render({
            'object': self.object,
            'link': link,
            'link_domain': link_domain,
        })
        return body_txt, body_html

    def form_valid(self, form):
        """Send email to admins if the form is valid."""
        data = form.cleaned_data
        self.email_kwargs['reply_to'] = (data['email'], )
        result = super().form_valid(form)
        return result


class SWCEventRequestConfirm(LoginNotRequiredMixin, TemplateView):
    """Display confirmation of received workshop request."""
    template_name = 'forms/workshop_swc_request_confirm.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Thank you for requesting a workshop'
        return context


class DCEventRequest(SWCEventRequest):
    form_class = DCEventRequestForm
    page_title = 'Request a Data Carpentry Workshop'
    template_name = 'forms/workshop_dc_request.html'
    success_url = reverse_lazy('dc_workshop_request_confirm')


class DCEventRequestConfirm(SWCEventRequestConfirm):
    """Display confirmation of received workshop request."""
    template_name = 'forms/workshop_dc_request_confirm.html'


@login_not_required
def profileupdaterequest_create(request):
    """
    Profile update request form. Accessible to all users (no login required).

    This one is used when instructors want to change their information.
    """
    form = ProfileUpdateRequestForm()
    page_title = 'Update Instructor Profile'

    if request.method == 'POST':
        form = ProfileUpdateRequestForm(request.POST)

        if form.is_valid():
            form.save()

            # TODO: email notification?

            context = {
                'title': 'Thank you for updating your instructor profile',
            }
            return render(request,
                          'forms/profileupdate_confirm.html',
                          context)
        else:
            messages.error(request, 'Fix errors below.')

    context = {
        'title': page_title,
        'form': form,
    }
    return render(request, 'forms/profileupdate.html', context)


# This form is disabled as per @maneesha's request
# class EventSubmission(LoginNotRequiredMixin, EmailSendMixin,
#                       AMYCreateView):
class EventSubmission(LoginNotRequiredMixin, TemplateView):
    """Display form for submitting existing workshops."""
    model = EventSubmissionModel
    form_class = EventSubmitForm
    template_name = 'forms/event_submit.html'
    success_url = reverse_lazy('event_submission_confirm')
    email_fail_silently = False
    email_kwargs = {
        'to': settings.REQUEST_NOTIFICATIONS_RECIPIENTS,
    }

    def get_success_message(self, *args, **kwargs):
        """Don't display a success message."""
        return ''

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Tell us about your workshop'
        return context

    def get_subject(self):
        return ('New workshop submission from {}'
                .format(self.object.contact_name))

    def get_body(self):
        link = self.object.get_absolute_url()
        link_domain = settings.SITE_URL
        body_txt = get_template('mailing/event_submission.txt') \
            .render({
                'object': self.object,
                'link': link,
                'link_domain': link_domain,
            })
        body_html = get_template('mailing/event_submission.html') \
            .render({
                'object': self.object,
                'link': link,
                'link_domain': link_domain,
            })
        return body_txt, body_html


class EventSubmissionConfirm(LoginNotRequiredMixin, TemplateView):
    """Display confirmation of received workshop submission."""
    template_name = 'forms/event_submission_confirm.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Thanks for your submission'
        return context


class DCSelfOrganizedEventRequest(LoginNotRequiredMixin, EmailSendMixin,
                                  AMYCreateView):
    "Display form for requesting self-organized workshops for Data Carpentry."
    model = DCSelfOrganizedEventRequestModel
    form_class = DCSelfOrganizedEventRequestForm
    # we're reusing DC templates for normal workshop requests
    template_name = 'forms/workshop_dc_request.html'
    success_url = reverse_lazy('dc_workshop_selforganized_request_confirm')
    email_fail_silently = False
    email_kwargs = {
        'to': settings.REQUEST_NOTIFICATIONS_RECIPIENTS,
    }

    def get_success_message(self, *args, **kwargs):
        """Don't display a success message."""
        return ''

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Register a self-organized Data Carpentry workshop'
        return context

    def get_subject(self):
        return ('DC: new self-organized workshop request from {} @ {}'
                .format(self.object.name, self.object.organization))

    def get_body(self):
        link = self.object.get_absolute_url()
        link_domain = settings.SITE_URL
        body_txt = get_template('mailing/dc_self_organized.txt') \
            .render({
                'object': self.object,
                'link': link,
                'link_domain': link_domain,
            })
        body_html = get_template('mailing/dc_self_organized.html') \
            .render({
                'object': self.object,
                'link': link,
                'link_domain': link_domain,
            })
        return body_txt, body_html


class DCSelfOrganizedEventRequestConfirm(LoginNotRequiredMixin, TemplateView):
    """Display confirmation of a received self-organized workshop request."""
    # we're reusing DC templates for normal workshop requests
    template_name = 'forms/workshop_dc_request_confirm.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Thanks for your submission'
        return context


@login_not_required
def trainingrequest_create(request):
    """A form to let all users (no login required) to request Instructor
    Training."""

    form = TrainingRequestForm()
    page_title = 'Apply for Instructor Training'

    if request.method == 'POST':
        form = TrainingRequestForm(request.POST)

        if form.is_valid():
            form.save()

            # TODO: email notification?

            context = {
                'title': 'Thank you for applying for an instructor training.',
            }
            return render(request,
                          'forms/trainingrequest_confirm.html',
                          context)
        else:
            messages.error(request, 'Fix errors below.')

    context = {
        'title': page_title,
        'form': form,
    }
    return render(request, 'forms/trainingrequest.html', context)

extforms/views.py
diff --git a/docs/releases/README.md b/docs/releases/README.md
new file mode 100644
index 000000000..e6b191f6e
--- /dev/null
+++ b/docs/releases/README.md
@@ -0,0 +1,6 @@
+# Release notes
+
+Release notes since v0.9.1 to v1.8.0 were taken from
+[Piotr Banaszkiewicz](https://github.com/pbanaszkiewicz)'s
+[blog](http://piotr.banaszkiewicz.org/) and adapted to a general template
+used here.
diff --git a/docs/releases/TEMPLATE.md b/docs/releases/TEMPLATE.md
new file mode 100644
index 000000000..12b22913b
--- /dev/null
+++ b/docs/releases/TEMPLATE.md
@@ -0,0 +1,28 @@
+# AMY release vX.Y.Z
+
+Major/Minor AMY [vX.Y.Z][] was released on YYYY-MM-DD.
+
+Thanks to ... ... ... for doing excellent job.
+
+## New features
+
+* [PersonA][] added something excellent
+* [PersonB][] added something excellent
+* [PersonC][] added something excellent
+* [PersonD][] added something excellent
+* [PersonE][] added something excellent
+
+## Bugfixes
+
+* [PersonA][] fixed something extraordinary
+* [PersonB][] fixed something extraordinary
+* [PersonC][] fixed something extraordinary
+* [PersonD][] fixed something extraordinary
+* [PersonE][] fixed something extraordinary
+
+[vX.Y.Z]: https://github.com/swcarpentry/amy/milestone/NN
+[PersonA]: https://github.com/PersonA
+[PersonB]: https://github.com/PersonB
+[PersonC]: https://github.com/PersonC
+[PersonD]: https://github.com/PersonD
+[PersonE]: https://github.com/PersonE
diff --git a/docs/releases/v0.9.1.md b/docs/releases/v0.9.1.md
new file mode 100644
index 000000000..8a3904a02
--- /dev/null
+++ b/docs/releases/v0.9.1.md
@@ -0,0 +1,9 @@
+# AMY release v0.9.1
+
+*This note was taken from v1.0.0; sorry for the brevity.*
+
+-   [Greg][] has worked on the "problems" page: switched its layout
+    to 3-column, added better email messages, etc.
+-   He also extended our API to return airport's country.
+
+[Greg]: https://github.com/gvwilson
diff --git a/docs/releases/v0.9.2.md b/docs/releases/v0.9.2.md
new file mode 100644
index 000000000..b0aa8cb0d
--- /dev/null
+++ b/docs/releases/v0.9.2.md
@@ -0,0 +1,8 @@
+# AMY release v0.9.2
+
+*This note was taken from v1.0.0; sorry for the brevity.*
+
+-   [Piotr][] hot-fixed an issue with creating M2M with intermediary model in
+    model forms (basically impossible, we have to do this manually).
+
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v0.9.5.md b/docs/releases/v0.9.5.md
new file mode 100644
index 000000000..29a7a96eb
--- /dev/null
+++ b/docs/releases/v0.9.5.md
@@ -0,0 +1,6 @@
+# AMY release v0.9.5
+
+*This note was taken from v1.0.0; sorry for the brevity.*
+
+-   This release adds workshop request forms and profile update request
+    form to AMY.
diff --git a/docs/releases/v0.9.6.md b/docs/releases/v0.9.6.md
new file mode 100644
index 000000000..f0a2e53d2
--- /dev/null
+++ b/docs/releases/v0.9.6.md
@@ -0,0 +1,8 @@
+# AMY release v0.9.6
+
+*This note was taken from v1.0.0; sorry for the brevity.*
+
+-   [Piotr][] hot-fixed a bug with settings: we should load more email settings
+    from environmental variables in order to use emails from Django.
+
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v0.9.7.md b/docs/releases/v0.9.7.md
new file mode 100644
index 000000000..9213eb83f
--- /dev/null
+++ b/docs/releases/v0.9.7.md
@@ -0,0 +1,20 @@
+# AMY release v0.9.7
+
+*This note was taken from v1.0.0; sorry for the brevity.*
+
+-   [Piotr][] added a new country: 'Online' (for international / online events,
+    like some Instructor Trainings).
+-   The dashboard was changed to show not only upcoming, but also
+    ongoing workshops (in the same column).
+-   [Greg][] added some helper commands to report inconsistencies in PDF
+    certificates for instructors.
+-   [Greg][] also removed old command used for checking AMY's database against
+    SWC site's archive. Site has moved to use AMY's API, so the archive
+    was gone and the command was made redundant.
+-   [Piotr][] updated `check.py` script (used for checking workshop settings
+    correctness) from workshop-template repository.
+-   [Greg][] cleared up titles of forms introduced in v0.9.5.
+-   [Piotr][] split these forms' templates and added SWC and DC logos to them.
+
+[Greg]: https://github.com/gvwilson
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.0.0.md b/docs/releases/v1.0.0.md
new file mode 100644
index 000000000..a9df6a478
--- /dev/null
+++ b/docs/releases/v1.0.0.md
@@ -0,0 +1,34 @@
+# AMY release v1.0.0
+
+That was a looong ride, but we're finally here! AMY is mature enough to
+be given the [v1.0.0](https://github.com/swcarpentry/amy/milestones/v1.0)
+tag.
+
+
+## New features
+
+-   We have a new database for development use.
+-   [Piotr][] changed the way AMY handles event URLs:
+    -   admins cannot use GitHub repository URL for event's URL
+    -   migration was used to change all URLs to website-URL format (if
+        possible)
+    -   [Piotr][] also fixed import/update from URL feature to auto-select
+        country if possible
+    -   …and to auto-use correct website URL instead of repository URL.
+-   AMY auto-counts event attendees if provided with a list of attendees
+    (for example via bulk-upload).
+-   External links now open in new tabs.
+-   Now there are more options to `Event.invoice` (for example with reason
+    for lack of invoice).
+-   New remove awards functionality.
+
+
+## Bug fixes
+
+-   Bulk-uploaded CSVs with entries that contained empty rows (for
+    example: `,,,,,,`) were treated as correct, but not anymore.
+-   "Artificial" country European Union had been removed and was brought
+    back since some hosts were using it.
+-   API now returns website URLs to events instead of original URLs.
+
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.1.0.md b/docs/releases/v1.1.0.md
new file mode 100644
index 000000000..8ff17658c
--- /dev/null
+++ b/docs/releases/v1.1.0.md
@@ -0,0 +1,49 @@
+# AMY release v1.1.0
+
+This is the second post-gsoc release of AMY, the workshop management
+tool for [Software-Carpentry](https://software-carpentry.org/). Below
+you can find a release notes for
+[v1.1.0](https://github.com/swcarpentry/amy/milestones/v1.1).
+
+
+## Announcement
+
+We're very happy to announce that development of AMY versions 1.1, 1.2 and
+1.3 is supported by [prof. Ethan White](http://whitelab.weecology.org/).
+Thanks a lot!
+
+
+## New features
+
+-   New switch for Open/Closed requests (both profile update requests
+    and workshop requests). Admins will be able to look up dismissed or
+    finished requests.
+-   "Recently changed" log was moved to a separate view so that it
+    doesn't clutter the dashboard.
+-   Some objects (events, hosts, persons, airports, tasks) are followed
+    by [django-reversion](https://github.com/etianen/django-reversion).
+    Thanks to that fact we can now show who+when created any of these
+    objects and who+when last modified it.
+-   Each event marked as "uninvoiced" can easily be invoiced by the
+    admin thanks to the new "Invoice" button on the event's details
+    page.
+-   New report: people who have been through instructor training but
+    didn't finish.
+-   Self-organized workshops can now have a "self-organized" host.
+-   New report: show how often instructors teach.
+-   Admins can mark events as completed and throw them out of the filter
+    results.
+-   **Big**: events can have multiple TODO items associated with them.
+
+
+
+## Bug fixes
+
+-   Small typo in `README.md` fixed.
+-   Logic for displaying uninvoiced events was fixed so that events
+    marked as uninvoiced but without any admin fee do show up on the
+    dashboard.
+-   Fields layout on the profile update request details page was changed
+    to avoid confusion.
+-   Better wording in email request to workshop hosts for attendees
+    figures.
diff --git a/docs/releases/v1.2.0.md b/docs/releases/v1.2.0.md
new file mode 100644
index 000000000..0fcef822e
--- /dev/null
+++ b/docs/releases/v1.2.0.md
@@ -0,0 +1,52 @@
+# AMY release v1.2.0
+
+This is the third post-gsoc release of AMY, the workshop management tool
+for [Software-Carpentry](https://software-carpentry.org/). This release
+was supported by [prof. Ethan White](http://whitelab.weecology.org/).
+Thanks a lot!
+
+Below you can find release notes for
+[v1.2.0](https://github.com/swcarpentry/amy/milestones/v1.2).
+
+## New features
+
+-   (big one) fix finding, parsing and validating of event tags: it will
+    now work with `<meta>` tags on workshop websites
+-   after approving person's profile update request, the updated profile
+    is displayed instead of the list of other update requests.
+-   password reset form was added,
+-   issues related to, for example, missing location data are now
+    highlighted on event details page,
+-   (big one) admins can now be assigned to specific events or event
+    requests,
+-   [Greg][] changed descriptions section names on "instructor issues" page,
+-   the same page was updated by me so that we can have pending and
+    "gave-up-on" trainees listed,
+-   the previous feature was introduced thanks to new 'stalled' tag,
+-   our API gained some filtering (go to
+    <https://amy.software-carpentry.org/api/v1/events/published/>, click
+    "OPTIONS" and look at "query\_params"; these can be added to the API
+    calls, for example:
+    <https://amy.software-carpentry.org/api/v1/events/published/?host=123456789&administrator=987654321>)
+-   the same API gained a new endpoint used for generating list of
+    current members of Software-Carpentry Foundation; this is in no way
+    official list of members, but it can be used to help determine who's
+    eligible (credits for this one go to both Greg and me since I
+    finished his pull request),
+-   it's now possible to search in events' URL, contact, venue and
+    address fields,
+-   2 new options for invoice status were added (*not invoiced for
+    historical reasons* and *not invoiced because of membership*),
+-   more places (workshop issues, and on each workshop without
+    attendance data) to send "Give us attendance figures" emails, more
+    people to send to,
+-   profile update requests can now be edited by admins.
+
+## Bug fixes
+
+-   wrong object type passed to `"".format()` method
+-   wrong characters permitted in event slugs
+-   (big one) inconsistant logic in `EventQuerySet.(un)published_events`
+    methods
+
+[Greg]: https://github.com/gvwilson
diff --git a/docs/releases/v1.2.1_v1.3.0.md b/docs/releases/v1.2.1_v1.3.0.md
new file mode 100644
index 000000000..08138451e
--- /dev/null
+++ b/docs/releases/v1.2.1_v1.3.0.md
@@ -0,0 +1,49 @@
+# AMY release v1.3.0
+
+In the past month we've seen two releases of AMY:
+[v1.2.1](https://github.com/swcarpentry/amy/milestone/19) and
+[v1.3.0](https://github.com/swcarpentry/amy/milestone/17).
+This is a joined release notes for both of them.
+
+
+## New features
+
+* use custom-built jQuery-UI (so that we no longer have conflicts
+  with Bootstrap's tooltip module)
+* Greg updated the script used to send instructors "Hey, update
+  your info" mails (it's getting removed later on)
+* it's possible to add memberships per host
+* new badge: DC instructor
+* new logic for dealing with two instructor badges
+* timeline of TO-DO items
+* basic models (e.g. lessons, tags, academic levels, etc.) are now
+  manageable from Django's admin interface
+* all persons view: add filtering by workshop type person taught at
+* remove blurred production database in favor of generated fake
+  database
+* mailing script turned into better Django management command
+* bulk upload now shows generated username and suggested people with
+  matching names
+* show preview of event on SWC website
+* API: filter events by tags
+
+
+## Bug fixes
+
+* wrong URL used in event validation or import/update features is
+  now indicated (and we won't receive wrong notifications about it)
+* properly throw 404 on some pages (previously: 500)
+* spaces are striped from `Person` and `ProfileUpdateRequest`
+  fields (names, emails)
+* disable location inputs on event details page if online country
+  was preselected
+
+
+## Removed
+
+* [Greg][] removed some unused scripts (`test-command-line-upload.sh`)
+  and commands (`parse-instructor-info.py`)
+* notifications about invalid HTTP header `Host`
+* other removed scripts and commands
+
+[Greg]: https://github.com/gvwilson
diff --git a/docs/releases/v1.3.1.md b/docs/releases/v1.3.1.md
new file mode 100644
index 000000000..a92e77baa
--- /dev/null
+++ b/docs/releases/v1.3.1.md
@@ -0,0 +1,6 @@
+# AMY release v1.3.1
+
+*This note was taken from v1.4.0; sorry for the brevity.*
+
+One bugfix: don't break whole timeline widget when there are TODOs without due
+date.
diff --git a/docs/releases/v1.3.2.md b/docs/releases/v1.3.2.md
new file mode 100644
index 000000000..e85923b88
--- /dev/null
+++ b/docs/releases/v1.3.2.md
@@ -0,0 +1,13 @@
+# AMY release v1.3.2
+
+*This note was taken from v1.4.0; sorry for the brevity.*
+
+New feature: stop using dots (`.`) for usernames, use underscores (`_`)
+instead.
+
+This was an interesting issue: since we rely on some Ruby software on the SWC
+website, we can't have dots in filenames (they're treated as parameter access
+operator, for example: `banaszkiewicz.piotr` is `piotr` parameter on
+`banaszkiewicz` object). But we have filenames that correspond to usernames in
+AMY. So it was necessary to drop dots and switch to underscores…
+
diff --git a/docs/releases/v1.4.0.md b/docs/releases/v1.4.0.md
new file mode 100644
index 000000000..aeade6aed
--- /dev/null
+++ b/docs/releases/v1.4.0.md
@@ -0,0 +1,76 @@
+# AMY release v1.4.0
+
+Intense month of January is almost behind us, so it's time for new AMY release.
+
+Three versions have been released since [v1.3.0][]: [v1.3.1][], [v1.3.2][]
+(both in separate release notes files) and finally [v1.4.0][].
+
+Unfortunately, due to the way we have our project laid out on GitHub, some of
+the features implemented for [v1.4.0][] before [v1.3.2][] were included in
+the deployment; I will still put them here, though.
+
+The biggest highlights of this month are definitely:
+
+* first approach to the new API
+* API reports
+* merging events.
+
+There were also some essential features, but not much. In v1.5 there will be
+a lot more.
+
+## Data fixing
+
+We had to programmatically fix/complete some of our records:
+
+* historical events on production server were assigned an administering
+  organization (that's the one responsible for taking care of the workshop
+  bureaucracy),
+* new DC instructors were added: anyone with a special note or anyone who
+  taught at DC workshop now has a `DC instructor` badge.
+
+## New features
+
+* `Person` model is now able to store person's occupation and ORCID code,
+* events can hold links to survey results (pre-workshop for learners and for
+  instructors, post-workshop for learners and for instructors, and long-term
+  for learners),
+* API call for getting members list is now for logged in users only, and
+  returns members' usernames too,
+* merging events: with option to select fields from either of events, or (in
+  some cases) even to combine them together. The underlying code may be
+  reused to fix persons merging,
+* workshop issues page now allows to filter workshops by assigned admin
+* move most of reports to the API; 3 reports now present a graph for easy use,
+  1 report was requested to be moved to API, and 1 new report was requested
+  (and [Piotr][] made it in API),
+* API: new structure. It's using hyperlinks between resources and allows to
+  view and filter for example people associated with specific events,
+* slow tests were fixed (we gained probably around 10s on whole test suite,
+  even though about 10-20 new tests were introduced); now it's time to speed up
+  migrations,
+* [Greg][] added two new badges to the database: maintainer and trainer; [Piotr][] made
+  sure to allow for editing badges via Django Admin interface, and also added
+  these new badges to the fake database command,
+* [Greg][] also added a new command for getting list of people who should be warned
+  because their instructor training was about to close,
+* meanwhile [Piotr][] added a command for displaying report about instructor training
+  completion rates.
+
+## Bug fixes
+
+* some fields containing numerical values were switched to other type of fields
+  to prevent slider from appearing; the background for this issue was that
+  when scrolling through a page with form, on MacOSX people would accidentally
+  change values of numerical fields,
+* generation of initial revisions was added to the process of creating a fake
+  database for development use,
+* some types of events (`stalled` and `unresponsive`) were kicked out from
+  debrief lookup,
+* some invoice options were changed to remain consistent with the rest.
+
+[v1.3.0]: https://github.com/swcarpentry/amy/milestones/v1.3
+[v1.3.1]: https://github.com/swcarpentry/amy/milestones/v1.3.1
+[v1.3.2]: https://github.com/swcarpentry/amy/milestones/v1.3.2
+[v1.4.0]: https://github.com/swcarpentry/amy/milestones/v1.4
+[Greg]: https://github.com/gvwilson
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.5.0.md b/docs/releases/v1.5.0.md
new file mode 100644
index 000000000..527a7c4f9
--- /dev/null
+++ b/docs/releases/v1.5.0.md
@@ -0,0 +1,54 @@
+# AMY release v1.5.0
+
+Development of AMY in February had seen a boost due to the winter break, and
+that ended with today's release of [v1.5.0][].
+
+
+## New features
+
+The biggest new features for this release:
+
+* new workshops requests once accepted are linked to resulting events
+* admins now can submit invoices from AMY
+* admins can now receive event submissions (this should work really well for
+  self-organized workshops that already have a workshop page)
+
+Other changes:
+
+* badge details view allows for filtering
+* development and production software was updated
+* production assets (JavaScript and CSS files) are now compressed and served
+  with unique name
+* persons merging was reworked and is now a lot better
+* it's possible to find duplicates in the database now
+* base templates were renamed to lower confusion
+* API returns award date for every person for every badge
+* added CSV renderer to some API endpoint for exporting members
+* debrief was renamed and also allows for CSV export
+* `Award` model gained `awarded_by` field pointing to the person responsible
+  for awarding a badge
+* person lookup in some places now works for "Name Lastname" pattern too.
+
+Changes contributed by [Greg][]:
+
+* CSV export of instructor completion rates
+* CSV export of missing instructor certificates
+
+(Greg's responsible for training new instructors.)
+
+
+## Bug fixes
+
+* Django Rest Framework erroring URLs were removed (still not sure what caused
+  them to error-out)
+* dashboard and workshop issues now show only active (== not stalled, not
+  marked as complete) events
+* workshop issues was extended by providing a list of workshops without any
+  assigned instructors
+* a rare error when looking someone up was fixed
+* API throttle rates have been increased
+* current and upcoming events on the dashboard are now based off of published
+  events.
+
+[v1.5.0]: https://github.com/swcarpentry/amy/milestones/v1.5
+[Greg]: https://github.com/gvwilson
diff --git a/docs/releases/v1.5.1.md b/docs/releases/v1.5.1.md
new file mode 100644
index 000000000..6f7b7b675
--- /dev/null
+++ b/docs/releases/v1.5.1.md
@@ -0,0 +1,55 @@
+# AMY release v1.5.1
+
+This past month we had a number of submissions from prospect GSOC'16 students
+(yay!) and, for the first time, number of bugs fixed exceeded number of new
+enhancements.
+
+Since the number of new features is small, we decided to release a minor version
+([v1.5.1][]).
+
+
+## Contributions by GSOC students
+
+March 2016 held GSOC'16 applications period for students. We had a lot of
+students this year and we encouraged them to take a look at AMY and maybe fix
+something.  This resulted in a number of good contributions.
+
+
+## New features
+
+Starting with new features since there's so few of them:
+
+* [Greg][] extended the `check_certificates.py` command to additionally
+  return events people participated in
+* [Shubham Singh][] added "Notes" field to instructor profile update form
+* [Shubham Singh][] added new tag "hackaton"
+* [Greg][] removed command `check_badges.py`
+* [Piotr][] enabled autogeneration of user's username after they're added to the
+  database
+* [Greg][] added link to Privacy Policy in the footer.
+
+
+## Bug fixes
+
+* [Nikhil Verma][] found and fixed "List duplicates" page error when no
+  duplicates existed
+* [Chris Medrela][] found and fixed 404 page for revision that didn't exist
+* [Greg][] fixed `NameError` in `check_certificates.py`
+* [Piotr][] fixed a 500 error appearing when user submitted incomplete form used for
+  matching people's names
+* Maneesha Sane fixed minimal number of instructors required in our workshop
+  request form
+* [Piotr][] fixed API renderers (CSV, YAML) not iterating generators but displaying
+  their textual representations
+* [Piotr][] fixed instructors-num-taught report to include people's names
+* [Piotr][] fixed a small typo in the name of post-workshop survey for instructors (it
+  was called "pre")
+* [Piotr][] made the emails case-insensitive
+* [Piotr][] fixed some 500 errors related to event importing.
+
+[v1.5.1]: https://github.com/swcarpentry/amy/milestones/v1.5.1
+[Shubham Singh]: https://github.com/shubhsingh594
+[Nikhil Verma]: https://github.com/nikhilweee
+[Chris Medrela]: https://github.com/chrismedrela
+[Piotr]: https://github.com/pbanaszkiewicz
+[Greg]: https://github.com/gvwilson
diff --git a/docs/releases/v1.5.2.md b/docs/releases/v1.5.2.md
new file mode 100644
index 000000000..98346913c
--- /dev/null
+++ b/docs/releases/v1.5.2.md
@@ -0,0 +1,226 @@
+# AMY bugfix release v1.5.2 and bug postmortem
+
+*This is an investigation story by [Piotr][].*
+
+24 hours ago, Maneesha Sane
+[noticed and informed](https://github.com/swcarpentry/amy/issues/744) on GitHub
+that one of the tags in AMY is missing.
+
+This observation led to an investigation on the servers and eventually to fix
+for a critical bug that caused the data loss.
+
+But before we jump into sysadmin work…
+
+## What is a "tag" in AMY terms?
+
+A tag is label that we give to various workshops. For example, all Software
+Carpentry workshops will have `SWC` tag, and all Data Carpentry workshops will
+have `DC` tag.
+
+There are more labels we use, and the one that went missing was `DC`.
+
+One event can have between zero and all the tags we have in the system, which
+means it's a many-to-many relationship between events and tags. This type of
+relationship requires additional intermediate table in the database.
+
+Contents from that table were missing because they were removed with removal
+of the `DC` tag.
+
+## Investigation
+
+I started the investigation by narrowing timespan where the event, that led to
+data loss, occurred.
+
+Then I followed by reading WWW server access logs to find out what happened in
+that timespan in hope I could find the bug.
+
+After narrowing list of suspects, I was able to reproduce the bug.
+
+Finally I retrieved the lost data from the most recent backup that still had
+it.
+
+### Ways to remove tags from AMY
+
+There's no interface for removing tags other than Django's auto-generated admin
+interface; only a couple of people have access to it.
+
+So the data loss was either human error or it was caused by code bug. This
+conclusion helped me define what I should be looking for in the WWW server log.
+
+### Narrowing event occurence timespan
+
+AMY's being backed-up by multiple systems; I logged into each of them and run
+multiple SQL queries on different databases to find out which backup had the
+`DC` tag and was the newest.
+
+It turned out that backup from 2016-04-06 17:00 UTC-4 was the most recent still
+with the `DC` tag.
+
+In the meantime I was fighting timezone correction… Our backup systems are in
+different datacenters and were running on different timezones.
+
+### Reading access log
+
+First thing I checked in the access log is if anyone was using the admin panel
+to remove the tag. Unfortunately this possibility was quickly ruled out; so
+the loss was caused by code bug.
+
+However, after reading the log no action stood out.
+
+Short, important side story: Software Carpentry website rebuilds every 30
+minutes. Each rebuild is shown in the log by multiple requests to AMY's API:
+
+{% highlight nginx %}
+[…] GET /api/v1/export/badges.yaml => generated 69122 bytes […]
+[…] GET /api/v1/export/instructors.yaml => generated 50488 bytes […]
+[…] GET /api/v1/events/published.yaml?tag=SWC => generated 231344 bytes […]
+[…] GET /api/v1/events/published.yaml?tag=DC => generated 17806 bytes […]
+[…] GET /api/v1/events/published.yaml?tag=TTT => generated 7879 bytes […]
+{% endhighlight %}
+
+Website grabs published events tagged by `SWC`, `DC` and `TTT` tags. This
+sequence of requests repeats every 30 minutes.
+
+After reading the log over and over I noticed that two consecutive calls to
+`/api/v1/events/published.yaml?tag=DC` yielded results of very different
+response lengths:
+
+{% highlight nginx %}
+[…] [Wed Apr  6 15:01:11 2016] GET /api/v1/events/published.yaml?tag=DC => generated 17806 bytes […]
+[... many more requests ...]
+[…] [Wed Apr  6 15:30:10 2016] GET /api/v1/events/published.yaml?tag=DC => generated 261521 bytes […]
+{% endhighlight %}
+
+Apparently then the DC tag disappeared, the API started returning all the
+published events, no matter if they were tagged `SWC` or something else.
+
+This was a clear indication that the `DC` tag disappeared between 15:01 and
+15:30.
+
+That timespan doesn't look like 17:00. Timezones... programmer's nightmare.
+
+### Suspects
+
+There was some user activity in this 30-minutes long window and one thing
+caught my eye:
+
+{% highlight nginx %}
+[…] POST /workshops/events/merge?event_a_0=2016-05-06-RDAP16-Atlanta&event_b_0=2016-05-06-asist […]
+{% endhighlight %}
+
+(The actual URL was slightly changed to remove unnecessary information.)
+
+This was a call to event merge functionality: someone wanted to merge
+workshops `2016-05-06-RDAP16-Atlanta` and `2016-05-06-asist`.
+
+{:.message}
+*Short side note*: merge functionality allows user to use more advanced
+strategy for merge; one can select which properties (or fields) in the final
+event should be used from event A (`2016-05-06-RDAP16-Atlanta` in our example)
+and which should be from event B (`2016-05-06-asist`). Additionally in case of
+event's tags it's possible to combine them from both base events.
+
+I started testing different strategies. I had a feeling that the bug had
+something to do with strategy for event tags. :)
+
+Finally I reproduced the bug by using following strategy:
+
+* base event: `2016-05-06-RDAP16-Atlanta` (event A)
+* tags: from event B.
+
+### Data retrieval
+
+At that point I decided to retrieve the lost data using SQL import/export
+functionality from the optimal (newest & containing the lost data) backup found
+earlier.
+
+### Bug
+
+The only code used in event merge functionality that would trigger accidental
+removal is:
+
+{% highlight python %}
+            if value == 'obj_a' and manager != related_a:
+                manager.all().delete()
+                manager.add(*list(related_a.all()))
+
+            elif value == 'obj_b' and manager != related_b:
+                manager.all().delete()
+                manager.add(*list(related_b.all()))
+{% endhighlight %}
+
+This code is used for substituting related objects (tags in our case). It works
+like this:
+
+> if some field's strategy is to switch to objects from the other event, then
+> remove all currently assigned objects and add objects from the other event's
+> field.
+
+Translated into tags:
+
+> if user wants to use event `2016-05-06-RDAP16-Atlanta` as base event, but
+> keep tags from the other event (`2016-05-06-asist`) then remove current tags
+> from base event and add tags from the other event.
+
+See what's going on here? Base event's tags were removed instead of being
+unassigned.
+
+#### Django: related manager and assignments
+
+In this section I'm going to talk about how relations work and if they can be
+unassigned instead of being removed.
+
+For many-to-many relationships (e.g. multiple events can be assigned multiple
+tags) Django creates an intermediate table that stores assignments. In this
+case, unassigning event from tag is as simple as removing that stored
+assignment from the intermediate table.
+
+For one-to-many relationships (e.g. multiple events can have the same
+organizer) there's no need for additional table; storing the organizer looks
+like `event.organizer = SomeOrganizer`.
+
+In case of the one-to-many relationships we can unassign the event from
+`SomeOrganizer` if and only if `event.organizer` field can store `NULL` value.
+If it cannot, then we have to remove the event.
+
+So the bug existed because the case of unassignment was not taken into account
+– only removal of related objects was accounted for.
+
+### Fix: need to find out when we can unassign
+
+Long story short: in Django only related manager with `.clear` method can
+unassign; if this method is not present then the only option is removal.
+
+So fixed code looks like this (minus the comments):
+
+{% highlight python %}
+            if value == 'obj_a' and manager != related_a:
+                if hasattr(manager, 'clear'):
+                    manager.clear()
+                else:
+                    manager.all().delete()
+                manager.set(list(related_a.all()))
+
+            elif value == 'obj_b' and manager != related_b:
+                if hasattr(manager, 'clear'):
+                    manager.clear()
+                else:
+                    manager.all().delete()
+                manager.set(list(related_b.all()))
+{% endhighlight %}
+
+(Yes, it probably should use `try - except` block instead of `hasattr`; pull
+request's welcome!)
+
+## Final words
+
+All in all, I feel good about this bug; if anything, I'd like eliminate the
+errorneous timezone arithmetics.
+
+Also all backup mechanics and logging worked really nice.
+
+As a result of investigation described above, the bug and the solution to it
+last night I released AMY [v1.5.2][].
+
+[v1.5.2]: https://github.com/swcarpentry/amy/milestones/v1.5.2
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.5.3.md b/docs/releases/v1.5.3.md
new file mode 100644
index 000000000..b8bec2bb4
--- /dev/null
+++ b/docs/releases/v1.5.3.md
@@ -0,0 +1,23 @@
+# AMY release v1.5.3
+
+A minor version [v1.5.3][] of AMY was released.
+
+## New features
+
+Now it's easier to add person to the database if they already submitted
+a profile update request.
+
+This is specifically useful for admins if they want to add one person and can
+contact them to get more details (like affiliation or airport).
+
+## Bug fixes
+
+* [Aditya][] fixed Django template tags autoescaping on the revision
+  page (ie. each page the changes log links to)
+* [Aditya][] again fixed "Update from URL" functionality that didn't
+  update event's URL in specific conditions.
+
+Thanks a lot, Aditya!
+
+[v1.5.3]: https://github.com/swcarpentry/amy/milestones/v1.5.3
+[Aditya]: https://github.com/narayanaditya95
diff --git a/docs/releases/v1.5.4.md b/docs/releases/v1.5.4.md
new file mode 100644
index 000000000..c1a5cfc55
--- /dev/null
+++ b/docs/releases/v1.5.4.md
@@ -0,0 +1,46 @@
+# AMY release v1.5.4
+
+Yesterday AMY [v1.5.4][] was released with a bunch of interesting changes.
+
+
+## New features
+
+* AMY is now capable of going through all active workshops and checking if
+  their metadata (slug, start/end date, instructors and helpers) had changed.
+  If so, a notification would be shown to the person associated with the event.
+* [Aditya][] improved history log by enabling it to show related
+  objects' real names instead of IDs.
+* [Greg][] added a button to mail everyone involved in a workshop
+* as part of his GSoC 2016 project, [Chris][] added the trainings
+  dashboard in its first shape
+* [Chris][] in collab with [Greg][] added SWC/DC instructor
+  badge indicators to: all persons, event details, and "find instructors" views
+* [Piotr][] upgraded the "Find instructors" view to enable admins to search
+  for not only instructors, but also in-progress instructor trainees and people
+  who once had been associated with the workshop organization. Therefore the
+  new name for "Finding instructors" is now "Find Workshop Staff".
+
+
+## Bug fixes
+
+* [Aditya][] fixed permissions issue when accessing event details page
+  by people without permission to add ToDo items.
+* [Piotr][] fixed a small error preventing DataCarpentry logo from showing up on DC
+  workshop request page.
+* [Piotr][] fixed a small error doubling people with both superuser and `admin` group
+  permissions in the admin lookup backend.
+* Even smaller error was pointing admins to use wrong URL in import event
+  template.  It is now fixed.
+* [Chris][] fixed the former "debriefing" view (now
+  "instructors by date") errors concerning emails generation when some people's
+  emails were unavailable.
+* [Chris][] fixed probably the oldest unnoticed issue ever: wrong link
+  generated for airport's IATA code.
+* [Chris][] fixed missing template from one of the new
+  features for this release.
+
+[v1.5.4]: https://github.com/swcarpentry/amy/milestones/v1.5.4
+[Aditya]: https://github.com/narayanaditya95
+[Chris]: https://github.com/chrismedrela
+[Greg]: https://github.com/gvwilson
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.6.0.md b/docs/releases/v1.6.0.md
new file mode 100644
index 000000000..6624f6650
--- /dev/null
+++ b/docs/releases/v1.6.0.md
@@ -0,0 +1,75 @@
+# AMY release v1.6.0
+
+After about 12 days of delay and 7 days of postponing, we finally closed
+and released AMY [v1.6.0][]. It packs a whole lot of changes and bugfixes!
+
+
+## New features
+
+* [Piotr][] implemented a Data-Carpentry form for submitting requests for running
+  self-organized workshops.
+* [Piotr][] added a histogram into frequency of instructors teaching report page.
+* [Aditya][] added "Contact all" button on the all persons page.
+* [Aditya][] continued [W. Trevor King][]'s work on the `Language`
+  model and now we can accurately track languages amongst multiple forms and
+  related models (e.g. events and persons).
+* [Aditya][] added a summary of tasks per role on person's details
+  page.
+* [Chris][] added an application form for individuals wanting to become
+  instructors.
+* [Piotr][] added `Language` support in additional forms (original PR was missing
+  language support in some forms).
+* **Big**: [Chris][] worked hard to bring GitHub authentication into
+  AMY (with success!).  There are some caveats, but we'll smoothen them out for
+  the next release. This work included opening AMY to other users (a move we
+  were afraid of), and tests for each and every test to ensure we got the
+  permissions right.
+* In the same PR, [Chris][] added an `AutoUpdateProfileForm` used by
+  users (who can log in from GitHub) to self-populate their profiles.
+* [Aditya][] defined sorting of tasks on the person's details page.
+
+
+## Bug fixes
+
+* [Piotr][] fixed a bug that caused `IntegrityError` when people with similar tasks
+  (task has a `role`, `person`, and an `event`; tasks for these people were the
+  same except `person` was different) were being merged.  `IntegrityError`
+  means that a uniqueness constraint was violated (ie. after the merge there
+  were two `Task(role, personA, event)`, which is prohibited).
+* [Chris][] fixed interpolation on some of our charts that looked like
+  the data was swinging, while in reality it wasn't.
+* [Aditya][] fixed default field values on the
+  "All activities over time" page; now the fields have meaningful default
+  values and the datetime inputs have a proper calendar widget.
+* [Aditya][] reworked teaching frequency report to eliminate bug that
+  duplicated numbers for people simultaneously marked as SWC and DC
+  instructors.
+* [Piotr][] fixed some corner cases in event validation (behavior for required or
+  optional tags/metadata (see below)).
+* [Piotr][] fixed a bug resulting in `500 Server error` when accessing weblink to
+  a non-existing `Host`.
+* [Chris][] added one small migrations missing from the codebase.
+* [Greg][] fixed a bug in API that prevented `list` from working on the
+  generator objects for some renderers (`CSV` and `Yaml`).
+* [Prerit Garg][] fixed a specific bug preventing saving a permissions form
+  when person's email field is empty.
+* [Chris][] fixed a `TrainingRequest` form that display additional
+  fields (that weren't supposed to appear).
+
+
+## Other
+
+* [Chris][] refactored "tags" to "metadata"; tags as key-value pairs
+  describing workshops' date, times, location, instructors and helpers. We
+  changed the naming to "metadata" to not confuse with `Tag` model.
+* [Chris][] sped up our tests by changing hashing algorithm to a slower
+  one, which -- surprisingly -- is one of suggested test speedup suggestions by
+  Django development team.
+
+[v1.6.0]: https://github.com/swcarpentry/amy/milestones/v1.6
+[Aditya]: https://github.com/narayanaditya95
+[Chris]: https://github.com/chrismedrela
+[Greg]: https://github.com/gvwilson
+[W. Trevor King]: https://github.com/wking
+[Prerit Garg]: https://github.com/prerit2010
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.6.1.md b/docs/releases/v1.6.1.md
new file mode 100644
index 000000000..0e564667e
--- /dev/null
+++ b/docs/releases/v1.6.1.md
@@ -0,0 +1,47 @@
+# AMY release v1.6.1
+
+We're taking momentum! Two days after [v1.6.0][] release, we're releasing a minor
+bug-fix version [v1.6.1][] which is not as small as you might think.
+
+
+## New features
+
+* [Aditya][] changed the default value for `invoice status` field for
+  events to "Not invoiced" (it was: "unknown").
+* [Piotr][] added a link to the login form on the logout page.  In future, we're going
+  to [redirect to the login page with a message](https://github.com/swcarpentry/amy/issues/867),
+  but we're waiting for Django to release a feature that will allow us to do
+  this easily.
+* [Piotr][] restyled login page so that it's clearer that people can use user+password
+  OR GitHub account to log into AMY.
+
+
+## Bug fixes
+
+* [Chris][] provided tests that make sure we don't have bugs associated
+  with saving M2M-related objects in an `AutoProfileUpdateForm`.
+* [Piotr][] added a link to the profile view page in the top navigation bar.  This
+  links to a `trainee-dashboard` page if current user is not an admin, and to
+  a `person-details` page otherwise.
+* [Chris][] fixed indentation of lists when they're placed inside of
+  tables.
+* [Chris][] added clickable links in some help texts in the
+  [training request form](https://amy.software-carpentry.org/workshops/request_training/).
+* [Chris][] fixed wording in one field of the aforementioned form.
+* [Piotr][] added a missing migration (we commonly forget to add migrations when
+  there are small changes introduced).
+
+
+## Other
+
+* [Aditya][] changed some text fields in AMY's models so that they
+  cannot be equal to a `NULL` (or `None`) value.  Instead an empty string is
+  used for these fields' default values.  Some fields, especially ones with
+  a uniqueness constraint, had to be left as nullable.  In particular, this
+  makes the `Event.slug` a required field.
+
+[v1.6.0]: https://github.com/swcarpentry/amy/milestones/v1.6
+[v1.6.1]: https://github.com/swcarpentry/amy/milestones/v1.6.1
+[Aditya]: https://github.com/narayanaditya95
+[Chris]: https://github.com/chrismedrela
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.6.2.md b/docs/releases/v1.6.2.md
new file mode 100644
index 000000000..6cc50e438
--- /dev/null
+++ b/docs/releases/v1.6.2.md
@@ -0,0 +1,28 @@
+# AMY release v1.6.2
+
+Whoa, another one?! Yesterday we released [v1.6.1][], today it's time for
+[v1.6.2][] with some very minor changes.
+
+## New features
+
+* New fields in the training request form:
+    * `group name` will enable us to register groups for the training, without
+      (for now) the need for a new form
+    * `comment` will be a place for any additional information; instead of it,
+      people would use `additional skills`.
+* `Event.slug` received new help text containing a format description for
+  admins to use.  This field's validation was also changed so that it only
+  allows entries in this specific format (this is additional to other
+  validation done by Django, ie. only latin characters, digits, underscores and
+  hyphens allowed).
+
+## Bug fixes
+
+* Migration `0088*`, which was supposed to generate fake slugs for events
+  without them, contained an error that we hit in the production, so [Piotr][] fixed it
+  by adding random characters to the slugs if uniqueness constraint was about
+  to be violated.
+
+[v1.6.1]: https://github.com/swcarpentry/amy/milestones/v1.6.1
+[v1.6.2]: https://github.com/swcarpentry/amy/milestones/v1.6.2
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.7.0.md b/docs/releases/v1.7.0.md
new file mode 100644
index 000000000..b8f2b7d80
--- /dev/null
+++ b/docs/releases/v1.7.0.md
@@ -0,0 +1,56 @@
+# AMY release v1.7.0
+
+After another two weeks of development and two weeks of delays, we're finally
+releasing AMY [v1.7.0][]
+
+This release is especially interesting since:
+
+1. it includes mostly Aditya's and Chris' PRs
+2. it includes two big PRs containing the biggest part of Aditya's and Chris'
+   Summer projects.
+
+## New features
+
+* [Chris][] helped check for missing migrations in automated continuous
+  integration service [Travis-CI](https://travis-ci.org/)
+* [Chris][] sped up Travis-CI checks of AMY's test suite by using
+  a cache directory
+* [Aditya][] as part of his
+  [Summer work](https://blog.narayanaditya.in/) added titles and URLs to task
+  objects in AMY (useful feature for PyData conference integration)
+* [Aditya][] changed form for creating new events so that admins can
+  assign themselves to a new event while creating it
+* [Aditya][] added a `Sponsorship` model to AMY and integrated it with
+  AMY (we can now track sponsors for events)
+* [Aditya][] migrated `Host` to `Organization`: it fixed some naming
+  inconsistencies
+* in v1.6 we dropped support for numerical event IDs to rely only on slugs
+  (e.g. `2016-08-13-Krakow` or `2017-01-xx-Boston`), now [Aditya][]
+  cleaned some remains left in the code from before dropping the support
+* [Piotr][] added support for `cancelled` tag used to mark events supposed to happen
+  but not happening eventually
+* [Chris][] added instructor training workflow, ie. huge part of AMY
+  used for instructor training
+* [Aditya][] added a feature for importing people, events, tasks from
+  PyData conference site in a comfortable way
+
+## Bug fixes
+
+* [Chris][] tracked and fixed an error in part of AMY responsible for
+  allowing users to log in with other credentials than user/password
+  (currently: GitHub login)
+* [Piotr][] fixed an API error occuring in some views (endpoints) when using CSV or
+  YAML return format
+* [Chris][] added access to AMY for people in invoicing group
+* [Chris][] replaced entity `&mdash;` with actual char `—`
+* [Aditya][] added a contact field on `Sponsorship` model
+* [Chris][] fixed issue with user social integration with GitHub
+  getting out of sync
+* [Piotr][] fixed JavaScript code responsible for generating dates (it was generating
+  e.g. `2016-8-3`, it's now generating `2016-08-03`)
+
+[v1.7.0]: https://github.com/swcarpentry/amy/milestone/28
+[v1.7.1]: https://github.com/swcarpentry/amy/milestone/33
+[Chris]: https://github.com/chrismedrela
+[Aditya]: https://github.com/narayanaditya95
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.7.1.md b/docs/releases/v1.7.1.md
new file mode 100644
index 000000000..6f8f30ebd
--- /dev/null
+++ b/docs/releases/v1.7.1.md
@@ -0,0 +1,27 @@
+# AMY bugfix release v1.7.1
+
+This release contains mostly bug fixes for features we added in [v1.7.0][].
+
+## New features
+
+* [Chris][] added a command line tool for importing trainees progress
+  from previous data format into AMY
+
+## Bug fixes
+
+* [Chris][] removed an overlooked debugging message alert in one of the
+  views
+* [Aditya][] added a cancel button to almost all the forms in AMY
+* [Piotr][] added a message to "Apply for Instructor Training" page saying that people
+  cannot register for Fall 2016 open-access training anymore
+* [Aditya][] fixed "Import from URL" not working on workshop acceptance
+  page
+* [Chris][] fixed some validation issue in one of training-related
+  forms
+* [Chris][] added access to admin dashboard in AMY to trainers
+
+[v1.7.0]: https://github.com/swcarpentry/amy/milestone/28
+[v1.7.1]: https://github.com/swcarpentry/amy/milestone/33
+[Chris]: https://github.com/chrismedrela
+[Aditya]: https://github.com/narayanaditya95
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.7.2.md b/docs/releases/v1.7.2.md
new file mode 100644
index 000000000..930a1a7c1
--- /dev/null
+++ b/docs/releases/v1.7.2.md
@@ -0,0 +1,11 @@
+# AMY bugfix release v1.7.2
+
+AMY [v1.7.2][] was released today. It contains one bug fix provided by
+[Aditya][].
+
+Aditya fixed a bug throwing 500 HTTP error when accessing
+`/api/v1/todos/user/`.  This API endpoint is being accessed by the browser
+whenever any admin user loads their dashboard.
+
+[v1.7.2]: https://github.com/swcarpentry/amy/milestone/34
+[Aditya]: https://github.com/narayanaditya95
diff --git a/docs/releases/v1.8.0.md b/docs/releases/v1.8.0.md
new file mode 100644
index 000000000..ec89c4d89
--- /dev/null
+++ b/docs/releases/v1.8.0.md
@@ -0,0 +1,46 @@
+# AMY release v1.8.0
+
+Major AMY [v1.8.0][] release was tagged. As you can see below, it was
+definitely focused on fixing bugs.
+
+
+## New Features
+
+* [Aditya][] provided a template change that displays link between closed
+  workshop request and corresponding event.
+* [Aditya][] hid survey-related fields on Event-related forms.
+* [Chris][] sped up (again :-) ) tests.
+* [Chris][] removed unnecessary help text for autocompletion fields.
+* [Aditya][] refactored delete views to use `DeleteViewContext`, essentially
+  making code more DRY and easy to change.
+* [Piotr][] added deleting entries from bulk-upload feature.
+* [Piotr][] updated [DataCarpentry](http://datacarpentry.org) self-organized workshops
+  registration form.
+
+
+## Bugfixes
+
+* [Aditya][] changed uniqueness constraints on `Sponsorship` model to reflect
+  recent changes he made on that model.
+* [Aditya][] changed display of some `Membership` model fields.
+* [Aditya][] added missing CSRF tokens in PyData import page.
+* [Chris][] fixed a rare case of email address leakage (CC instead of BCC) in
+  event details page, instructors by date and in workshop staff finder.
+* [Aditya][] changed a uniqueness constraint on `Task` model + added some other
+  small improvements.
+* [Chris][] fixed non-working links and corrected ordering in all trainings
+  page.
+* [Aditya][] refactored internal URLs file to use nested URLs structure and
+  therefore made it a lot more readable.
+* [Chris][] made "progress" column in trainees view wider
+* [Aditya][] hid from import instances that were decided not to be imported
+* [Piotr][] fixed error message on faulty bulk-upload process.
+* [Piotr][] fixed a double-display of unpublished and published views in very specific
+  circumstances.
+* [Piotr][] stopped counting in unresponsive workshops in workshops issues page.
+
+
+[v1.8.0]: https://github.com/swcarpentry/amy/milestone/32
+[Aditya]: https://github.com/narayanaditya95
+[Chris]: https://github.com/chrismedrela
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/docs/releases/v1.9.1.md b/docs/releases/v1.9.1.md
new file mode 100644
index 000000000..d585f1d3f
--- /dev/null
+++ b/docs/releases/v1.9.1.md
@@ -0,0 +1,27 @@
+# AMY bugfix release v1.9.1
+
+AMY [v1.9.1][], intended to fix a major bug in `/forms/request_training/`,
+incorporates a number of development-related changes that were scheduled for
+[v1.10.0][].
+
+## New development features
+
+* [Chris][] Improved formatting in `ReportsViewSet`.
+* [Aditya][] updated settings to work with local development (a change required
+  by Django bugfix).
+* [Aditya][] changed Travis-CI configuration to work correctly with PyData
+  installation; especially he managed to set up a test matrix for checking code
+  with PyData installed and without.
+* [Aditya][] refactored views so that a prepopulation of forms would be easier
+  to achieve for developers.
+
+## Bugfixes
+
+* [Piotr][] fixed a bug in training request form, which prevented users from
+  seeing a confirmation of their request.
+
+[v1.9.1]: https://github.com/swcarpentry/amy/milestone/38
+[v1.10.0]: https://github.com/swcarpentry/amy/milestone/37
+[Aditya]: https://github.com/narayanaditya95
+[Chris]: https://github.com/chrismedrela
+[Piotr]: https://github.com/pbanaszkiewicz
diff --git a/extforms/views.py b/extforms/views.py
index 58553b982..8382a3e23 100644
--- a/extforms/views.py
+++ b/extforms/views.py
@@ -1,5 +1,6 @@
 from django.conf import settings
-from django.core.urlresolvers import reverse, reverse_lazy
+from django.contrib import messages
+from django.core.urlresolvers import reverse_lazy
 from django.shortcuts import render
 from django.template.loader import get_template
 from django.views.generic import TemplateView

Import old release notes
Add them to `docs/releases` dictionary. Get old release notes from [Piotr's blog](http://piotr.banaszkiewicz.org/).

It'd be better to keep release notes along with source code, so that they won't get lost, and to have everything in one place. @pbanaszkiewicz what's your opinion?
