# -*- coding: utf-8 -*-
import datetime
+import calendar
from django.http import QueryDict
from mezzanine.pages.models import Page
from mezzanine.blog.models import BlogPost
@register.filter
def get_attr(obj, attr):
return getattr(obj, attr)
+
+@register.filter
+def month_name(month_number):
+ return calendar.month_name[month_number]
+
+@register.filter
+def format_wp(work_packages):
+ work_packages = [str(wk.number) for wk in work_packages]
+ return ",".join(work_packages)
+
+@register.filter
+def format_percent(percent):
+ return str(percent * 100) + ' %'
from organization.core.admin import *
from organization.pages.admin import PageImageInline, PageBlockInline, PagePlaylistInline, DynamicContentPageInline, PageRelatedTitleAdmin
from organization.shop.models import PageProductList
-from organization.network.utils import TimesheetXLS
+from organization.network.utils import TimesheetXLS, set_timesheets_validation_date
class OrganizationAdminInline(StackedDynamicInlineAdmin):
search_fields = ['year','activity__person__last_name', "project__title"]
list_display = ['person', 'activity', 'year', 'month', 'project', 'work_package', 'percentage', 'accounting', 'validation']
list_filter = ['activity__person', 'year', 'project']
- actions = ['export_xls',]
+ actions = ['export_xls', 'validate_timesheets']
+
def person(self, instance):
return instance.activity.person
def export_xls(self, request, queryset):
xls = TimesheetXLS(queryset)
return xls.write()
+
+ def validate_timesheets(self, request, queryset):
+ set_timesheets_validation_date(queryset)
+
export_xls.short_description = "Export person timesheets"
import requests
import time
from django.conf import settings
-from datetime import date, datetime, timedelta
+from datetime import date, timedelta
import dateutil.parser
WEEK_DAYS = {
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from django.utils import timezone
from dal import autocomplete
import dal_queryset_sequence
import dal_select2_queryset_sequence
class PersonActivityTimeSheetForm(forms.ModelForm):
+ def save(self):
+ self.instance.accounting = timezone.now()
+ super(PersonActivityTimeSheetForm, self).save()
+
class Meta:
model = PersonActivityTimeSheet
fields = ('__all__')
- # PersonActivityTimeSheetviewfields = ['pub_date', 'headline', 'content', 'reporter']
+ exclude = ['accounting', 'validation']
--- /dev/null
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.11 on 2017-01-18 10:19
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('organization-network', '0083_auto_20170116_1235'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='personactivitytimesheet',
+ name='accounting',
+ field=models.DateField(blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='personactivitytimesheet',
+ name='validation',
+ field=models.DateField(blank=True, null=True),
+ ),
+ ]
--- /dev/null
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.11 on 2017-01-18 11:39
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('organization-network', '0084_auto_20170118_1119'),
+ ]
+
+ operations = [
+ migrations.AlterUniqueTogether(
+ name='personactivitytimesheet',
+ unique_together=set([('activity', 'project', 'month', 'year')]),
+ ),
+ ]
--- /dev/null
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.11 on 2017-01-18 11:47
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('organization-network', '0085_auto_20170118_1239'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='personactivitytimesheet',
+ name='month',
+ field=models.IntegerField(choices=[(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')], verbose_name='month'),
+ ),
+ ]
from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
-from django.utils import timezone
from mezzanine.pages.models import Page
from mezzanine.core.models import RichText, Displayable, Slugged
('pattern-bg--triangles', _('triangles')),
]
+MONTH_CHOICES = [
+ (1, _('January')),
+ (2, _('February')),
+ (3, _('March')),
+ (4, _('April')),
+ (5, _('May')),
+ (6, _('June')),
+ (7, _('July')),
+ (8, _('August')),
+ (9, _('September')),
+ (10, _('October')),
+ (11, _('November')),
+ (12, _('December')),
+]
+
ALIGNMENT_CHOICES = (('left', _('left')), ('left', _('left')), ('right', _('right')))
CSS_COLOR_CHOICES = [
project = models.ForeignKey('organization-projects.Project', verbose_name=_('project'), related_name='timesheets')
work_packages = models.ManyToManyField('organization-projects.ProjectWorkPackage', verbose_name=_('work package'), related_name='timesheets', blank=True)
percentage = models.FloatField(_('% of work time on the project'), validators=[validate_positive])
- month = models.IntegerField(_('month'))
+ month = models.IntegerField(_('month'), choices=MONTH_CHOICES)
year = models.IntegerField(_('year'))
- accounting = models.DateField(default=timezone.now(), blank=True, null=True)
- validation = models.DateField(default=timezone.now(), blank=True, null=True)
+ accounting = models.DateField(blank=True, null=True)
+ validation = models.DateField(blank=True, null=True)
@property
def date(self):
verbose_name = _('activity timesheet')
verbose_name_plural = _('activity timesheets')
ordering = ['month',]
+ unique_together = (("activity", "project", "month", "year"),)
class PersonActivityVacation(Period):
from django.http import HttpResponse
from xlwt import *
import calendar
-from organization.network.api import *
import datetime
+from django.utils import timezone
+from organization.network.api import *
from collections import defaultdict, OrderedDict
from pprint import pprint
return md_dict
-class TimesheetXLS2(object):
-
- def __init__(self, timesheets):
- self.timesheets = timesheets.order_by('activity','project', 'year', 'month')
-
-
class TimesheetXLS(object):
t_dict = OrderedDict()
if not person_slug in self.t_dict:
self.t_dict[person_slug] = {}
# caculate for each person leaved days in year
+ print("timesheet.year", timesheet.year)
date_from = datetime.date(timesheet.year, 1, 1)
date_to = datetime.date(timesheet.year, 12, 31)
nb_half_days = get_nb_half_days_by_period_per_month(date_from, date_to)
leave_days = get_leave_days_per_month(date_from, date_to, timesheet.activity.person.external_id)
- worked_hours_by_month = {"test" : "1"}
+ worked_hours_by_month = {}
# for each month
for m_key, m_val in nb_half_days.items():
# for each week day
response['Content-Disposition'] = 'attachment; filename=users.xls'
self.book.save(response)
return response
+
+
+
+def set_timesheets_validation_date(timesheets):
+ """ Admin action to set validation date for selected timesheets """
+ for timesheet in timesheets :
+ timesheet.validation = timezone.now()
+ timesheet.save()
from organization.core.views import *
from datetime import date
from organization.network.forms import *
-from organization.network.utils import TimesheetXLS, TimesheetXLS2
+from organization.network.utils import TimesheetXLS
+from collections import OrderedDict
+
class PersonListView(ListView):
context_object_name = 'timesheet'
form_class = PersonActivityTimeSheetForm
+ def get_success_url(self):
+ print(" self.kwargs['slug']", self.kwargs['slug'])
+ return reverse('organization-network-timesheet-list-view', kwargs={'slug': self.kwargs['slug']})
+
def get_initial(self):
initial = super(TimeSheetCreateView, self).get_initial()
+ # get the more recent activity
initial['activity'] = PersonActivity.objects.filter(person__slug=self.kwargs['slug']).first()
initial['month'] = self.kwargs['month']
initial['year'] = self.kwargs['year']
class PersonActivityTimeSheetListView(TimesheetAbstractView, ListView):
model = PersonActivityTimeSheet
template_name='network/person_activity_timesheet/person_activity_timesheet_list.html'
- context_object_name = 'timesheet'
+ context_object_name = 'timesheets_by_year'
def get_queryset(self):
if 'slug' in self.kwargs:
- return PersonActivityTimeSheet.objects.filter(activity__person__slug__exact=self.kwargs['slug'])
+ timesheets = PersonActivityTimeSheet.objects.filter(activity__person__slug__exact=self.kwargs['slug'])
+ t_dict = OrderedDict()
+ for timesheet in timesheets:
+ year = timesheet.year
+ if not year in t_dict:
+ t_dict[year] = {}
+ project_slug = timesheet.project.title
+ # if new person
+ if not project_slug in t_dict[year]:
+ t_dict[year][project_slug] = []
+ t_dict[year][project_slug].append(timesheet)
+ return t_dict
def get_context_data(self, **kwargs):
context = super(PersonActivityTimeSheetListView, self).get_context_data(**kwargs)
--- /dev/null
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.11 on 2017-01-18 11:39
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('organization-projects', '0041_merge'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='project',
+ name='manager',
+ field=models.ManyToManyField(blank=True, related_name='projects_manager', to='organization-network.Person', verbose_name='Manager'),
+ ),
+ ]
website = models.URLField(_('website'), max_length=512, blank=True)
topic = models.ForeignKey('ProjectTopic', verbose_name=_('topic'), related_name='projects', blank=True, null=True)
referring_person = models.ManyToManyField('organization-network.Person', verbose_name=_('Referring Person'), related_name='projects_referring_person', blank=True)
- manager = models.ManyToManyField('organization-network.Person', verbose_name=_('Manager'), related_name='projects_manager', blank=True, null=True)
+ manager = models.ManyToManyField('organization-network.Person', verbose_name=_('Manager'), related_name='projects_manager', blank=True)
class Meta:
verbose_name = _('project')
-{{ object }}
+{% load organization_tags %}
+<table>
+ <tr>
+ <th>{{ timesheet.month|month_name }}</th>
+ </tr>
+ <tr>
+ <td>
+ {{ timesheet.percentage|format_percent }}
+ </td>
+ </tr>
+ <tr>
+ <td>
+ {% with timesheet.work_packages.all as work_packages %}
+ {% if work_packages %}
+ {{ timesheet.work_packages.all|format_wp }}
+ {% else %}
+ <span>-</span>
+ {% endif %}
+ {% endwith %}
+ </td>
+ </tr>
+</table>
{% block page_content %}
<a class="pull-right button button--black" href="{% url 'organization-network-timesheet-create-view' slug current_year current_month %}" title="">Declare this month</a>
-
- {% if person_activity_timesheet %}
-
- {% for pat in person_activity_timesheet %}
- {% include "network/person_activity_timesheet/includes/person_activity_timesheet_inline.html" with object=pat %}
+ {{ timesheets_by_project }}
+ {% if timesheets_by_year %}
+ {% for year_k, year_v in timesheets_by_year.items %}
+ <h2>{{ year_k }}</h2>
+ {% for project_k, project_v in year_v.items %}
+ <h3>{{ project_k }}</h3>
+ <table>
+ <tr>
+ {% for timesheet in project_v %}
+ <td>
+ {% include "network/person_activity_timesheet/includes/person_activity_timesheet_inline.html" %}
+ </td>
+ {% endfor %}
+ </tr>
+ </table>
+ {% endfor %}
{% endfor %}
{% else %}