class ProgrammeEventMeta(ContactEmailMixin, EventMetaBase): public_from = models.DateTimeField( null=True, blank=True, verbose_name='Ohjelmakartan julkaisuaika', help_text='Ohjelmakartta näkyy kansalle tästä eteenpäin.', ) contact_email = models.CharField( max_length=255, blank=True, validators=[contact_email_validator], verbose_name='yhteysosoite', help_text='Kaikki ohjelmajärjestelmän lähettämät sähköpostiviestit lähetetään tästä ' 'osoitteesta, ja tämä osoite näytetään ohjelmanjärjestäjälle yhteysosoitteena. Muoto: Selite <[email protected]>.', ) accepting_cold_offers_from = models.DateTimeField( null=True, blank=True, verbose_name=_("Accepting cold offers from"), ) accepting_cold_offers_until = models.DateTimeField( null=True, blank=True, verbose_name=_("Accepting cold offers until"), ) schedule_layout = models.CharField( max_length=max(len(choice[0]) for choice in SCHEDULE_LAYOUT_CHOICES), default='reasonable', choices=SCHEDULE_LAYOUT_CHOICES, verbose_name=_('Schedule layout'), help_text=_( 'Some events may opt to make their schedule use the full width of the browser window. ' 'This option selects between reasonable width (the default) and full width.' ), ) def __init__(self, *args, **kwargs): if 'public' in kwargs: public = kwargs.pop('public') if public: kwargs['public_from'] = now() super(ProgrammeEventMeta, self).__init__(*args, **kwargs) def get_special_programmes(self, include_unpublished=False, **extra_criteria): from .room import Room from .programme import Programme schedule_rooms = Room.objects.filter(view_rooms__view__event=self.event).only('id') criteria = dict(category__event=self.event, **extra_criteria) if not include_unpublished: criteria.update(state='published') return Programme.objects.filter(**criteria).exclude(room__in=schedule_rooms) @classmethod def get_or_create_dummy(cls): from core.models import Event from django.utils.timezone import now event, unused = Event.get_or_create_dummy() admin_group, hosts_group = cls.get_or_create_groups(event, ['admins', 'hosts']) return cls.objects.get_or_create( event=event, defaults=dict( admin_group=admin_group, public_from=now(), ) ) @property def is_public(self): return self.public_from is not None and now() > self.public_from @property def is_accepting_cold_offers(self): return is_within_period( self.accepting_cold_offers_from, self.accepting_cold_offers_until, ) @property def is_full_width(self): """ For easy iffability in templates. """ return self.schedule_layout == 'full-width' @property def default_role(self): from .role import Role return Role.objects.get(personnel_class__event=self.event, is_default=True) @property def is_using_alternative_programme_forms(self): from .alternative_programme_form import AlternativeProgrammeForm return AlternativeProgrammeForm.objects.filter(event=self.event).exists() @property def default_alternative_programme_form(self): from .alternative_programme_form import AlternativeProgrammeForm return AlternativeProgrammeForm.objects.filter(event=self.event, slug='default').first() def publish(self): self.public_from = now() self.save() def unpublish(self): self.public_from = None self.save() public = alias_property('is_public') @property def signup_extra_model(self): if self.event.labour_event_meta is not None: return self.event.labour_event_meta.signup_extra_model else: from labour.models import EmptySignupExtra return EmptySignupExtra
class Signup(models.Model, CsvExportMixin): person = models.ForeignKey('core.Person', related_name='signups') event = models.ForeignKey('core.Event') personnel_classes = models.ManyToManyField( 'labour.PersonnelClass', blank=True, verbose_name='Henkilöstöluokat', help_text= 'Mihin henkilöstöryhmiin tämä henkilö kuuluu? Henkilö saa valituista ryhmistä ' 'ylimmän mukaisen badgen.', ) job_categories = models.ManyToManyField( 'labour.JobCategory', verbose_name='Haettavat tehtävät', help_text= 'Valitse kaikki ne tehtävät, joissa olisit valmis työskentelemään ' 'tapahtumassa. Huomaathan, että sinulle tarjottavia tehtäviä voi rajoittaa se, ' 'mitä pätevyyksiä olet ilmoittanut sinulla olevan. Esimerkiksi järjestyksenvalvojaksi ' 'voivat ilmoittautua ainoastaan JV-kortilliset.', related_name='signup_set') notes = models.TextField( blank=True, verbose_name='Käsittelijän merkinnät', help_text= ('Tämä kenttä ei normaalisti näy henkilölle itselleen, mutta jos tämä ' 'pyytää henkilörekisteriotetta, kentän arvo on siihen sisällytettävä.' ), ) created_at = models.DateTimeField(auto_now_add=True, verbose_name='Luotu') updated_at = models.DateTimeField(auto_now=True, verbose_name='Päivitetty') job_categories_accepted = models.ManyToManyField( 'labour.JobCategory', blank=True, related_name='accepted_signup_set', verbose_name='Hyväksytyt tehtäväalueet', help_text= 'Tehtäväalueet, joilla hyväksytty vapaaehtoistyöntekijä tulee työskentelemään. ' 'Tämän perusteella henkilölle mm. lähetetään oman tehtäväalueensa työvoimaohjeet. ' 'Harmaalla merkityt tehtäväalueet ovat niitä, joihin hakija ei ole itse hakenut.' ) job_categories_rejected = models.ManyToManyField( 'labour.JobCategory', blank=True, related_name='+', verbose_name=_('Rejected job categories'), help_text= _('The workforce manager may use this field to inform other workforce managers that ' 'this applicant will not be accepted to a certain job category. This field is not visible ' 'to the applicant, but should they request a record of their own information, this field will ' 'be included.')) xxx_interim_shifts = models.TextField( blank=True, null=True, default="", verbose_name="Työvuorot", help_text= ("Tämä tekstikenttä on väliaikaisratkaisu, jolla vänkärin työvuorot voidaan " "merkitä Kompassiin ja lähettää vänkärille työvoimaviestissä jo ennen kuin " "lopullinen työvuorotyökalu on käyttökunnossa."), ) alternative_signup_form_used = models.ForeignKey( 'labour.AlternativeSignupForm', blank=True, null=True, verbose_name="Ilmoittautumislomake", help_text= ("Tämä kenttä ilmaisee, mitä ilmoittautumislomaketta hakemuksen täyttämiseen käytettiin. " "Jos kenttä on tyhjä, käytettiin oletuslomaketta."), ) job_title = models.CharField( max_length=JOB_TITLE_LENGTH, blank=True, default='', verbose_name="Tehtävänimike", help_text= ("Printataan badgeen ym. Asetetaan automaattisesti hyväksyttyjen tehtäväalueiden perusteella, " "mikäli kenttä jätetään tyhjäksi."), ) is_active = models.BooleanField(verbose_name='Aktiivinen', default=True) time_accepted = models.DateTimeField( null=True, blank=True, verbose_name='Hyväksytty', ) time_confirmation_requested = models.DateTimeField( null=True, blank=True, verbose_name='Vahvistusta vaadittu', ) time_finished = models.DateTimeField( null=True, blank=True, verbose_name='Vuorot valmiit', ) time_complained = models.DateTimeField( null=True, blank=True, verbose_name='Vuoroista reklamoitu', ) time_cancelled = models.DateTimeField( null=True, blank=True, verbose_name='Peruutettu', ) time_rejected = models.DateTimeField( null=True, blank=True, verbose_name='Hylätty', ) time_arrived = models.DateTimeField( null=True, blank=True, verbose_name='Saapunut tapahtumaan', ) time_work_accepted = models.DateTimeField( null=True, blank=True, verbose_name='Työpanos hyväksytty', ) time_reprimanded = models.DateTimeField( null=True, blank=True, verbose_name='Työpanoksesta esitetty moite', ) is_accepted = time_bool_property('time_accepted') is_confirmation_requested = time_bool_property( 'time_confirmation_requested') is_finished = time_bool_property('time_finished') is_complained = time_bool_property('time_complained') is_cancelled = time_bool_property('time_cancelled') is_rejected = time_bool_property('time_rejected') is_arrived = time_bool_property('time_arrived') is_work_accepted = time_bool_property('time_work_accepted') is_workaccepted = alias_property( 'is_work_accepted') # for automagic groupiness is_reprimanded = time_bool_property('time_reprimanded') is_new = property(lambda self: self.state == 'new') is_applicants = alias_property( 'is_active') # group is called applicants for historical purposes is_confirmation = alias_property('is_confirmation_requested') is_processed = property(lambda self: self.state != 'new') class Meta: verbose_name = _('signup') verbose_name_plural = _('signups') def __str__(self): p = self.person.full_name if self.person else 'None' e = self.event.name if self.event else 'None' return '{p} / {e}'.format(**locals()) @property def personnel_class(self): """ The highest personnel class of this Signup (possibly None). """ return self.personnel_classes.first() @property def signup_extra_model(self): return self.event.labour_event_meta.signup_extra_model @property def signup_extra(self): if not hasattr(self, '_signup_extra'): SignupExtra = self.signup_extra_model self._signup_extra = SignupExtra.for_signup(self) return self._signup_extra def get_first_categories(self): return self.job_categories.all()[:NUM_FIRST_CATEGORIES] @property def is_more_categories(self): return self.job_categories.count() > NUM_FIRST_CATEGORIES def get_redacted_category_names(self): return ', '.join( cat.name for cat in self.job_categories.all()[NUM_FIRST_CATEGORIES:]) @property def job_categories_label(self): if self.state == 'new': return 'Haetut tehtävät' else: return 'Hyväksytyt tehtävät' @property def job_category_accepted_labels(self): state = self.state label_class = SIGNUP_STATE_LABEL_CLASSES[state] if state == 'new': label_texts = [cat.name for cat in self.get_first_categories()] labels = [(label_class, label_text, None) for label_text in label_texts] if self.is_more_categories: labels.append( (label_class, '...', self.get_redacted_category_names())) elif state == 'cancelled': labels = [(label_class, 'Peruutettu', None)] elif state == 'rejected': labels = [(label_class, 'Hylätty', None)] elif state == 'beyond_logic': labels = [(label_class, 'Perätilassa', None)] elif self.is_accepted: label_texts = [ cat.name for cat in self.job_categories_accepted.all() ] labels = [(label_class, label_text, None) for label_text in label_texts] else: from warnings import warn warn('Unknown state: {state}'.format(self=self)) labels = [] return labels @property def personnel_class_labels(self): label_texts = [pc.name for pc in self.personnel_classes.all()] return [('label-default', label_text, None) for label_text in label_texts] @property def some_job_title(self): """ Tries to figure a job title for this worker using the following methods in this order 1. A manually set job title 2. The title of the job category the worker is accepted into 3. A generic job title """ if self.job_title: return self.job_title elif self.job_categories_accepted.exists(): return self.job_categories_accepted.first().name else: return 'Työvoima' @property def granted_privileges(self): if 'access' not in settings.INSTALLED_APPS: return [] from access.models import GrantedPrivilege return GrantedPrivilege.objects.filter( person=self.person, privilege__group_privileges__group__in=self.person.user.groups.all( ), privilege__group_privileges__event=self.event, ) @property def potential_privileges(self): if 'access' not in settings.INSTALLED_APPS: return [] from access.models import Privilege return Privilege.get_potential_privileges( person=self.person, group_privileges__event=self.event) @classmethod def get_or_create_dummy(cls, accepted=False): from core.models import Person, Event from .job_category import JobCategory person, unused = Person.get_or_create_dummy() event, unused = Event.get_or_create_dummy() job_category, unused = JobCategory.get_or_create_dummy() signup, created = Signup.objects.get_or_create(person=person, event=event) extra = signup.signup_extra signup.job_categories = [job_category] extra.save() if accepted: signup.job_categories_accepted = signup.job_categories.all() signup.personnel_classes.add( signup.job_categories.first().personnel_classes.first()) signup.state = 'accepted' signup.save() signup.apply_state() return signup, created @classmethod def get_state_query_params(cls, state): flag_values = STATE_FLAGS_BY_NAME[state] assert len(STATE_TIME_FIELDS) == len(flag_values) query_params = [] for time_field_name, flag_value in zip(STATE_TIME_FIELDS, flag_values): time_field_preposition = '{}__isnull'.format(time_field_name) query_params.append((time_field_preposition, not flag_value)) # First state flag is not a time bool field, but an actual bona fide boolean field. # Also "is null" semantics mean that flag values are flipped, so we need to backflip it. query_params[0] = ('is_active', not query_params[0][1]) return OrderedDict(query_params) @classmethod def mass_reject(cls, signups): return cls._mass_state_change('new', 'rejected', signups) @classmethod def mass_request_confirmation(cls, signups): return cls._mass_state_change('accepted', 'confirmation', signups) @classmethod def filter_signups_for_mass_send_shifts(cls, signups): return signups.filter( **cls.get_state_query_params('accepted')).exclude( xxx_interim_shifts='', shifts__isnull=True, ) @classmethod def mass_send_shifts(cls, signups): return cls._mass_state_change( old_state='accepted', new_state='finished', signups=signups, filter_func=cls.filter_signups_for_mass_send_shifts) @classmethod def _mass_state_change(cls, old_state, new_state, signups, filter_func=None): if filter_func is None: signups = signups.filter( **Signup.get_state_query_params(old_state)) else: signups = filter_func(signups) for signup in signups: signup.state = new_state signup.save() signup.apply_state() return signups def apply_state(self): self.apply_state_sync() if 'background_tasks' in settings.INSTALLED_APPS: from ..tasks import signup_apply_state signup_apply_state.delay(self.pk) else: self._apply_state() def apply_state_sync(self): self.apply_state_ensure_job_categories_accepted_is_set() self.apply_state_ensure_personnel_class_is_set() self.signup_extra.apply_state() self.apply_state_create_badges() def _apply_state(self): self.apply_state_group_membership() self.apply_state_email_aliases() self.apply_state_send_messages() def apply_state_group_membership(self): from .job_category import JobCategory from .personnel_class import PersonnelClass groups_to_add = set() groups_to_remove = set() for group_suffix in SIGNUP_STATE_GROUPS: should_belong_to_group = getattr( self, 'is_{group_suffix}'.format(group_suffix=group_suffix)) group = self.event.labour_event_meta.get_group(group_suffix) if should_belong_to_group: groups_to_add.add(group) else: groups_to_remove.add(group) for job_category in JobCategory.objects.filter(event=self.event): should_belong_to_group = self.job_categories_accepted.filter( pk=job_category.pk).exists() group = self.event.labour_event_meta.get_group(job_category.slug) if should_belong_to_group: groups_to_add.add(group) else: groups_to_remove.add(group) for personnel_class in PersonnelClass.objects.filter( event=self.event, app_label='labour'): should_belong_to_group = self.personnel_classes.filter( pk=personnel_class.pk).exists() group = self.event.labour_event_meta.get_group( personnel_class.slug) if should_belong_to_group: groups_to_add.add(group) else: groups_to_remove.add(group) ensure_user_group_membership(self.person.user, groups_to_add, groups_to_remove) def apply_state_email_aliases(self): if 'access' not in settings.INSTALLED_APPS: return from access.models import GroupEmailAliasGrant GroupEmailAliasGrant.ensure_aliases(self.person) def apply_state_send_messages(self, resend=False): if 'mailings' not in settings.INSTALLED_APPS: return from mailings.models import Message Message.send_messages(self.event, 'labour', self.person) def apply_state_ensure_job_categories_accepted_is_set(self): if self.is_accepted and not self.job_categories_accepted.exists( ) and self.job_categories.count() == 1: self.job_categories_accepted.add(self.job_categories.get()) def apply_state_ensure_personnel_class_is_set(self): for app_label in self.job_categories_accepted.values_list( 'app_label', flat=True).distinct(): if self.personnel_classes.filter(app_label=app_label).exists(): continue any_jca = self.job_categories_accepted.filter( app_label=app_label).first() personnel_class = any_jca.personnel_classes.first() self.personnel_classes.add(personnel_class) def apply_state_create_badges(self): if 'badges' not in settings.INSTALLED_APPS: return if self.event.badges_event_meta is None: return from badges.models import Badge Badge.ensure(event=self.event, person=self.person) def get_previous_and_next_signup(self): queryset = self.event.signup_set.order_by('person__surname', 'person__first_name', 'id').all() return get_previous_and_next(queryset, self) @property def _state_flags(self): # The Grand Order is defined here. return ( self.is_active, self.is_accepted, self.is_confirmation_requested, self.is_finished, self.is_complained, self.is_arrived, self.is_work_accepted, self.is_reprimanded, self.is_rejected, self.is_cancelled, ) @_state_flags.setter def _state_flags(self, flags): # These need to be in the Grand Order. ( self.is_active, self.is_accepted, self.is_confirmation_requested, self.is_finished, self.is_complained, self.is_arrived, self.is_work_accepted, self.is_reprimanded, self.is_rejected, self.is_cancelled, ) = flags @property def state(self): return STATE_NAME_BY_FLAGS[self._state_flags] @state.setter def state(self, new_state): self._state_flags = STATE_FLAGS_BY_NAME[new_state] @property def next_states(self): cur_state = self.state states = [] if cur_state == 'new': states.extend(('accepted', 'rejected', 'cancelled')) elif cur_state == 'accepted': states.extend(('finished', 'confirmation', 'cancelled')) elif cur_state == 'confirmation': states.extend(('accepted', 'cancelled')) elif cur_state == 'finished': states.extend(('arrived', 'complained', 'no_show', 'relieved')) elif cur_state == 'complained': states.extend(('finished', 'relieved')) elif cur_state == 'arrived': states.extend(('honr_discharged', 'dish_discharged', 'relieved')) elif cur_state == 'beyond_logic': states.extend(( 'new', 'accepted', 'finished', 'complained', 'rejected', 'cancelled', 'arrived', 'honr_discharged', 'no_show', )) if cur_state != 'beyond_logic': states.append('beyond_logic') return states @property def next_states_buttons(self): return [ StateTransition(self, to_state) for to_state in self.next_states ] @property def formatted_state(self): return dict(SIGNUP_STATE_NAMES).get(self.state, '') @property def state_label_class(self): return SIGNUP_STATE_LABEL_CLASSES[self.state] @property def state_description(self): return SIGNUP_STATE_DESCRIPTIONS.get(self.state, '') @property def state_times(self): return [( self._meta.get_field(field_name).verbose_name, getattr(self, field_name, None), ) for field_name in STATE_TIME_FIELDS if getattr(self, field_name, None)] @property def person_messages(self): if 'mailings' in settings.INSTALLED_APPS: if getattr(self, '_person_messages', None) is None: self._person_messages = self.person.personmessage_set.filter( message__recipient__event=self.event, message__recipient__app_label='labour', ).order_by('-created_at') return self._person_messages else: return [] @property def have_person_messages(self): if 'mailings' in settings.INSTALLED_APPS: return self.person_messages.exists() else: return False @property def applicant_has_actions(self): return any([ self.applicant_can_edit, self.applicant_can_confirm, self.applicant_can_cancel, ]) @property def applicant_can_edit(self): return self.state == 'new' and self.is_registration_open @property def is_registration_open(self): if self.alternative_signup_form_used is not None: return self.alternative_signup_form_used.is_active else: return self.event.labour_event_meta.is_registration_open @property def applicant_can_confirm(self): return self.state == 'confirmation' def confirm(self): assert self.state == 'confirmation' self.state = 'accepted' self.save() self.apply_state() @property def applicant_can_cancel(self): return self.is_active and not self.is_cancelled and not self.is_rejected and \ not self.is_arrived @property def formatted_personnel_classes(self): from .job_category import format_job_categories return format_job_categories(self.personnel_classes.all()) @property def formatted_job_categories_accepted(self): from .job_category import format_job_categories return format_job_categories(self.job_categories_accepted.all()) @property def formatted_job_categories(self): from .job_category import format_job_categories return format_job_categories(self.job_categories.all()) @property def formatted_shifts(self): parts = [] if self.xxx_interim_shifts: parts.append(self.xxx_interim_shifts) parts.extend(text_type(shift) for shift in self.shifts.all()) return "\n\n".join(part for part in parts if part) # for admin @property def full_name(self): return self.person.full_name @property def info_links(self): from .info_link import InfoLink return InfoLink.objects.filter( event=self.event, group__user=self.person.user, ) @property def email_address(self): from access.models import EmailAlias email_alias = EmailAlias.objects.filter( type__domain__organization=self.event.organization, person=self.person, ).order_by('type__priority').first() # TODO order return email_alias.email_address if email_alias else self.person.email @classmethod def get_csv_fields(cls, event): if getattr(event, '_signup_csv_fields', None) is None: from core.models import Person event._signup_csv_fields = [] related_models = [Person, Signup] fields_to_skip = [ # useless & non-serializable (Person, 'user'), (Signup, 'person'), # too official (Person, 'official_first_names'), (Person, 'muncipality'), ] SignupExtra = event.labour_event_meta.signup_extra_model if SignupExtra is not None: related_models.append(SignupExtra) fields_to_skip.extend([ (SignupExtra, 'event'), (SignupExtra, 'person'), ]) # XXX HACK jv-kortin numero if 'labour_common_qualifications' in settings.INSTALLED_APPS: from labour_common_qualifications.models import JVKortti related_models.append(JVKortti) fields_to_skip.append((JVKortti, 'personqualification')) for model in related_models: for field in model._meta.fields: if (model, field.name) in fields_to_skip: continue event._signup_csv_fields.append((model, field)) for field in model._meta.many_to_many: event._signup_csv_fields.append((model, field)) return event._signup_csv_fields def get_csv_related(self): from core.models import Person related = {Person: self.person} signup_extra_model = self.signup_extra_model if signup_extra_model: related[signup_extra_model] = self.signup_extra # XXX HACK jv-kortin numero if 'labour_common_qualifications' in settings.INSTALLED_APPS: from labour_common_qualifications.models import JVKortti try: jv_kortti = JVKortti.objects.get( personqualification__person=self.person) related[JVKortti] = jv_kortti except JVKortti.DoesNotExist: related[JVKortti] = None return related def as_dict(self): # XXX? signup_extra = self.signup_extra shift_wishes = signup_extra.shift_wishes if signup_extra.get_field( 'shift_wishes') else '' total_work = signup_extra.total_work if signup_extra.get_field( 'total_work') else '' shift_type = signup_extra.get_shift_type_display( ) if signup_extra.get_field('shift_type') else '' return dict( id=self.person.id, fullName=self.person.full_name, shiftWishes=shift_wishes, totalWork=total_work, currentlyAssigned=self.shifts.all().aggregate( sum_hours=Coalesce(Sum('hours'), 0))['sum_hours'], shiftType=shift_type, ) @classmethod def for_signup(cls, signup): """ Surveys make use of this method. """ return signup
class LabourEventMeta(ContactEmailMixin, EventMetaBase): signup_extra_content_type = models.ForeignKey('contenttypes.ContentType', on_delete=models.CASCADE) registration_opens = models.DateTimeField( null=True, blank=True, verbose_name=_("Registration opens"), ) public_from = alias_property('registration_opens') registration_closes = models.DateTimeField( null=True, blank=True, verbose_name=_("Registration closes"), ) public_until = alias_property('registration_closes') work_begins = models.DateTimeField(verbose_name='Ensimmäiset työvuorot alkavat') work_ends = models.DateTimeField(verbose_name='Viimeiset työvuorot päättyvät') monitor_email = models.CharField( max_length=255, blank=True, verbose_name='tarkkailusähköposti', help_text='Kaikki työvoimajärjestelmän lähettämät sähköpostiviestit lähetetään myös ' 'tähän osoitteeseen.', ) contact_email = models.CharField( max_length=255, blank=True, validators=[contact_email_validator,], verbose_name='yhteysosoite', help_text='Kaikki työvoimajärjestelmän lähettämät sähköpostiviestit lähetetään tästä ' 'osoitteesta, ja tämä osoite näytetään työvoimalle yhteysosoitteena. Muoto: Selite <[email protected]>.', ) signup_message = models.TextField( null=True, blank=True, default='', verbose_name='Ilmoittautumisen huomautusviesti', help_text='Tämä viesti näytetään kaikille työvoimailmoittautumisen alussa. Käytettiin ' 'esimerkiksi Tracon 9:ssä kertomaan, että työvoimahaku on avoinna enää JV:ille ja ' 'erikoistehtäville.', ) work_certificate_signer = models.TextField( null=True, blank=True, default='', verbose_name='Työtodistuksen allekirjoittaja', help_text='Tämän kentän sisältö näkyy työtodistuksen allekirjoittajan nimenselvennyksenä. ' 'On suositeltavaa sisällyttää tähän omalle rivilleen allekirjoittajan tehtävänimike.' ) use_cbac = True class Meta: verbose_name = _('labour event meta') verbose_name_plural = _('labour event metas') def __str__(self): return self.event.name if self.event else 'None' @property def signup_extra_model(self): return self.signup_extra_content_type.model_class() @classmethod def events_registration_open(cls): from core.models import Event t = now() return Event.objects.filter( laboureventmeta__registration_opens__isnull=False, laboureventmeta__registration_opens__lte=t, ).exclude( laboureventmeta__registration_closes__isnull=False, laboureventmeta__registration_closes__lte=t, ) @classmethod def get_or_create_dummy(cls): from django.contrib.contenttypes.models import ContentType from core.models import Event from .signup_extras import EmptySignupExtra event, unused = Event.get_or_create_dummy() content_type = ContentType.objects.get_for_model(EmptySignupExtra) admin_group, = LabourEventMeta.get_or_create_groups(event, ['admins']) t = now() labour_event_meta, created = cls.objects.get_or_create( event=event, defaults=dict( admin_group=admin_group, signup_extra_content_type=content_type, registration_opens=t - timedelta(days=60), registration_closes=t + timedelta(days=60), work_begins=event.start_time - timedelta(days=1), work_ends=event.end_time + timedelta(days=1), contact_email='*****@*****.**', monitor_email='*****@*****.**', ) ) labour_event_meta.create_groups() return labour_event_meta, created @classmethod def get_or_create_groups(cls, event, job_categories_or_suffixes): suffixes = [ jc_or_suffix if isinstance(jc_or_suffix, str) else jc_or_suffix.slug for jc_or_suffix in job_categories_or_suffixes ] groups = super(LabourEventMeta, cls).get_or_create_groups(event, suffixes) if 'mailings' in settings.INSTALLED_APPS: from mailings.models import RecipientGroup from .job_category import JobCategory from .personnel_class import PersonnelClass for jc_or_suffix, group in zip(job_categories_or_suffixes, groups): if isinstance(jc_or_suffix, JobCategory): verbose_name = jc_or_suffix.name job_category = jc_or_suffix personnel_class = None elif isinstance(jc_or_suffix, PersonnelClass): verbose_name = jc_or_suffix.name job_category = None personnel_class = jc_or_suffix else: verbose_name = GROUP_VERBOSE_NAMES_BY_SUFFIX[jc_or_suffix] job_category = None personnel_class = None RecipientGroup.objects.get_or_create( event=event, app_label='labour', group=group, defaults=dict( job_category=job_category, personnel_class=personnel_class, verbose_name=verbose_name, ), ) return groups def create_groups_async(self): if 'background_tasks' in settings.INSTALLED_APPS: from ..tasks import labour_event_meta_create_groups labour_event_meta_create_groups.delay(self.pk) else: self.create_groups() def create_groups(self): from .job_category import JobCategory from .personnel_class import PersonnelClass job_categories_or_suffixes = list(SIGNUP_STATE_GROUPS) job_categories_or_suffixes.extend(JobCategory.objects.filter(event=self.event)) job_categories_or_suffixes.extend(PersonnelClass.objects.filter(event=self.event, app_label='labour')) return LabourEventMeta.get_or_create_groups(self.event, job_categories_or_suffixes) @property def is_registration_open(self): return is_within_period(self.registration_opens, self.registration_closes) is_public = alias_property('is_registration_open') def publish(self): """ Used by the start/stop signup period view to start the signup period. Returns True if the user needs to be warned about a certain corner case where information was lost. """ warn = False if self.public_until and self.public_until <= now(): self.public_until = None warn = True self.public_from = now() self.save() return warn def unpublish(self): """ Used by the start/stop signup period view to end the signup period. We prefer setting public_until to clearing public_from because this causes less information loss. """ self.public_until = now() self.save() def is_person_signed_up(self, person): return self.event.signup_set.filter(person=person).exists() def get_signup_for_person(self, person): from .signup import Signup try: return self.event.signup_set.get(person=person) except Signup.DoesNotExist: return Signup(person=person, event=self.event) @property def work_hours(self): return full_hours_between(self.work_begins, self.work_ends) @property def applicants_group(self): return self.get_group('applicants') @property def accepted_group(self): return self.get_group('accepted') @property def finished_group(self): return self.get_group('finished') @property def rejected_group(self): return self.get_group('rejected')