aboutsummaryrefslogtreecommitdiffstats
path: root/kubernetes/sdnc/requirements.yaml
AgeCommit message (Expand)AuthorFilesLines
2021-08-17[SDNC] Update chart with service accountfarida azmy1-1/+4
2021-06-08[COMMON] Remove CertService client mechanismPiotr Marcinkiewicz1-3/+0
2021-03-24[DOC][COMMON] Prepare Honolulu releaseSylvain Desbureaux1-17/+15
2021-02-05[COMMON] Create certManagerCertificate chartPiotr Marcinkiewicz1-0/+3
2021-01-20[COMMON] Add template for CertServiceClientRemigiusz Janeczek1-0/+4
2020-12-14[COMMON][MARIADB] Upgrade Mariadb DB galera versionSylvain Desbureaux1-1/+1
2020-11-30[COMMON][DOC] Bump version GuilinSylvain Desbureaux1-13/+13
2020-11-27[SDNC] Uses new tpls for repos / imagesSylvain Desbureaux1-1/+3
2020-08-31[SDNC] oom for clustered disaggregated SDN-RAlexander Dehn1-1/+25
2020-07-23[SDNC] Set STDOUT Log levelSylvain Desbureaux1-0/+3
2020-05-20[SDNC] Use common aaf template in sdncKrzysztof Opasiak1-0/+3
2020-04-20[COMMON] helm chart for elastic dbAlexander Dehn1-0/+3
2020-04-02Bump chart versionSylvain Desbureaux1-5/+5
2020-03-04[SDNC] Use common secret template in sdncKrzysztof Opasiak1-2/+1
2019-09-11Remove cds charts from sdnc requirementes.yamlAbdelmuhaimen Seaudi1-4/+0
2019-07-11Moving Helm Chart version for El AltoMike Elliott1-6/+6
2019-04-12SDNC using shared mariadb-galera clusterMahendra Raghuwanshi1-3/+5
2019-03-04Add CDS as new chartSylvain Desbureaux1-1/+2
2019-02-10Update Chart version for Dublin ReleaseMike Elliott1-6/+6
2018-09-24Update Chart versions to 3.0Mike Elliott1-4/+4
2018-09-21Add requirement to network-name-gen for SDNCAlexis de Talhouët1-0/+3
2018-09-20Add requirement on controller-blueprint for sdncAlexis de Talhouët1-0/+3
2018-07-15SDN-C Multi-site High-availability - Auto-failoverMohammadreza Pasandideh1-1/+5
2018-04-02Add Standardized Configuration to SDN-C Chartjmac1-0/+24
536' href='#n536'>536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
#
# ============LICENSE_START==========================================
# org.onap.vvp/engagementmgr
# ===================================================================
# Copyright © 2017 AT&T Intellectual Property. All rights reserved.
# ===================================================================
#
# Unless otherwise specified, all software contained herein is licensed
# under the Apache License, Version 2.0 (the “License”);
# you may not use this software except in compliance with the License.
# You may obtain a copy of the License at
#
#             http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
#
# Unless otherwise specified, all documentation contained herein is licensed
# under the Creative Commons License, Attribution 4.0 Intl. (the “License”);
# you may not use this documentation except in compliance with the License.
# You may obtain a copy of the License at
#
#             https://creativecommons.org/licenses/by/4.0/
#
# Unless required by applicable law or agreed to in writing, documentation
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ============LICENSE_END============================================
#
# ECOMP is a trademark and service mark of AT&T Intellectual Property.
from __future__ import unicode_literals
import uuid
from django.contrib.auth.models import User
from django.db import models
from django.db.models.deletion import SET_NULL
from django.db.models.signals import post_save
from django.utils import timezone
from django.utils.timezone import timedelta
from engagementmanager.service.logging_service import LoggingServiceFactory
from engagementmanager.utils.constants import EngagementStage, ActivityType, \
    NextStepType, CheckListState, CheckListCategory, CheckListDecisionValue, \
    CheckListLineType, \
    RecentEngagementActionType, NextStepState


logger = LoggingServiceFactory.get_logger()


class Role(models.Model):
    uuid = models.CharField(default=uuid.uuid4, max_length=36, unique=True)
    name = models.CharField(max_length=36, unique=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = "ice_role"


class Vendor(models.Model):
    uuid = models.CharField(default=uuid.uuid4, max_length=36, unique=True)
    name = models.CharField(max_length=100, unique=True)
    public = models.BooleanField()

    def __str__(self):
        return self.name

    class Meta:
        db_table = "ice_vendor"


class CustomUser(User):
    activation_token = models.CharField(max_length=128, unique=True, null=True)
    activation_token_create_time = models.DateTimeField(
        'activation_token_create_time', default=timezone.now, null=True)
    temp_password = models.CharField(
        max_length=256, null=True, blank=True, default=None)

    def __str__(self):
        return self.email

    class Meta:
        db_table = "ice_custom_user"


class IceUserProfile(models.Model):
    user = models.OneToOneField(
        CustomUser, null=False, on_delete=models.CASCADE)
    uuid = models.CharField(default=uuid.uuid4, max_length=36, unique=True)
    company = models.ForeignKey(Vendor, on_delete=models.PROTECT, null=True)
    phone_number = models.CharField(max_length=30)
    full_name = models.CharField(max_length=30)
    role = models.ForeignKey(Role, on_delete=models.PROTECT, null=True)
    # although number of expected users<10^5 we prefer to index it
    email = models.EmailField('email', unique=True, db_index=True)
    create_time = models.DateTimeField('creation time', default=timezone.now)
    ssh_public_key = models.CharField(
        'ssh_public_key', max_length=1024, null=True, blank=True)
    regular_email_updates = models.BooleanField(default=False)
    email_updates_on_every_notification = models.BooleanField(default=True)
    email_updates_daily_digest = models.BooleanField(default=False)
    is_service_provider_contact = models.BooleanField(default=False)
    rgwa_access_key = models.CharField(
        max_length=1024, null=True, blank=True, unique=True)
    rgwa_secret_key = models.CharField(
        max_length=1024, null=True, blank=True, unique=True)
    slack_handle = models.CharField(
        max_length=64, null=True, blank=True, default=None)

    def __str__(self):
        return self.full_name + " - " + self.email

    class Meta:
        db_table = "ice_user_profile"


def create_user_profile(sender, instance, created, **kwargs):
    if created:
        user = CustomUser.objects.get(username=instance)
        profile, created = IceUserProfile.objects.get_or_create(
            user=user, email=instance)


post_save.connect(create_user_profile, sender=CustomUser)


class DeploymentTarget(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    name = models.CharField(max_length=45)
    version = models.CharField(max_length=100)
    weight = models.IntegerField(default=1)
    ui_visibility = models.BooleanField(default=True)

    def __str__(self):
        return self.name + ", Version: " + self.version

    class Meta:
        db_table = "ice_deployment_target"
        unique_together = (("name", "version"),)


# Represents ECOMPRelease
class ECOMPRelease(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    name = models.CharField(max_length=45)
    weight = models.IntegerField(default=1)
    ui_visibility = models.BooleanField(default=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = "ice_ecomp_release"


# Represents Deployment Target site, for example: Willows. It is connected
# to deployment target version
class DeploymentTargetSite(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    name = models.CharField(max_length=45)

    def __str__(self):
        return self.name

    class Meta:
        db_table = "ice_deployment_target_site"


def get_default_target_completion_date():
    return timezone.now() + timedelta(days=16)


class Engagement(models.Model):
    # no need to index this since index already exists thanks to it PK
    # characteristics
    uuid = models.CharField(
        default=uuid.uuid4, max_length=64, primary_key=True)
    engagement_team = models.ManyToManyField(
        IceUserProfile, related_name='members')
    creator = models.ForeignKey(
        IceUserProfile,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='Engagement_creator')
    contact_user = models.ForeignKey(
        IceUserProfile,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='Engagement_contact_user')
    engagement_manual_id = models.CharField(
        max_length=36,
        null=False,
        blank=False,
        default=-1,
        db_index=True)  # index in favor of dashboard search
    progress = models.IntegerField(default=0)
    target_completion_date = models.DateField(
        null=True, blank=True, default=get_default_target_completion_date)
    engagement_stage = models.CharField(
        max_length=15,
        default=EngagementStage.Intake.name,
        choices=EngagementStage.choices(),
        db_index=True)
    # Can be: Intake, Active, Validated, Completed @UndefinedVariable
    create_time = models.DateTimeField('creation time', default=timezone.now)
    peer_reviewer = models.ForeignKey(
        IceUserProfile,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='Engagement_peer_reviewer')
    reviewer = models.ForeignKey(
        IceUserProfile,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='Engagement_el_reviewer')
    starred_engagement = models.ManyToManyField(
        IceUserProfile, default=None, blank=True)
    heat_validated_time = models.DateTimeField(
        'heat validated time', null=True, blank=True)
    image_scan_time = models.DateTimeField(
        'image scan time', null=True, blank=True)
    aic_instantiation_time = models.DateTimeField(
        'aic instantiation time', null=True, blank=True)
    asdc_onboarding_time = models.DateTimeField(
        'asdc onboarding time', null=True, blank=True)
    started_state_time = models.DateTimeField(
        'started state time', null=True, blank=True)
    intake_time = models.DateTimeField('intake time', null=True, blank=True)
    active_time = models.DateTimeField('active time', null=True, blank=True)
    validated_time = models.DateTimeField(
        'validated time', null=True, blank=True)
    completed_time = models.DateTimeField(
        'completed time', null=True, blank=True)
    archive_reason = models.TextField(default=None, null=True)
    archived_time = models.DateTimeField(
        'archived time', null=True, blank=True)
    is_with_files = models.BooleanField(default=False)

    def __str__(self):
        return " uuid: " + str(self.uuid)

    class Meta:
        db_table = "ice_engagement"


class EngagementStatus(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=64, primary_key=True)
    engagement = models.ForeignKey(Engagement, on_delete=models.PROTECT)
    description = models.CharField(max_length=256)
    creator = models.ForeignKey(
        IceUserProfile,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='status_creator')
    create_time = models.DateTimeField(default=timezone.now)
    update_time = models.DateTimeField(default=timezone.now)

    class Meta:
        db_table = "ice_engagement_status"


class Activity(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, unique=True, primary_key=True)
    create_time = models.DateTimeField(default=timezone.now)
    description = models.CharField(max_length=512)
    is_notification = models.BooleanField(default=False)
    engagement = models.ForeignKey(
        Engagement, on_delete=models.PROTECT, db_index=True, null=True)
    activity_type = models.CharField(
        max_length=36, choices=ActivityType.choices())
    activity_owner = models.ForeignKey(IceUserProfile, null=True, blank=True)
    metadata = models.CharField(max_length=1024)

    def __str__(self):
        return 'Activity created at ' + str(self.create_time) \
            + ', Description: ' + self.description + \
            ', Notification:' + str(self.is_notification) + \
            ', ActivityType=' + str(self.activity_type)

    class Meta:
        ordering = ['-create_time']
        db_table = "ice_activity"
        # index in favor of pullRecentActivities
        index_together = ["engagement", "activity_owner"]


class Notification(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, unique=True, primary_key=True)
    user = models.ForeignKey(IceUserProfile, on_delete=models.CASCADE)
    is_sent = models.BooleanField(default=False)
    is_read = models.BooleanField(default=False)
    activity = models.ForeignKey(Activity, on_delete=models.CASCADE, null=True)

    def __str__(self):
        return str(self.user) + ' ' + str(self.is_sent) + \
            ' ' + str(self.is_read)

    class Meta:
        db_table = "ice_notification"
        # index in favor of Notification screen (pull_recent_notifications,
        # num_of_notifications_for_user)
        index_together = ["user", "is_read"]


class Feedback(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, unique=True, primary_key=True)
    create_time = models.DateTimeField(default=timezone.now)
    user = models.ForeignKey(IceUserProfile, null=False)
    description = models.TextField('feedback_description')

    def __str__(self):
        return 'Feedback created at ' + \
            str(self.create_time) + ' ' + str(self.user) + \
            ', Description: ' + self.description

    class Meta:
        db_table = "ice_feedback"
        # index in favor of Notification screen (pull_recent_notifications,
        # num_of_notifications_for_user)
        index_together = ["user"]


class ApplicationServiceInfrastructure(models.Model):
    name = models.CharField(max_length=100, unique=True)
    uuid = models.CharField(default=uuid.uuid4, max_length=36, unique=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = "ice_application_service_infrastructure"
        unique_together = (('name', 'uuid'),)


class VF(models.Model):
    # in favor of dashboard search by keyword
    name = models.CharField(max_length=100, db_index=True)
    # in favor of dashboard search by keyword
    version = models.CharField(max_length=100, db_index=True, null=True)
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, unique=True, primary_key=True)
    # index in favor of getRecentEng, getStarredEng
    engagement = models.OneToOneField(
        Engagement, null=False, blank=False, default=-1, db_index=True)
    deployment_target = models.ForeignKey(
        DeploymentTarget, on_delete=models.SET_NULL, null=True, blank=True)
    ecomp_release = models.ForeignKey(ECOMPRelease, null=True, blank=False)
    deployment_target_sites = models.ManyToManyField(
        DeploymentTargetSite,
        default=None,
        blank=True,
        related_name='DeployTarget_sites')
    is_service_provider_internal = models.BooleanField(default=False)
    vendor = models.ForeignKey(Vendor, on_delete=models.PROTECT)
    git_repo_url = models.CharField(max_length=512, blank=False, default=-1)
    target_lab_entry_date = models.DateField(
        'target_lab_entry_date', null=False)

    def jenkins_job_name(self):
        if not self.engagement.engagement_manual_id:
            raise ValueError(
                "engagement_manual_id (%s) is not valid for jenkins job name" %
                self.engagement.engagement_manual_id)
        return "{self.name}_{self.engagement.engagement_manual_id}".format(
            self=self)

    def __str__(self):
        return self.name

    class Meta:
        db_table = "ice_vf"


class VFC(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, unique=True, primary_key=True)
    vf = models.ForeignKey(VF, on_delete=models.PROTECT, db_index=True)
    # in favor of dashboard search by keyword
    name = models.CharField(max_length=100, db_index=True)
    external_ref_id = models.CharField(max_length=20, default='')
    ice_mandated = models.BooleanField(default=False)
    company = models.ForeignKey(Vendor, on_delete=SET_NULL, null=True)
    create_time = models.DateTimeField('creation time', default=timezone.now)
    creator = models.ForeignKey(
        IceUserProfile,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='Vfc_creator')

    def __str__(self):
        return self.name

    class Meta:
        db_table = "ice_vfc"


class RecentEngagement(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=64, primary_key=True)
    vf = models.ForeignKey(VF, on_delete=models.CASCADE)
    user_uuid = models.CharField(max_length=64)
    action_type = models.CharField(
        max_length=36, choices=RecentEngagementActionType.choices())
    last_update = models.DateTimeField('update time', default=timezone.now)

    def __str__(self):
        return " uuid: " + str(self.uuid)

    class Meta:
        db_table = "ice_recent_engagement"

# _________________________ C H E C K - L I S T __________________________


class ChecklistTemplate(models.Model):  # Reference Table
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    name = models.CharField('template name', max_length=255)
    category = models.CharField(
        max_length=36,
        choices=CheckListCategory.choices(),
        default=CheckListCategory.overall.name)  # @UndefinedVariable
    version = models.IntegerField('template version')
    create_time = models.DateTimeField('creation time', default=timezone.now)
    update_time = models.DateTimeField(
        'last update time', null=True, blank=True)

    def __str__(self):
        return self.name + ' ' + self.category

    class Meta:
        db_table = "ice_checklist_template"


class ChecklistSection(models.Model):  # Reference Table
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    name = models.CharField('section name', max_length=255)
    weight = models.FloatField('checklist weight')
    description = models.TextField('section description')
    validation_instructions = models.TextField(
        'section validation instructions')
    create_time = models.DateTimeField('creation time', default=timezone.now)
    update_time = models.DateTimeField(
        'last update time', null=True, blank=True)
    parent_section = models.ForeignKey("self", null=True, blank=True)
    template = models.ForeignKey(
        ChecklistTemplate, on_delete=models.PROTECT, null=False, blank=False)

    def __str__(self):
        return self.name + ' ' + self.description

    class Meta:
        db_table = "ice_checklist_section"


class ChecklistLineItem(models.Model):  # Reference Table
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    name = models.CharField('line name', max_length=255)
    weight = models.FloatField('line weight')
    description = models.TextField('line description')
    line_type = models.CharField(
        max_length=36,
        choices=CheckListLineType.choices(),
        default=CheckListLineType.auto.name)  # @UndefinedVariable
    validation_instructions = models.TextField('line validation instructions')
    create_time = models.DateTimeField('creation time', default=timezone.now)
    update_time = models.DateTimeField(
        'last update time', null=True, blank=True)
    template = models.ForeignKey(
        ChecklistTemplate, on_delete=models.CASCADE, null=False, blank=False)
    section = models.ForeignKey(
        ChecklistSection, on_delete=models.CASCADE, null=False, blank=False)

    def save(self, *args, **kwargs):
        if (self.template != self.section.template is not None):
            raise ValueError(
                "ChecklistLineItem can't be saved/updated \
                since the template " +
                self.template.name +
                " is not equal to its section's template " +
                self.section.template.name)
        super(ChecklistLineItem, self).save(*args, **kwargs)

    def __str__(self):
        return self.name + ' ' + self.description

    class Meta:
        db_table = "ice_checklist_line_item"


class Checklist(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    name = models.CharField('checklist name', max_length=255)
    state = models.CharField(max_length=36, choices=CheckListState.choices(
    ), default=CheckListState.pending.name)  # @UndefinedVariable
    # On each validation cycle this counter is increased
    validation_cycle = models.IntegerField('validation cycle')
    weight = models.FloatField('checklist weight', default=0)
    associated_files = models.TextField('list of files from gitlab')
    create_time = models.DateTimeField('creation time', default=timezone.now)
    update_time = models.DateTimeField(
        'last update time', null=True, blank=True)
    # index in favor of get StarredEngagements (especially for admin and EL)
    engagement = models.ForeignKey(
        Engagement, on_delete=models.CASCADE, db_index=True)
    template = models.ForeignKey(ChecklistTemplate, on_delete=models.PROTECT)
    # The EL that opened the modal.
    creator = models.ForeignKey(
        IceUserProfile,
        on_delete=models.CASCADE,
        related_name='checklist_creator')
    # The user who currently validates the checklist
    owner = models.ForeignKey(
        IceUserProfile,
        on_delete=models.CASCADE,
        related_name='checklist_owner')

    def __str__(self):
        return self.name + ' ' + self.state

    class Meta:
        db_table = "ice_checklist"


class NextStep(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    create_time = models.DateTimeField('creation time', default=timezone.now)
    creator = models.ForeignKey(
        IceUserProfile,
        on_delete=models.PROTECT,
        related_name="NextStep_creator")
    last_update_time = models.DateTimeField(
        'last update time', default=timezone.now)
    last_updater = models.ForeignKey(
        IceUserProfile,
        on_delete=models.PROTECT,
        null=True,
        related_name="NextStep_last_updater")
    # Can be: Modified, Added, Completed, Denied
    last_update_type = models.CharField(max_length=15, default='Added')
    position = models.IntegerField()
    description = models.TextField()
    # Can be: Incomplete, Completed
    state = models.CharField(max_length=15, choices=NextStepState.choices())
    # Can be: Intake, Active, Validated, Completed. This field is used to
    # correlate between the engagement stage to the next step stage in favor
    # of UI.
    engagement_stage = models.CharField(max_length=15)
    engagement = models.ForeignKey(
        Engagement, on_delete=models.PROTECT, null=True, blank=True)
    owner = models.ForeignKey(
        IceUserProfile, on_delete=models.PROTECT, null=True, blank=True)
    next_step_type = models.CharField(
        max_length=36,
        choices=NextStepType.choices(),
        default=NextStepType.user_defined.name)  # @UndefinedVariable
    files = models.TextField('list of files', null=True)
    assignees = models.ManyToManyField(
        IceUserProfile, related_name='assignees')
    due_date = models.DateField('due_date', null=True)

    def __str__(self):
        return self.engagement_stage + ' ' + self.state + \
            ' ' + self.description

    class Meta:
        db_table = "ice_next_step"
        verbose_name_plural = 'Next steps'
        # index in favor of nextstep_service.get_next_steps
        index_together = ["engagement_stage", "owner"]


class ChecklistDecision(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    review_value = models.CharField(
        max_length=36,
        choices=CheckListDecisionValue.choices())
    peer_review_value = models.CharField(
        max_length=36, choices=CheckListDecisionValue.choices())
    create_time = models.DateTimeField('creation time', default=timezone.now)
    update_time = models.DateTimeField(
        'last update time', null=True, blank=True)
    checklist = models.ForeignKey(Checklist, on_delete=models.CASCADE)
    template = models.ForeignKey(ChecklistTemplate, on_delete=models.CASCADE)
    lineitem = models.ForeignKey(ChecklistLineItem, on_delete=models.CASCADE)

    def save(self, *args, **kwargs):
        if (self.template != self.checklist.template is not None):
            raise ValueError(
                "ChecklistDecision can't be saved/updated \
                since the template " +
                self.template.name +
                " is not equal to its checklist's template " +
                self.checklist.template.name)
        if (self.template != self.lineitem.section.template is not None):
            raise ValueError(
                "ChecklistDecision can't be saved/updated \
                since the template " +
                self.template.name +
                " is not equal to its lineitem/section's template " +
                self.lineitem.section.template)
        if (self.checklist.template != self.lineitem.section.template
                is not None):
            raise ValueError(
                "ChecklistDecision can't be saved/updated since \
                its checklist's template " +
                self.checklist.template +
                " is not equal to its lineitem/section's template " +
                self.lineitem.section.template)
        super(ChecklistDecision, self).save(*args, **kwargs)

    def __str__(self):
        return 'decision:' + self.uuid + ' ' + self.template.name + \
            ' ' + self.review_value + ' ' + self.peer_review_value

    class Meta:
        db_table = "ice_checklist_decision"


class ChecklistAuditLog(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    category = models.CharField(max_length=255)
    description = models.TextField()
    create_time = models.DateTimeField('creation time', default=timezone.now)
    update_time = models.DateTimeField(
        'last update time', null=True, blank=True)
    creator = models.ForeignKey(IceUserProfile)
    # should be None if decisions is populated
    checklist = models.ForeignKey(
        Checklist, on_delete=models.CASCADE, null=True, blank=True)
    # should be None if checklist is populated
    decision = models.ForeignKey(
        ChecklistDecision, on_delete=models.CASCADE, null=True, blank=True)

    def save(self, *args, **kwargs):
        if (self.checklist is not None and self.decision is not None):
            raise ValueError(
                "ChecklistAuditLog can't be attached \
                to both checklist and decision. Please \
                remove one of them and retry the operation")
        super(ChecklistAuditLog, self).save(*args, **kwargs)

    def __str__(self):
        return self.category + ' ' + self.description

    class Meta:
        db_table = "ice_checklist_audit_log"


class Invitation(models.Model):
    uuid = models.CharField(
        default=uuid.uuid4, max_length=36, primary_key=True)
    engagement_uuid = models.CharField(
        max_length=64, null=False, blank=False, db_index=True)
    invited_by_user_uuid = models.CharField(
        max_length=64, null=False, blank=False, db_index=True)
    email = models.CharField(max_length=255, null=False, blank=False)
    invitation_token = models.CharField(
        max_length=1024,
        null=False,
        blank=False,
        db_index=True)  # index in favor of signup
    accepted = models.BooleanField(default=False)
    create_time = models.DateTimeField(
        'invitation creation time', default=timezone.now)

    def __str__(self):
        return "Invite from " + self.invited_by_user_uuid + " to " + \
            self.email + " for joining engagement " + self.engagement_uuid

    class Meta:
        db_table = "ice_invitation"
        unique_together = (("engagement_uuid", "email"),)