2022-04-12 00:18:51 +02:00
from django . db import models
2022-04-12 16:09:17 +02:00
import secrets
from django . shortcuts import reverse
2022-04-12 20:00:19 +02:00
from datetime import timedelta
from django . utils import timezone
2022-04-20 18:39:33 +02:00
from django . db . models import F , Count , Q , ExpressionWrapper
2022-04-27 15:07:43 +02:00
from phonenumber_field . modelfields import PhoneNumberField
2022-04-27 17:45:34 +02:00
from dynamic_preferences . registries import global_preferences_registry
global_preferences = global_preferences_registry . manager ( )
2022-04-15 16:20:13 +02:00
2022-04-27 15:24:22 +02:00
2022-04-12 14:41:01 +02:00
class Room ( models . Model ) :
name = models . CharField ( max_length = 200 , primary_key = True )
required_helpers = models . IntegerField ( )
2022-04-15 16:20:13 +02:00
2022-04-12 14:41:01 +02:00
def __str__ ( self ) :
return self . name
2022-04-15 16:20:13 +02:00
2022-04-12 14:41:01 +02:00
class Shift ( models . Model ) :
room = models . ForeignKey ( Room , on_delete = models . RESTRICT )
start_at = models . DateTimeField ( )
duration = models . DurationField ( )
2022-05-10 15:43:37 +02:00
required_helpers = models . IntegerField (
default = 0 , help_text = " When this is set to zero, the room value is used instead. "
)
2022-04-23 14:36:47 +02:00
deleted = models . BooleanField ( default = False )
2022-04-12 14:41:01 +02:00
def __str__ ( self ) :
return f " { self . room . name } : { self . start_at } "
2022-04-20 18:57:04 +02:00
2022-04-20 17:33:15 +02:00
def has_ended ( self ) :
return ( self . start_at + self . duration ) < timezone . now ( )
2022-04-20 18:57:04 +02:00
2022-04-20 18:39:33 +02:00
def is_running ( self ) :
2022-04-20 18:57:04 +02:00
return ( self . start_at < = timezone . now ( ) ) and ( not self . has_ended ( ) )
2022-04-12 14:41:01 +02:00
2022-04-15 16:20:13 +02:00
2022-04-12 14:41:01 +02:00
class Helper ( models . Model ) :
2022-04-27 16:19:35 +02:00
phone = PhoneNumberField ( unique = True , editable = False )
2022-04-12 14:41:01 +02:00
name = models . CharField ( max_length = 200 )
# change this to a generic state variable to allow for number blocking/account deactivation?
number_validated = models . BooleanField ( default = False )
2022-04-15 16:20:13 +02:00
2022-04-12 14:41:01 +02:00
def __str__ ( self ) :
return self . name
2022-04-15 16:20:13 +02:00
2022-04-12 17:04:47 +02:00
def send_confirmation ( self ) :
( token , created ) = LoginToken . objects . get_or_create ( helper = self )
2022-04-20 19:18:48 +02:00
token . send ( )
2022-04-12 17:04:47 +02:00
return token
2022-04-20 18:57:04 +02:00
2022-04-20 18:39:33 +02:00
# current or next shift
def important_shift ( self ) :
2022-04-20 18:57:04 +02:00
ret = (
ShiftRegistration . objects . annotate (
2022-04-20 18:39:33 +02:00
shift_end = ExpressionWrapper (
F ( " shift__start_at " ) + F ( " shift__duration " ) ,
output_field = models . DateTimeField ( ) ,
2022-04-20 18:57:04 +02:00
)
)
2022-04-27 20:24:00 +02:00
. filter ( helper = self , shift_end__gte = timezone . now ( ) , shift__deleted = False )
2022-04-20 18:57:04 +02:00
. order_by ( " shift__start_at " )
. first ( )
)
2022-04-20 18:39:33 +02:00
if ret :
return ret . shift
2022-04-12 14:41:01 +02:00
class ShiftRegistration ( models . Model ) :
class Meta :
2022-04-15 16:20:13 +02:00
unique_together = ( ( " shift " , " helper " ) , )
2022-04-12 14:41:01 +02:00
# use restrict for now as Model.delete is not called
shift = models . ForeignKey ( Shift , on_delete = models . RESTRICT )
helper = models . ForeignKey ( Helper , on_delete = models . CASCADE )
2022-04-20 22:05:26 +02:00
reminder_sent = models . BooleanField ( default = False )
2022-04-15 16:20:13 +02:00
2022-04-12 20:00:19 +02:00
def can_cancel ( self ) :
2022-04-27 17:45:34 +02:00
return self . shift . start_at > (
2022-04-27 21:35:28 +02:00
timezone . now ( )
+ global_preferences_registry . manager ( ) [ " helper__min_cancel_time " ]
2022-04-27 17:45:34 +02:00
)
2022-04-15 16:20:13 +02:00
2022-04-20 22:05:26 +02:00
def send_reminder ( self ) :
2022-05-10 15:43:37 +02:00
text = f " Deine kontakt-Schicht beginnt um { self . shift . start_at . strftime ( ' % H: % M ' ) } , bitte komm eine halbe Stunde vorher an den Infopoint. "
2022-04-20 22:05:26 +02:00
msg = Message ( to = self . helper , text = text )
msg . save ( )
self . reminder_sent = True
self . save ( )
2022-04-21 00:53:53 +02:00
def __str__ ( self ) :
return f " { self . helper . name } : { self . shift } "
2022-04-12 14:41:01 +02:00
class Message ( models . Model ) :
# remove limit and send long messages in multiple messages?
text = models . CharField ( max_length = 160 )
to = models . ForeignKey ( Helper , on_delete = models . CASCADE )
2022-04-20 19:18:48 +02:00
created_at = models . DateTimeField ( auto_now_add = True )
2022-04-27 19:10:23 +02:00
sent_at = models . DateTimeField ( null = True )
2022-04-15 16:20:13 +02:00
2022-04-12 14:41:01 +02:00
def __str__ ( self ) :
2022-04-20 19:18:48 +02:00
return f " { self . to . name } ( { self . created_at } ): { self . text } "
2022-04-12 14:41:01 +02:00
2022-04-15 16:20:13 +02:00
2022-04-12 14:41:01 +02:00
def gen_token ( ) :
2022-04-25 23:35:13 +02:00
return secrets . token_urlsafe (
15
) # returns 15 bytes Base64-encoded (times 1.333...) = 20 characters
2022-04-12 14:41:01 +02:00
2022-04-15 16:20:13 +02:00
2022-04-12 14:41:01 +02:00
class LoginToken ( models . Model ) :
2022-04-15 16:20:13 +02:00
id = models . CharField (
max_length = 20 , primary_key = True , default = gen_token , editable = False
)
2022-04-12 16:09:17 +02:00
helper = models . ForeignKey ( Helper , on_delete = models . CASCADE )
2022-04-20 19:18:48 +02:00
sent_at = models . DateTimeField ( auto_now_add = True )
2022-04-29 02:22:47 +02:00
send_count = models . IntegerField ( default = 0 )
2022-04-20 19:18:48 +02:00
def send ( self ) :
2022-04-29 02:36:32 +02:00
text = f " Dein Registrierungslink zum Helfer*innensystem: https://kontakt.rocks { self . get_absolute_url ( ) } \n Wenn du dich nicht registriert hast, ignoriere diese SMS. "
2022-04-20 19:18:48 +02:00
msg = Message ( to = self . helper , text = text )
msg . save ( )
self . sent_at = timezone . now ( )
2022-04-29 02:22:47 +02:00
self . send_count + = 1
2022-04-20 19:18:48 +02:00
self . save ( )
2022-04-27 19:10:23 +02:00
# import here to break import cycle
from . tasks import send_message
2022-04-27 22:34:26 +02:00
send_message . delay ( msg . pk )
2022-04-15 16:20:13 +02:00
2022-04-12 16:09:17 +02:00
def get_absolute_url ( self ) :
2022-04-15 16:20:13 +02:00
return reverse ( " token_login " , kwargs = { " token " : self . id } )