diff --git a/finances/forms.py b/finances/forms.py
index dd99b82bb5aa501a3b23097fbcace9a0a56e4350..36f2907125b04a5328016a652614a75f37e919b9 100644
--- a/finances/forms.py
+++ b/finances/forms.py
@@ -1,16 +1,69 @@
+import datetime
+
 from django import forms
+from django.contrib.auth import get_user_model
+from django.utils.dates import MONTHS
+from django.db.models import Sum
 
 from .models import WorkLog
 
+today = datetime.datetime.today()
+
 
 class WorkLogForm(forms.ModelForm):
+    def __init__(self, *args, **kwargs):
+        self.types = kwargs.pop('log_types', False)
+        super().__init__(*args, **kwargs)
+        if self.types:
+            self.fields['log_type'] = forms.ChoiceField(choices=self.types)
+
     class Meta:
         model = WorkLog
         fields = (
             'comments',
+            'log_type',
             'duration',
         )
         widgets = {
             'comments': forms.Textarea(attrs={'rows': 4}),
             'duration': forms.TextInput(attrs={'placeholder': 'HH:MM:SS'})
         }
+
+
+class LogsMonthlyActiveFilter(forms.Form):
+    month = forms.ChoiceField(choices=[(k, v) for k, v in MONTHS.items()])
+    year = forms.ChoiceField(choices=[(y, y) for y in reversed(range(today.year-6, today.year+1))])
+
+    def __init__(self, *args, **kwargs):
+        if not kwargs.get('data', False) and not args[0]:
+            args = list(args)
+            args[0] = {
+                'month': today.month,
+                'year': today.year
+            }
+            args = tuple(args)
+        kwargs['initial'] = {
+            'month': today.month,
+            'year': today.year
+        }
+        super().__init__(*args, **kwargs)
+
+    def get_totals(self):
+        # Make accessible without need to explicitly check validity of form.
+        self.is_valid()
+
+        users = get_user_model().objects.filter(
+            work_logs__work_date__month=self.cleaned_data['month'],
+            work_logs__work_date__year=self.cleaned_data['year']).distinct()
+        output = []
+        for user in users:
+            logs = user.work_logs.filter(
+                work_date__month=self.cleaned_data['month'],
+                work_date__year=self.cleaned_data['year'])
+            output.append({
+                'logs': logs,
+                'duration': logs.aggregate(total=Sum('duration')),
+                'user': user
+            })
+
+        return output
diff --git a/finances/migrations/0002_auto_20171007_1349.py b/finances/migrations/0002_auto_20171007_1349.py
index abab65056fe0b8b637687d7b06d3444086eed0a9..01d4dcaf94b2c414dfe25f2b5ddc40088e2aecbf 100644
--- a/finances/migrations/0002_auto_20171007_1349.py
+++ b/finances/migrations/0002_auto_20171007_1349.py
@@ -5,35 +5,6 @@ from __future__ import unicode_literals
 from django.db import migrations
 
 
-def move_hour_registrations(apps, schema_editor):
-    """
-    Move all ProductionEvent hours to Finances model.
-    """
-    ProductionEvent = apps.get_model('production', 'ProductionEvent')
-    WorkLog = apps.get_model('finances', 'WorkLog')
-    ContentType = apps.get_model('contenttypes', 'ContentType')
-    stream_content_type = ContentType.objects.get(app_label='production', model='productionstream')
-
-    for event in ProductionEvent.objects.filter(duration__isnull=False):
-        log = WorkLog(
-            user=event.noted_by.user,
-            comments=event.comments,
-            duration=event.duration,
-            work_date=event.noted_on,
-            target_content_type=stream_content_type,
-            target_object_id=event.stream.id
-        )
-        log.save()
-    return
-
-
-def move_hour_registrations_inverse(apps, schema_editor):
-    """
-    Move all ProductionEvent hours to Finances model inversed (not implemented).
-    """
-    return
-
-
 class Migration(migrations.Migration):
 
     dependencies = [
@@ -41,6 +12,4 @@ class Migration(migrations.Migration):
         ('production', '0028_auto_20171007_1311'),
     ]
 
-    operations = [
-        migrations.RunPython(move_hour_registrations, move_hour_registrations_inverse)
-    ]
+    operations = []
diff --git a/finances/migrations/0005_auto_20171010_0921.py b/finances/migrations/0005_auto_20171010_0921.py
new file mode 100644
index 0000000000000000000000000000000000000000..c3ffa146363d51e1864ceca81cb91530799c0d09
--- /dev/null
+++ b/finances/migrations/0005_auto_20171010_0921.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-10 07:21
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('finances', '0004_auto_20171007_1426'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='worklog',
+            options={'ordering': ['-work_date', 'created']},
+        ),
+        migrations.AddField(
+            model_name='worklog',
+            name='log_type',
+            field=models.CharField(blank=True, max_length=128),
+        ),
+    ]
diff --git a/finances/migrations/0006_auto_20171010_1003.py b/finances/migrations/0006_auto_20171010_1003.py
new file mode 100644
index 0000000000000000000000000000000000000000..2789ccec488ef8f80370306548c28cdda16eaf2c
--- /dev/null
+++ b/finances/migrations/0006_auto_20171010_1003.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-10 08:03
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+def move_hour_registrations(apps, schema_editor):
+    """
+    Move all ProductionEvent hours to Finances model.
+    """
+    ProductionEvent = apps.get_model('production', 'ProductionEvent')
+    WorkLog = apps.get_model('finances', 'WorkLog')
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    stream_content_type = ContentType.objects.get(app_label='production', model='productionstream')
+
+    for event in ProductionEvent.objects.filter(duration__isnull=False):
+        if event.event in ['assigned_to_supervisor',
+                           'message_edadmin_to_supervisor',
+                           'message_supervisor_to_edadmin',
+                           'officer_tasked_with_proof_production',
+                           'message_supervisor_to_officer',
+                           'proofs_checked_by_supervisor',
+                           'proofs_returned_by_authors',
+                           'proofs_returned_by_authors',
+                           'authors_have_accepted_proofs',
+                           'paper_published']:
+            _type = 'Production: Supervisory tasks'
+        else:
+            _type = 'Production: Production Officer tasks'
+
+        log = WorkLog(
+            user=event.noted_by.user,
+            log_type=_type,
+            comments=event.comments,
+            duration=event.duration,
+            work_date=event.noted_on,
+            content_type=stream_content_type,
+            object_id=event.stream.id
+        )
+        log.save()
+    return
+
+
+def move_hour_registrations_inverse(apps, schema_editor):
+    """
+    Move all ProductionEvent hours to Finances model inversed (not implemented).
+    """
+    return
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('finances', '0005_auto_20171010_0921'),
+    ]
+
+    operations = [
+        migrations.RunPython(move_hour_registrations, move_hour_registrations_inverse)
+    ]
diff --git a/finances/models.py b/finances/models.py
index d4c8622fd8b3ac23b346343343ee41f2b3244b0e..3982eb14a3e5b0536d504ba4ef13ffa4103d95a6 100644
--- a/finances/models.py
+++ b/finances/models.py
@@ -10,6 +10,7 @@ from .utils import id_to_slug
 class WorkLog(models.Model):
     user = models.ForeignKey(settings.AUTH_USER_MODEL)
     comments = models.TextField(blank=True)
+    log_type = models.CharField(max_length=128, blank=True)
     duration = models.DurationField(blank=True, null=True)
     work_date = models.DateField(default=timezone.now)
     created = models.DateTimeField(auto_now_add=True)
@@ -20,6 +21,7 @@ class WorkLog(models.Model):
 
     class Meta:
         default_related_name = 'work_logs'
+        ordering = ['-work_date', 'created']
 
     def __str__(self):
         return 'Log of {0} {1} on {2}'.format(
diff --git a/finances/templates/finances/timesheets.html b/finances/templates/finances/timesheets.html
index 63d43519a766bf7535bedf40f08346554eb7c837..f32ae41dfa46ac45e19d96ffca47e1e1e01e030f 100644
--- a/finances/templates/finances/timesheets.html
+++ b/finances/templates/finances/timesheets.html
@@ -7,6 +7,7 @@
 {% block pagetitle %}: Team Timesheets{% endblock pagetitle %}
 
 {% load bootstrap %}
+{% load scipost_extras %}
 
 {% block content %}
 
@@ -25,26 +26,25 @@
     <div class="col-12">
         <h2 class="mb-2 mt-4">Team Timesheets</h2>
         {% for total in totals %}
-            <h3 class="mb-1">{{ total.user }}</h3>
+            <h3 class="mb-1">{{ total.user.first_name }} {{ total.user.last_name }}</h3>
             <table class="table">
                 <thead class="thead-default">
                   <tr>
                       <th>Date</th>
-                      <th>By</th>
-                      <th>Stream</th>
-                      <th>Event</th>
+                      <th>Related to object</th>
+                      <th>Log type</th>
+                      <th>Comments</th>
                       <th>Duration</th>
                   </tr>
                 </thead>
-
                 <tbody>
-                    {% for event in total.events %}
+                    {% for log in total.logs %}
                         <tr>
-                            <td>{{ event.noted_on }}</td>
-                            <td>{{ event.noted_by }}</td>
-                            <td>{{ event.stream }}</td>
-                            <td>{{ event.get_event_display }}</td>
-                            <td>{{ event.duration }}</td>
+                            <td>{{ log.work_date }}</td>
+                            <td>{{ log.content }}</td>
+                            <td>{{ log.log_type }}</td>
+                            <td>{{ log.comments }}</td>
+                            <td>{{ log.duration|duration }}</td>
                         </tr>
                     {% endfor %}
                     <tr>
diff --git a/finances/templates/partials/finances/logs.html b/finances/templates/partials/finances/logs.html
index 7b9db48220dfd2ed74781910031b994031df1ce8..956a07bf576138d2f52963462326ff17212f3c56 100644
--- a/finances/templates/partials/finances/logs.html
+++ b/finances/templates/partials/finances/logs.html
@@ -7,6 +7,8 @@
                 <div>
                     <strong>{{ log.user.first_name }} {{ log.user.last_name }}</strong>
                     <br>
+                    <span class="text-muted">{{ log.log_type }}</span>
+                    <br>
                     {{ log.comments }}
                 </div>
                 <div class="text-muted text-right d-flex justify-content-end">
@@ -25,10 +27,3 @@
         <li>No logs were found.</li>
     {% endfor %}
 </ul>
-{% comment %}
-user = models.ForeignKey(settings.AUTH_USER_MODEL)
-comments = models.TextField(blank=True)
-duration = models.DurationField(blank=True, null=True)
-work_date = models.DateField(default=timezone.now)
-created = models.DateTimeField(auto_now_add=True)
-{% endcomment %}
diff --git a/finances/views.py b/finances/views.py
index f235ef16abcdc2b786b14fb70eb69e69a4566182..33200e7c2eb3a10a9c2b6f905960d0cb497583f9 100644
--- a/finances/views.py
+++ b/finances/views.py
@@ -4,8 +4,7 @@ from django.http import Http404
 from django.shortcuts import render
 from django.views.generic.edit import DeleteView
 
-from production.forms import ProductionUserMonthlyActiveFilter
-
+from .forms import LogsMonthlyActiveFilter
 from .models import WorkLog
 from .utils import slug_to_id
 
@@ -15,7 +14,7 @@ def timesheets(request):
     """
     See an overview per month of all timesheets.
     """
-    form = ProductionUserMonthlyActiveFilter(request.GET or None)
+    form = LogsMonthlyActiveFilter(request.GET or None)
     context = {
         'form': form,
     }
diff --git a/mails/forms.py b/mails/forms.py
index 2c34d6453d48abdd7803fb433788d84e8040437c..9e8b9fbeb840877dbb04aaf6a869e715396e8cf7 100644
--- a/mails/forms.py
+++ b/mails/forms.py
@@ -1,10 +1,14 @@
 import json
+import inspect
 
 from django import forms
 from django.core.mail import EmailMultiAlternatives
+from django.contrib.auth import get_user_model
 from django.conf import settings
 from django.template import loader
 
+from scipost.models import Contributor
+
 
 class EmailTemplateForm(forms.Form):
     subject = forms.CharField(max_length=250, label="Subject*")
@@ -27,6 +31,8 @@ class EmailTemplateForm(forms.Form):
         recipient = self.object
         for attr in self.mail_data.get('to_address').split('.'):
             recipient = getattr(recipient, attr)
+            if inspect.ismethod(recipient):
+                recipient = recipient()
         self.recipient = recipient
 
         # Set the data as initials
@@ -39,15 +45,42 @@ class EmailTemplateForm(forms.Form):
         html_template = loader.get_template('email/general.html')
         html_message = html_template.render({'text': message})
 
-        # Get recipients list. Always send through BCC to prevent privacy issues!
-        bcc_to = self.object
-        for attr in self.mail_data.get('bcc_to').split('.'):
-            bcc_to = getattr(bcc_to, attr)
-        bcc_list = [
-            bcc_to,
-        ]
-        if self.cleaned_data.get('additional_bcc'):
-            bcc_list.append(self.cleaned_data.get('additional_bcc'))
+        # Get recipients list. Try to send through BCC to prevent privacy issues!
+        bcc_list = []
+        if self.mail_data.get('bcc_to'):
+            bcc_to = self.object
+            for attr in self.mail_data.get('bcc_to').split('.'):
+                bcc_to = getattr(bcc_to, attr)
+
+            if not isinstance(bcc_to, list):
+                bcc_list = [bcc_to]
+            else:
+                bcc_list = bcc_to
+
+        if self.cleaned_data.get('extra_recipient'):
+            bcc_list.append(self.cleaned_data.get('extra_recipient'))
+
+        # Check the send list
+        if isinstance(self.recipient, list):
+            recipients = self.recipient
+        elif not isinstance(self.recipient, str):
+            try:
+                recipients = list(self.recipient)
+            except TypeError:
+                recipients = [self.recipient]
+        else:
+            recipients = [self.recipient]
+        recipients = list(recipients)
+
+        # Check if email needs to be taken from instance
+        _recipients = []
+        for recipient in recipients:
+            if isinstance(recipient, Contributor):
+                _recipients.append(recipient.user.email)
+            elif isinstance(recipient, get_user_model()):
+                _recipients.append(recipient.email)
+            elif isinstance(recipient, str):
+                _recipients.append(recipient)
 
         # Send the mail
         email = EmailMultiAlternatives(
@@ -55,7 +88,7 @@ class EmailTemplateForm(forms.Form):
             message,
             '%s <%s>' % (self.mail_data.get('from_address_name', 'SciPost'),
                          self.mail_data.get('from_address', 'no-reply@scipost.org')),  # From
-            [self.recipient],  # To
+            _recipients,  # To
             bcc=bcc_list,
             reply_to=[self.mail_data.get('from_address', 'no-reply@scipost.org')])
         email.attach_alternative(html_message, 'text/html')
diff --git a/mails/templates/mail_templates/production_send_proofs.json b/mails/templates/mail_templates/production_send_proofs.json
new file mode 100644
index 0000000000000000000000000000000000000000..5e38b3a6e8bb1f858e66805c7cfd09da77e93913
--- /dev/null
+++ b/mails/templates/mail_templates/production_send_proofs.json
@@ -0,0 +1,7 @@
+{
+    "subject": "SciPost: Your proofs have been produced",
+    "to_address": "stream.submission.authors.all",
+    "from_address_name": "SciPost Production",
+    "from_address": "proofs@scipost.org",
+    "context_object": "proofs"
+}
diff --git a/mails/templates/mail_templates/production_send_proofs.txt b/mails/templates/mail_templates/production_send_proofs.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d6cbda22c323b76397e3a18668aa1211185668cd
--- /dev/null
+++ b/mails/templates/mail_templates/production_send_proofs.txt
@@ -0,0 +1,11 @@
+Dear {% for author in proofs.stream.submission.authors.all %}{{ author.get_title_display }} {{ author.user.last_name }}{% if not forloop.last %}, {% elif proofs.stream.submission.authors.count > 1 %} and {% endif %}{% endfor %},
+
+The SciPost production team has finished the proofs of your manuscript (version {{ proofs.version }}). You can find the proofs on your submission's page (see https://scipost.org{{ proofs.stream.submission.get_absolute_url }}).
+
+Please review the proofs and let us know whether you accept the proofs for publication using the form on the submission page.
+
+
+Sincerely,
+
+{{ proofs.stream.supervisor.user.first_name }} {{ proofs.stream.supervisor.user.last_name }}
+SciPost Production
diff --git a/production/constants.py b/production/constants.py
index e37691c7a445b05bc6081a2c7ca3adacc5e3eee9..8c7f309dc63976aa7c03dca48b7b41fc61380a3f 100644
--- a/production/constants.py
+++ b/production/constants.py
@@ -47,3 +47,11 @@ PROOFS_STATUSES = (
     (PROOFS_DECLINED, 'Proofs declined by authors'),
     (PROOFS_RENEWED, 'Proofs renewed'),
 )
+
+PRODUCTION_OFFICERS_WORK_LOG_TYPES = (
+    ('Production: Production Officer tasks', 'Production Officer tasks'),
+)
+PRODUCTION_ALL_WORK_LOG_TYPES = (
+    ('Production: Supervisory tasks', 'Supervisory tasks'),
+    ('Production: Production Officer tasks', 'Production Officer tasks'),
+)
diff --git a/production/forms.py b/production/forms.py
index bec9a69c289a2564b7ddebc66efc1fb7eb894b2d..1768d6d12d07e8e59cf64f72db7e501dea1d64ae 100644
--- a/production/forms.py
+++ b/production/forms.py
@@ -1,8 +1,6 @@
 import datetime
 
 from django import forms
-from django.utils.dates import MONTHS
-from django.db.models import Sum
 
 from . import constants
 from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs
@@ -136,46 +134,6 @@ class UserToOfficerForm(forms.ModelForm):
             production_user__isnull=True).order_by('last_name')
 
 
-class ProductionUserMonthlyActiveFilter(forms.Form):
-    month = forms.ChoiceField(choices=[(k, v) for k, v in MONTHS.items()])
-    year = forms.ChoiceField(choices=[(y, y) for y in reversed(range(today.year-6, today.year+1))])
-
-    def __init__(self, *args, **kwargs):
-        if not kwargs.get('data', False) and not args[0]:
-            args = list(args)
-            args[0] = {
-                'month': today.month,
-                'year': today.year
-            }
-            args = tuple(args)
-        kwargs['initial'] = {
-            'month': today.month,
-            'year': today.year
-        }
-        super().__init__(*args, **kwargs)
-
-    def get_totals(self):
-        # Make accessible without need to explicitly check validity of form.
-        self.is_valid()
-
-        users = ProductionUser.objects.filter(events__duration__isnull=False,
-                                              events__noted_on__month=self.cleaned_data['month'],
-                                              events__noted_on__year=self.cleaned_data['year']
-                                              ).distinct()
-        output = []
-        for user in users:
-            events = user.events.filter(duration__isnull=False,
-                                        noted_on__month=self.cleaned_data['month'],
-                                        noted_on__year=self.cleaned_data['year'])
-            output.append({
-                'events': events,
-                'duration': events.aggregate(total=Sum('duration')),
-                'user': user
-            })
-
-        return output
-
-
 class ProofsUploadForm(forms.ModelForm):
     class Meta:
         model = Proofs
@@ -215,7 +173,8 @@ class ProofsDecisionForm(forms.ModelForm):
             prodevent = ProductionEvent(
                 stream=proofs.stream,
                 event='status',
-                comments='Received feedback: {comments}'.format(comments=comments),
+                comments='Received feedback from the authors: {comments}'.format(
+                    comments=comments),
                 noted_by=proofs.stream.supervisor
             )
             prodevent.save()
diff --git a/production/migrations/0023_auto_20170930_1759.py b/production/migrations/0023_auto_20170930_1759.py
index 2143c4cdffab851f0d7049f9a09e96c4a2f9fc3e..0f510418996feeeaa5376a1e1af99baba670296c 100644
--- a/production/migrations/0023_auto_20170930_1759.py
+++ b/production/migrations/0023_auto_20170930_1759.py
@@ -5,35 +5,6 @@ from __future__ import unicode_literals
 from django.db import migrations, models
 
 
-def update_status(apps, schema_editor):
-    """
-    Update current Production Event type.
-    """
-    ProductionEvent = apps.get_model('production', 'ProductionEvent')
-    for event in ProductionEvent.objects.all():
-        if event.duration:
-            event.event = 'registration'
-        elif event.event == ['assigned_to_supervisor', 'officer_tasked_with_proof_production']:
-            event.event = 'assignment'
-        elif event.event in ['message_edadmin_to_supervisor', 'message_supervisor_to_edadmin', 'message_supervisor_to_officer', 'message_officer_to_supervisor']:
-            event.event = 'message'
-        else:
-            event.event = 'status'
-        event.save()
-    return
-
-
-def update_status_inverse(apps, schema_editor):
-    """
-    Inverse update current Production Event type. As this mapping is impossible to make,
-    it'll just all be a unique status: `Event`
-    """
-    ProductionEvent = apps.get_model('production', 'ProductionEvent')
-    for event in ProductionEvent.objects.all():
-        event.event = 'Event'
-    return
-
-
 class Migration(migrations.Migration):
 
     dependencies = [
@@ -46,5 +17,4 @@ class Migration(migrations.Migration):
             name='event',
             field=models.CharField(choices=[('assignment', 'Assignment'), ('status', 'Status change'), ('message', 'Message'), ('registration', 'Registration hours')], max_length=64),
         ),
-        migrations.RunPython(update_status, update_status_inverse)
     ]
diff --git a/production/migrations/0031_auto_20171010_0921.py b/production/migrations/0031_auto_20171010_0921.py
new file mode 100644
index 0000000000000000000000000000000000000000..dcf9fe6f6dad9b30fe69346ce5747543fc0a247d
--- /dev/null
+++ b/production/migrations/0031_auto_20171010_0921.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-10 07:21
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('production', '0030_auto_20171009_2111'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='proofs',
+            options={'ordering': ['stream', 'version'], 'verbose_name_plural': 'Proofs'},
+        ),
+    ]
diff --git a/production/migrations/0032_auto_20171010_1008.py b/production/migrations/0032_auto_20171010_1008.py
new file mode 100644
index 0000000000000000000000000000000000000000..e92a693b6487c1f75028d2d3c3ace21afef92dd8
--- /dev/null
+++ b/production/migrations/0032_auto_20171010_1008.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-10-10 08:08
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+def update_status(apps, schema_editor):
+    """
+    Update current Production Event type.
+    """
+    ProductionEvent = apps.get_model('production', 'ProductionEvent')
+    for event in ProductionEvent.objects.all():
+        if event.duration:
+            event.event = 'registration'
+        elif event.event == ['assigned_to_supervisor', 'officer_tasked_with_proof_production']:
+            event.event = 'assignment'
+        elif event.event in ['message_edadmin_to_supervisor', 'message_supervisor_to_edadmin', 'message_supervisor_to_officer', 'message_officer_to_supervisor']:
+            event.event = 'message'
+        else:
+            event.event = 'status'
+        event.save()
+    return
+
+
+def update_status_inverse(apps, schema_editor):
+    """
+    Inverse update current Production Event type. As this mapping is impossible to make,
+    it'll just all be a unique status: `Event`
+    """
+    ProductionEvent = apps.get_model('production', 'ProductionEvent')
+    for event in ProductionEvent.objects.all():
+        event.event = 'Event'
+    return
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('finances', '0006_auto_20171010_1003'),
+        ('production', '0031_auto_20171010_0921'),
+    ]
+
+    operations = [
+        # Do not run this migration,
+        #  the field is U/S anyway and in case it goes wrong we loose data here.
+
+        # migrations.RunPython(update_status, update_status_inverse)
+    ]
diff --git a/production/models.py b/production/models.py
index 3c55f1a91ebd3c88b0a6c54abf78a4905ad59b41..549027075dea4866f62f1cb3cc2d5620cfd60868 100644
--- a/production/models.py
+++ b/production/models.py
@@ -63,7 +63,7 @@ class ProductionStream(models.Model):
 
     @cached_property
     def total_duration(self):
-        totdur = self.events.aggregate(models.Sum('duration'))
+        totdur = self.work_logs.aggregate(models.Sum('duration'))
         return totdur['duration__sum']
 
     @cached_property
diff --git a/production/templates/production/partials/production_events.html b/production/templates/production/partials/production_events.html
index 13b1b0251dd3cbac9fe0a1c524fa349db5ff56ae..653f9c3df4bbf1999172d6fd62d320206a720b41 100644
--- a/production/templates/production/partials/production_events.html
+++ b/production/templates/production/partials/production_events.html
@@ -43,8 +43,3 @@
         <li>No events were found.</li>
     {% endfor %}
 </ul>
-
-{% if stream.total_duration %}
-    <hr>
-    <p class="text-right">Total duration for this stream: <strong>{{ stream.total_duration|duration }}</strong></p>
-{% endif %}
diff --git a/production/templates/production/partials/production_stream_card.html b/production/templates/production/partials/production_stream_card.html
index 7a5470487dd6c901fe07e19d453126247d315d32..85473b2a34db7edfd56af09298e575b9c3a9df72 100644
--- a/production/templates/production/partials/production_stream_card.html
+++ b/production/templates/production/partials/production_stream_card.html
@@ -1,6 +1,7 @@
 {% extends 'production/partials/production_stream_card_completed.html' %}
 
 {% load bootstrap %}
+{% load scipost_extras %}
 
 {% block actions %}
     {% include 'production/partials/stream_status_changes.html' with form=status_form stream=stream %}
@@ -22,7 +23,7 @@
     <ul>
         <li>
             <a href="javascript:;" data-toggle="toggle" data-target="#log_form">Add hours to the Stream</a>
-            <form id="log_form" style="display: none;" action="{% url 'production:add_event' stream_id=stream.id %}" method="post" class="mb-2">
+            <form id="log_form" style="display: none;" action="{% url 'production:add_work_log' stream_id=stream.id %}" method="post" class="mb-2">
                 {% csrf_token %}
                 {{ work_log_form|bootstrap }}
                 <input type="submit" class="btn btn-secondary" name="submit" value="Log">
@@ -33,6 +34,11 @@
 
   {% include 'partials/finances/logs.html' with logs=stream.work_logs.all %}
 
+  {% if stream.total_duration %}
+      <hr>
+      <p class="text-right">Total duration for this stream: <strong>{{ stream.total_duration|duration }}</strong></p>
+  {% endif %}
+
     {% if "can_perform_supervisory_actions" in sub_perms or "can_work_for_stream" in sub_perms %}
           <h3>Actions</h3>
           <ul>
diff --git a/production/templates/production/partials/production_stream_card_completed.html b/production/templates/production/partials/production_stream_card_completed.html
index 99165bd4bdd764038d8433eab2c05aeef933d14c..19eb76bb651c36fc534b62fcd830b29da70ae657 100644
--- a/production/templates/production/partials/production_stream_card_completed.html
+++ b/production/templates/production/partials/production_stream_card_completed.html
@@ -1,5 +1,6 @@
 {% load bootstrap %}
 {% load guardian_tags %}
+{% load scipost_extras %}
 
 {% get_obj_perms request.user for stream as "sub_perms" %}
 
@@ -41,6 +42,11 @@
 
           <h3>Work Log</h3>
           {% include 'partials/finances/logs.html' with logs=stream.work_logs.all %}
+
+            {% if stream.total_duration %}
+                <hr class="sm">
+                <p class="pl-4 ml-3">Total duration for this stream: <strong>{{ stream.total_duration|duration }}</strong></p>
+            {% endif %}
       {% endblock %}
 
     {% if "can_work_for_stream" in sub_perms %}
diff --git a/production/templates/production/production.html b/production/templates/production/production.html
index 80dc89057a589d769bb44cff099e2b418aa82621..844922fd09730361c68469c5a446ac6493eb5201 100644
--- a/production/templates/production/production.html
+++ b/production/templates/production/production.html
@@ -7,6 +7,7 @@
 {% block pagetitle %}: Production page{% endblock pagetitle %}
 
 {% load bootstrap %}
+{% load scipost_extras %}
 
 {% block content %}
 
@@ -113,23 +114,24 @@
       <thead class="thead-default">
 	<tr>
 	  <th>Date</th>
+	  <th>Comment</th>
 	  <th>Stream</th>
-	  <th>Event</th>
+      <th>Log type</th>
 	  <th>Duration</th>
 	</tr>
       </thead>
-
       <tbody role="tablist">
-	{% for event in ownevents %}
-	<tr>
-	  <td>{{ event.noted_on }}</td>
-	  <td>{{ event.stream }}</td>
-	  <td>{{ event.get_event_display }}</td>
-	  <td>{{ event.duration }}</td>
-	</tr>
+	{% for log in request.user.work_logs.all %}
+    	<tr>
+    	  <td>{{ log.work_date }}</td>
+    	  <td>{{ log.comments }}</td>
+    	  <td>{{ log.content }}</td>
+          <td>{{ log.log_type }}</td>
+    	  <td>{{ log.duration|duration }}</td>
+    	</tr>
 	{% empty %}
 	<tr>
-	  <td colspan="4">No events found.</td>
+	  <td colspan="4">No logs found.</td>
 	</tr>
 	{% endfor %}
       </tbody>
diff --git a/production/urls.py b/production/urls.py
index a4c6703083ab5420c1461e5ee3d2c334539e2b3a..c44ef6554601b294b125872607bb94f85bd7dcc5 100644
--- a/production/urls.py
+++ b/production/urls.py
@@ -23,6 +23,8 @@ urlpatterns = [
         production_views.toggle_accessibility, name='toggle_accessibility'),
     url(r'^streams/(?P<stream_id>[0-9]+)/events/add$',
         production_views.add_event, name='add_event'),
+    url(r'^streams/(?P<stream_id>[0-9]+)/logs/add$',
+        production_views.add_work_log, name='add_work_log'),
     url(r'^streams/(?P<stream_id>[0-9]+)/officer/add$',
         production_views.add_officer, name='add_officer'),
     url(r'^streams/(?P<stream_id>[0-9]+)/officer/(?P<officer_id>[0-9]+)/remove$',
diff --git a/production/views.py b/production/views.py
index 83f047c4dc23926191f4d33d9ac78ed7ac3a75d3..49bfa8926e18bd7ba4c338287cb27d08c1c6110d 100644
--- a/production/views.py
+++ b/production/views.py
@@ -16,6 +16,7 @@ from guardian.core import ObjectPermissionChecker
 from guardian.shortcuts import assign_perm, remove_perm
 
 from finances.forms import WorkLogForm
+from mails.views import MailEditingSubView
 
 from . import constants
 from .models import ProductionUser, ProductionStream, ProductionEvent, Proofs
@@ -44,13 +45,8 @@ def production(request, stream_id=None):
         streams = streams.filter_for_user(request.user.production_user)
     streams = streams.order_by('opened')
 
-    ownevents = ProductionEvent.objects.filter(
-        noted_by=request.user.production_user,
-        duration__gte=datetime.timedelta(minutes=1)).order_by('-noted_on')
-
     context = {
         'streams': streams,
-        'ownevents': ownevents,
     }
 
     if stream_id:
@@ -61,7 +57,12 @@ def production(request, stream_id=None):
             context['assign_invitiations_officer_form'] = AssignInvitationsOfficerForm()
             context['assign_supervisor_form'] = AssignSupervisorForm()
             context['prodevent_form'] = ProductionEventForm()
-            context['work_log_form'] = WorkLogForm()
+
+            if request.user.has_perm('scipost.can_view_all_production_streams'):
+                types = constants.PRODUCTION_ALL_WORK_LOG_TYPES
+            else:
+                types = constants.PRODUCTION_OFFICERS_WORK_LOG_TYPES
+            context['work_log_form'] = WorkLogForm(log_types=types)
             context['upload_proofs_form'] = ProofsUploadForm()
         except ProductionStream.DoesNotExist:
             pass
@@ -106,7 +107,12 @@ def stream(request, stream_id):
     assign_invitiations_officer_form = AssignInvitationsOfficerForm()
     assign_supervisor_form = AssignSupervisorForm()
     upload_proofs_form = ProofsUploadForm()
-    work_log_form = WorkLogForm()
+
+    if request.user.has_perm('scipost.can_view_all_production_streams'):
+        types = constants.PRODUCTION_ALL_WORK_LOG_TYPES
+    else:
+        types = constants.PRODUCTION_OFFICERS_WORK_LOG_TYPES
+    work_log_form = WorkLogForm(log_types=types)
     status_form = StreamStatusForm(instance=stream, production_user=request.user.production_user)
 
     context = {
@@ -180,6 +186,31 @@ def add_event(request, stream_id):
     return redirect(reverse('production:production', args=(stream.id,)))
 
 
+@is_production_user()
+@permission_required('scipost.can_view_production', raise_exception=True)
+def add_work_log(request, stream_id):
+    stream = get_object_or_404(ProductionStream, pk=stream_id)
+    checker = ObjectPermissionChecker(request.user)
+    if not checker.has_perm('can_work_for_stream', stream):
+        return redirect(stream.get_absolute_url())
+
+    if request.user.has_perm('scipost.can_view_all_production_streams'):
+        types = constants.PRODUCTION_ALL_WORK_LOG_TYPES
+    else:
+        types = constants.PRODUCTION_OFFICERS_WORK_LOG_TYPES
+    work_log_form = WorkLogForm(request.POST or None, log_types=types)
+
+    if work_log_form.is_valid():
+        log = work_log_form.save(commit=False)
+        log.content = stream
+        log.user = request.user
+        log.save()
+        messages.success(request, 'Work Log added to Stream.')
+    else:
+        messages.warning(request, 'The form was invalidly filled.')
+    return redirect(stream.get_absolute_url())
+
+
 @is_production_user()
 @permission_required('scipost.can_assign_production_officer', raise_exception=True)
 @transaction.atomic
@@ -581,20 +612,35 @@ def send_proofs(request, stream_id, version):
         return redirect(reverse('production:production'))
 
     try:
-        proof = stream.proofs.can_be_send().get(version=version)
+        proofs = stream.proofs.can_be_send().get(version=version)
     except Proofs.DoesNotExist:
         raise Http404
 
-    proof.status = constants.PROOFS_SENT
-    proof.accessible_for_authors = True
-    proof.save()
+    proofs.status = constants.PROOFS_SENT
+    proofs.accessible_for_authors = True
 
     if stream.status not in [constants.PROOFS_PUBLISHED, constants.PROOFS_CITED,
                              constants.PRODUCTION_STREAM_COMPLETED]:
         stream.status = constants.PROOFS_SENT
         stream.save()
 
-    # TODO: SEND EMAIL TO NOTIFY OR KEEP THIS A HUMAN ACTION?
+    mail_request = MailEditingSubView(request, mail_code='production_send_proofs',
+                                      proofs=proofs)
+    if mail_request.is_valid():
+        proofs.save()
+        stream.save()
+        messages.success(request, 'Proofs have been sent.')
+        mail_request.send()
+        prodevent = ProductionEvent(
+            stream=stream,
+            event='status',
+            comments='Proofs version {version} sent to authors.'.format(version=proofs.version),
+            noted_by=request.user.production_user
+        )
+        prodevent.save()
+        return redirect(stream.get_absolute_url())
+    else:
+        return mail_request.return_render()
 
     messages.success(request, 'Proofs have been sent.')
     return redirect(stream.get_absolute_url())
diff --git a/scipost/views.py b/scipost/views.py
index b6c67e3297e4c11354988483ab9225e43acf08be..c142244dc7789f3c738d31341bfc9b0aaa3148ee 100644
--- a/scipost/views.py
+++ b/scipost/views.py
@@ -39,7 +39,7 @@ from commentaries.models import Commentary
 from comments.models import Comment
 from journals.models import Publication, Journal
 from news.models import NewsItem
-from submissions.models import Submission, EditorialAssignment, RefereeInvitation,\
+from submissions.models import Submission, RefereeInvitation,\
                                Report, EICRecommendation
 from partners.models import MembershipAgreement
 from theses.models import ThesisLink