text: from django.db import models
  from django.contrib import admin
  from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, \
                                          PermissionsMixin
  from datetime import datetime
  
  
  class UserManager(BaseUserManager):
  
      def create_user(self, email, password=None, **extra_fields):
          """Creates and saves a new user with password as a username"""
          if not email:
              raise ValueError("User must have an email address")
          user = self.model(email=self.normalize_email(email), **extra_fields)
          user.set_password(password)
          user.save(using=self._db)
  
          return user
  
      def create_superuser(self, email, password):
          """Creates and saves a new superuser"""
          user = self.create_user(email, password)
          user.is_staff = True
          user.is_superuser = True
          user.save(using=self._db)
  
          return user
  
  
  class User(AbstractBaseUser, PermissionsMixin):
      """Custom user model that supports using email instead of username"""
      email = models.EmailField(max_length=255, unique=True)
      name = models.CharField(max_length=255)
      is_active = models.BooleanField(default=True)
      is_staff = models.BooleanField(default=False)
  
      objects = UserManager()
  
      USERNAME_FIELD = 'email'
  
  
  class OrgUnit(models.Model):
      """Organizational Unit who is owner of processes or does activities"""
      ORG_UNIT_CATEGORY = [
              (1, 'zarząd / pion'),
              (2, 'komórka organizacyjna Centrali'),
              (3, 'terenowa jednostka organizacyjna'),
              (4, 'wewnętrzna komórka organizacyjna w Centrali'),
              (5, 'centrum'),
              (6, 'wewnętrzna komórka organizacyjna w oddziale'),
              (8, 'określenie ogólne, agregujące'),
              (9, 'stanowisko'),
      ]
  
      name = models.CharField(
          max_length=256,
      )
      acronym = models.CharField(max_length=128, blank=True, default='')
  
      org_unit_type = models.PositiveSmallIntegerField(
          choices=ORG_UNIT_CATEGORY,
          default=False,
      )
      is_subject_to = models.ManyToManyField(
          'self',
          related_name='contains',
          blank=True,
      )
  
      class Meta:
          ordering = [
              'acronym',
          ]
  
      def __str__(self):
          if self.acronym == '':
              return self.name
          else:
              return '%s (%s)' % (self.acronym, self.name, )
  
  
  class Process(models.Model):
      """Business process - element of management architecture"""
  
      PROCESS_TYPE = [
              ('Operacyjny', 'Operacyjny'),
              ('Zarządzania', 'Zarządzania'),
              ('Wsparcia', 'Wsparcia'),
      ]
  
      PROCESS_LEVEL = [
              (True, 'poziom 1 (megaproces)'),
              (False, 'poziom 2 (proces)'),
      ]
  
      name = models.CharField(max_length=255)
      proc_id = models.CharField(max_length=4)
      is_megaprocess = models.BooleanField(
          default=False,
          choices=PROCESS_LEVEL)
      type = models.CharField(
          max_length=15,
          choices=PROCESS_TYPE,
      )
      owner = models.ForeignKey(
          'OrgUnit',
          on_delete=models.CASCADE,
      )
      parent = models.ForeignKey(
          'self',
          on_delete=models.SET_NULL,
          blank=True,
          null=True,
          limit_choices_to={'is_megaprocess': True}
      )
  
      def __str__(self):
          return 'Proces %s' % (self.proc_id,)
  
  
  class Procedure(models.Model):
      process = models.OneToOneField(
          'Process',
          on_delete=models.CASCADE,
          limit_choices_to={'is_megaprocess': False},
          unique_for_date='effective_date',
      )
      version = models.CharField(max_length=5, blank=True)
      developed_by = models.ForeignKey(
          'OrgUnit',
          on_delete=models.SET_NULL,
          blank=True,
          null=True,
      )
      effective_date = models.DateField()
      goal = models.CharField(max_length=255, blank=True)
  
      class Meta:
          ordering = [
              'process__proc_id',
          ]
  
      def __str__(self):
          return 'Procedura: %s' % (self.process)
  
  
  class ProcedureInline(admin.TabularInline):
      model = Procedure
  
  
  class Stakeholder(models.Model):
      """Stakeholder that participates in business processes"""
      STAKEHOLDER_TYPE = [
          (False, 'zewnętrzny'),
          (True, 'wewnętrzny'),
      ]
  
      name = models.CharField(max_length=128)
      description = models.TextField(blank=True, null=True)
      type = models.BooleanField(
          default=False,
          choices=STAKEHOLDER_TYPE
      )
  
      class Meta:
          unique_together = ('name', 'description',)
          ordering = [
              'name',
          ]
  
      def __str__(self):
          return '%s [%s]' % (self.name, self.description)
  
  
  class WorkTask(models.Model):
      """WorkTask is a part of a action that has defined
          performers, participants and applications"""
      action = models.ForeignKey(
          'Action',
          on_delete=models.CASCADE,
      )
      in_action_order = models.PositiveSmallIntegerField()
      task = models.TextField()
      participants = models.TextField(blank=True, null=True)
      executive_units = models.ManyToManyField(
          'OrgUnit',
          related_name='responsible',
      )
      cooperative_units = models.ManyToManyField(
          'OrgUnit',
          related_name='cooperating',
          blank=True,
  
      )
      stakeholders = models.ManyToManyField(
          'Stakeholder',
          blank=True,
      )
      applications = models.TextField(blank=True, null=True)
      business_apps = models.ManyToManyField(
          'BusinessApp',
          blank=True,
      )
  
      def __str__(self):
          return '%s %s %s' % (
              self.action,
              self.in_action_order,
              self.task,
          )
  
  
  class WorkTaskInline(admin.TabularInline):
      model = WorkTask
  
  
  class Action(models.Model):
      """Action is a part of a procedure for process that has defined
          input, products, performers and applications"""
  
      INPUT_OUTPUT_TYPE = [
          (0, 'niezdefiniowany'),
          (1, 'informacja')
      ]
  
      procedure = models.ForeignKey(
          'Procedure',
          on_delete=models.CASCADE,
      )
      in_process_step = models.PositiveSmallIntegerField()
      name = models.TextField(max_length=512)
      notes = models.TextField(blank=True, null=True)
      trigger = models.TextField(blank=True)
      trigger_type = models.PositiveSmallIntegerField(
          choices=INPUT_OUTPUT_TYPE,
          default=0,
      )
      effects = models.TextField(blank=True)
      effects_type = models.PositiveSmallIntegerField(
          choices=INPUT_OUTPUT_TYPE,
          default=0,
      )
  
      class Meta:
          ordering = [
              'procedure__process__proc_id',
              'in_process_step',
          ]
  
      def __str__(self):
          return '%s: %s. %s' % (
              self.procedure.process.proc_id,
              self.in_process_step,
              self.name[:128]
          )
  
  class ActionInline(admin.TabularInline):
      model = Action
  
  
  class BCMActivity(models.Model):
      """BCM Activity is a part of a process that is subject to BIA"""
  
      MTPD_TIMES = [
              (1, '4 godz.'),
              (2, 'dzień'),
              (3, '2 dni'),
              (4, 'tydzień'),
              (5, '2 tygodnie'),
              (6, 'miesiąc'),
              (7, '2 miesiące'),
              (8, 'do odwołania'),
      ]
  
      name = models.CharField(max_length=512)
      process = models.ForeignKey(
          'Process',
          on_delete=models.CASCADE,
          limit_choices_to={'is_megaprocess': False}
      )
      performer = models.ForeignKey(
          'OrgUnit',
          on_delete=models.CASCADE,
      )
      id_BIA = models.CharField(max_length=15, blank=True, null=True)
      MTPD = models.PositiveSmallIntegerField(
          choices=MTPD_TIMES
      )
      min_recovery_level = models.TextField(blank=True)
      TTN = models.PositiveSmallIntegerField(
          choices=MTPD_TIMES,
          blank=True,
      )
      tasks = models.ManyToManyField(
          'WorkTask',
          blank=True,
      )
      locations = models.ManyToManyField(
          'Location',
          related_name='localized_in',
          blank=True,
      )
      equipment = models.ManyToManyField(
          'Equipment',
          related_name='uses',
          blank=True,
      )
      positions = models.ManyToManyField(
          'Position',
          related_name='performs',
          blank=True,
      )
      supplies = models.ManyToManyField(
          'Supplies',
          related_name='requires',
          blank=True,
      )
      information = models.ManyToManyField(
          'Information',
          related_name='process',
          blank=True,
      )
  
  
      def __str__(self):
          return '%s: [%s] %s' % (
              self.process,
              self.performer.acronym,
              self.name
          )
  
  
  class BusinessApp(models.Model):
      """Software application that supports business processes"""
  
      name = models.CharField(max_length=64, unique=True)
      description = models.TextField(blank=True, null=True)
      not_in_PKU = models.BooleanField(default=False)
  
      class Meta:
          ordering = [
              'name',
          ]
  
      def __str__(self):
          return self.name
  
  
  class Location(models.Model):
      """Phisical location (building etc.)"""
  
      MTPD_CHOICES = BCMActivity.MTPD_TIMES.copy()
      MTPD_CHOICES.insert(0, (None,'------'))
  
      name = models.CharField(max_length=64, unique=True)
      description = models.TextField(blank=True, null=True)
      address = models.CharField(max_length=128, blank=True, null=True)
      number = models.PositiveSmallIntegerField(default=1)
      manager = models.ForeignKey(
          'OrgUnit',
          null=True,
          default=None,
          on_delete=models.SET_NULL,
      )
      over_MTPD = models.PositiveSmallIntegerField(
          choices=MTPD_CHOICES,
          null=True,
          blank=True,
          default=None
      )
  
      class Meta:
          ordering = [
              'name',
          ]
  
      def __str__(self):
          return '%s [%s]' % (
              self.name,
              self.address
          )
  
  
  class Equipment(models.Model):
      """Office and technical equipment"""
  
      MTPD_CHOICES = BCMActivity.MTPD_TIMES.copy()
      MTPD_CHOICES.insert(0, (None,'------'))
  
      name = models.CharField(max_length=128)
      eq_model = models.CharField(max_length=64, blank=True, null=True)
      description = models.TextField(blank=True, null=True)
      number = models.PositiveSmallIntegerField(default=1)
      eq_user = models.ForeignKey(
          'Position',
          null=True,
          default=None,
          on_delete=models.SET_NULL,
      )
      over_MTPD = models.PositiveSmallIntegerField(
          choices=MTPD_CHOICES,
          null=True,
          blank=True,
          default=None
      )
  
      class Meta:
          unique_together = ('name', 'eq_model',)
          ordering = [
              'name',
          ]
  
      def __str__(self):
          return '%s [%s]' % (
              self.name,
              self.eq_model
          )
  
  
  class Position(models.Model):
      """Official position at work"""
  
      MTPD_CHOICES = BCMActivity.MTPD_TIMES.copy()
      MTPD_CHOICES.insert(0, (None,'------'))
  
      name = models.CharField(max_length=128)
      description = models.TextField(blank=True, null=True)
      number = models.PositiveSmallIntegerField(default=1)
      unit = models.ForeignKey(
          'OrgUnit',
          null=True,
          on_delete=models.SET_NULL,
      )
      over_MTPD = models.PositiveSmallIntegerField(
          choices=MTPD_CHOICES,
          default=None,
          blank=True,
          null=True
      )
  
      class Meta:
          unique_together = ('name', 'unit',)
          ordering = [
              'name',
          ]
  
      def __str__(self):
          return self.name
  
  
  class Supplies(models.Model):
      """Supplies for office and technical equipment"""
  
      MTPD_CHOICES = BCMActivity.MTPD_TIMES.copy()
      MTPD_CHOICES.insert(0, (None,'------'))
  
      name = models.CharField(max_length=128)
      sup_model = models.CharField(max_length=64, blank=True, null=True)
      description = models.TextField(blank=True, null=True)
      min_stock = models.CharField(max_length=128, blank=True, null=True)
      eq_user = models.ForeignKey(
          'Position',
          null=True,
          default=None,
          on_delete=models.SET_NULL,
      )
      over_MTPD = models.PositiveSmallIntegerField(
          choices=MTPD_CHOICES,
          null=True,
          blank=True,
          default=None
      )
  
      class Meta:
          unique_together = ('name', 'sup_model',)
          ordering = [
              'name',
          ]
  
      def __str__(self):
          return '%s [%s]' % (
              self.name,
              self.sup_model
          )
  
  
  class Information(models.Model):
      """Information that can be used as content for input and output"""
  
      INF_TYPE = [
              (1, 'Wejście do działania'),
              (2, 'Wyjście z działania'),
              (3, 'Inne'),
      ]
  
      MTPD_CHOICES = BCMActivity.MTPD_TIMES.copy()
      MTPD_CHOICES.insert(0, (None,'------'))
  
      name = models.CharField(max_length=256)
      type = models.PositiveSmallIntegerField(
          choices=INF_TYPE,
          null=False,
          default=3
      )
      description = models.TextField(
          blank=True,
          null=True
      )
      location = models.CharField(
          max_length=256,
          blank=True,
          null=True,
          default=None
      )
      over_MTPD = models.PositiveSmallIntegerField(
          choices=MTPD_CHOICES,
          null=True,
          blank=True,
          default=None
      )
  
      class Meta:
          unique_together = ('name', 'description', 'type')
          ordering = [
              'name',
          ]
  
      def __str__(self):
          return '%s [%s][%s]' % (
              self.name,
              self.type,
              self.description
          )
  