Time tracking you can host anywhere, built on Django. Full export support in multiple formats and easily extensible.
djangodockerhandcodedpythonself-hostedtime-trackingtimetracker
1from pytz import all_timezones
2
3from django.conf import settings
4from django.conf.global_settings import LANGUAGES
5from django.contrib.auth import get_user_model
6from django.db import models
7from django.db.models.signals import post_save
8from django.contrib.sites.models import Site
9from django.dispatch import receiver
10
11
12class Conf(models.Model):
13 site = models.OneToOneField(Site, related_name="conf", on_delete=models.CASCADE)
14
15 # Internationalization settings
16 i18n_language_code = models.CharField(
17 verbose_name="Language Code",
18 max_length=7,
19 choices=LANGUAGES,
20 default=settings.LANGUAGE_CODE,
21 )
22 i18n_timezone = models.CharField(
23 verbose_name="Timezone",
24 max_length=255,
25 choices=(zip(all_timezones, all_timezones)),
26 default=settings.TIME_ZONE,
27 )
28
29 # SMTP settings
30 smtp_from_address = models.EmailField(
31 verbose_name='"From" Email Address',
32 default=settings.DEFAULT_FROM_EMAIL,
33 blank=True,
34 )
35 smtp_host = models.CharField(
36 verbose_name="SMTP Host",
37 max_length=255,
38 blank=True,
39 default=settings.EMAIL_HOST,
40 )
41 smtp_user = models.CharField(
42 verbose_name="SMTP Username",
43 max_length=255,
44 blank=True,
45 default=settings.EMAIL_HOST_USER,
46 )
47 # TODO: Store and use this securely.
48 smtp_password = models.CharField(
49 verbose_name="SMTP Password",
50 help_text="This password is stored in plaintext. Use with caution!",
51 max_length=255,
52 blank=True,
53 default=settings.EMAIL_HOST_PASSWORD,
54 )
55 smtp_port = models.PositiveIntegerField(
56 verbose_name="SMTP Port", blank=True, default=settings.EMAIL_PORT
57 )
58 smtp_tls = models.BooleanField(
59 verbose_name="Use TLS", default=settings.EMAIL_USE_TLS
60 )
61
62 objects = models.Manager()
63
64 class Meta:
65 verbose_name = "Configuration"
66 verbose_name_plural = "Configuration"
67
68 def __str__(self):
69 return "Configuration for {}".format(self.site.name)
70
71
72@receiver(post_save, sender=Site)
73def create_conf(sender, instance, created, **kwargs):
74 if created:
75 Conf.objects.create(site=instance)
76
77
78@receiver(post_save, sender=Site)
79def save_conf(sender, instance, **kwargs):
80 instance.conf.save()
81
82
83class SitePermission(models.Model):
84 user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
85 sites = models.ManyToManyField("sites.Site", blank=True)
86
87 objects = models.Manager()
88
89 class Meta:
90 verbose_name = "Site permission"
91 verbose_name_plural = "Site permissions"
92
93 def __str__(self):
94 return "Site permissions for {}".format(self.user)