class TestPlan(TreeNode, UrlMixin): """A plan within the TCMS""" history = KiwiHistoricalRecords() name = models.CharField(max_length=255, db_index=True) text = models.TextField(blank=True) create_date = models.DateTimeField(auto_now_add=True) is_active = models.BooleanField(default=True, db_index=True) extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True) product_version = models.ForeignKey( Version, related_name="plans", on_delete=models.CASCADE ) author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) product = models.ForeignKey( "management.Product", related_name="plan", on_delete=models.CASCADE ) type = models.ForeignKey(PlanType, on_delete=models.CASCADE) tag = models.ManyToManyField( "management.Tag", through="testplans.TestPlanTag", related_name="plan" ) def __str__(self): return self.name def add_case(self, case, sortkey=None):
class TestPlan(TCMSActionModel): """A plan within the TCMS""" history = KiwiHistoricalRecords() name = models.CharField(max_length=255, db_index=True) text = models.TextField(blank=True) create_date = models.DateTimeField(auto_now_add=True) is_active = models.BooleanField(default=True, db_index=True) extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True) product_version = models.ForeignKey(Version, related_name='plans', on_delete=models.CASCADE) author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) product = models.ForeignKey('management.Product', related_name='plan', on_delete=models.CASCADE) type = models.ForeignKey(PlanType, on_delete=models.CASCADE) parent = models.ForeignKey('TestPlan', blank=True, null=True, related_name='child_set', on_delete=models.CASCADE) tag = models.ManyToManyField('management.Tag', through='testplans.TestPlanTag', related_name='plan') def __str__(self): return self.name @classmethod def to_xmlrpc(cls, query=None): _query = query or {} qs = distinct_filter(TestPlan, _query).order_by('pk') serializer = TestPlanRPCSerializer(model_class=cls, queryset=qs) return serializer.serialize_queryset() def add_case(self, case, sortkey=None):
class TestPlan(TCMSActionModel): """A plan within the TCMS""" history = KiwiHistoricalRecords() plan_id = models.AutoField(primary_key=True) name = models.CharField(max_length=255, db_index=True) text = models.TextField(blank=True) create_date = models.DateTimeField(db_column='creation_date', auto_now_add=True) is_active = models.BooleanField(db_column='isactive', default=True, db_index=True) extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True) product_version = models.ForeignKey(Version, related_name='plans', on_delete=models.CASCADE) author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) product = models.ForeignKey('management.Product', related_name='plan', on_delete=models.CASCADE) type = models.ForeignKey(PlanType, on_delete=models.CASCADE) parent = models.ForeignKey('TestPlan', blank=True, null=True, related_name='child_set', on_delete=models.CASCADE) tag = models.ManyToManyField('management.Tag', through='testplans.TestPlanTag', related_name='plan') class Meta: index_together = [['product', 'plan_id']] def __str__(self): return self.name @classmethod def to_xmlrpc(cls, query=None): _query = query or {} qs = distinct_filter(TestPlan, _query).order_by('pk') serializer = TestPlanXMLRPCSerializer(model_class=cls, queryset=qs) return serializer.serialize_queryset() @classmethod def list(cls, query=None): """docstring for list_plans""" new_query = {} for key, value in query.items(): if value and key not in ['action', 't', 'f', 'a']: if key == 'version': # comes from case/clone.html key = 'product_version' new_query[key] = value.strip() if hasattr(value, 'strip') else value query_set = cls.objects if new_query.get('search'): query_set = query_set.filter(Q(plan_id__icontains=new_query['search']) | Q(name__icontains=new_query['search'])) del new_query['search'] return query_set.filter(**new_query).order_by('pk').distinct() def add_case(self, case, sortkey=None):
class TestExecution(TCMSActionModel): history = KiwiHistoricalRecords() case_run_id = models.AutoField(primary_key=True) assignee = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='case_run_assignee', on_delete=models.CASCADE) tested_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='case_run_tester', on_delete=models.CASCADE) case_text_version = models.IntegerField() close_date = models.DateTimeField(null=True, blank=True) sortkey = models.IntegerField(null=True, blank=True) run = models.ForeignKey(TestRun, related_name='case_run', on_delete=models.CASCADE) case = models.ForeignKey('testcases.TestCase', related_name='case_run', on_delete=models.CASCADE) status = models.ForeignKey(TestExecutionStatus, on_delete=models.CASCADE) build = models.ForeignKey('management.Build', on_delete=models.CASCADE) class Meta: unique_together = ('case', 'run', 'case_text_version') def links(self): """ Returns all links attached to this object! """ return LinkReference.objects.filter(execution=self.pk) def __str__(self): return '%s: %s' % (self.pk, self.case_id) @classmethod def to_xmlrpc(cls, query: dict = None): if query is None: query = {} query_set = distinct_filter(TestExecution, query).order_by('pk') serializer = TestExecutionXMLRPCSerializer(model_class=cls, queryset=query_set) return serializer.serialize_queryset() def get_bugs(self): return LinkReference.objects.filter(execution=self.pk, is_defect=True) def get_bugs_count(self): return self.get_bugs().count() def _get_absolute_url(self): # NOTE: this returns the URL to the TestRun containing this TestExecution! return reverse('testruns-get', args=[self.run_id])
class TestExecution(TCMSActionModel): history = KiwiHistoricalRecords() assignee = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, related_name="case_run_assignee", on_delete=models.CASCADE, ) tested_by = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, related_name="case_run_tester", on_delete=models.CASCADE, ) case_text_version = models.IntegerField() close_date = models.DateTimeField(null=True, blank=True) sortkey = models.IntegerField(null=True, blank=True) run = models.ForeignKey(TestRun, related_name="case_run", on_delete=models.CASCADE) case = models.ForeignKey("testcases.TestCase", related_name="case_run", on_delete=models.CASCADE) status = models.ForeignKey(TestExecutionStatus, on_delete=models.CASCADE) build = models.ForeignKey("management.Build", on_delete=models.CASCADE) class Meta: unique_together = ("case", "run", "case_text_version") def links(self): # used in tests return LinkReference.objects.filter(execution=self.pk) def __str__(self): return "%s: %s" % (self.pk, self.case_id) @classmethod def to_xmlrpc(cls, query: dict = None): if query is None: query = {} query_set = distinct_filter(TestExecution, query).order_by("pk") serializer = TestExecutionRPCSerializer(model_class=cls, queryset=query_set) return serializer.serialize_queryset() def get_bugs(self): return LinkReference.objects.filter(execution=self.pk, is_defect=True) def _get_absolute_url(self): # NOTE: this returns the URL to the TestRun containing this TestExecution! return reverse("testruns-get", args=[self.run_id])
class Template(models.Model): history = KiwiHistoricalRecords() name = models.CharField(max_length=255) text = models.TextField(blank=False) class Meta: verbose_name = _("Template") verbose_name_plural = _("Templates") def __str__(self): return self.name
class TestExecution(models.Model, UrlMixin): history = KiwiHistoricalRecords() assignee = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, related_name="execution_assignee", on_delete=models.CASCADE, ) tested_by = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, related_name="execution_tester", on_delete=models.CASCADE, ) case_text_version = models.IntegerField() start_date = models.DateTimeField(null=True, blank=True, db_index=True) stop_date = models.DateTimeField(null=True, blank=True, db_index=True) sortkey = models.IntegerField(null=True, blank=True) run = models.ForeignKey(TestRun, related_name="executions", on_delete=models.CASCADE) case = models.ForeignKey("testcases.TestCase", related_name="executions", on_delete=models.CASCADE) status = models.ForeignKey(TestExecutionStatus, on_delete=models.CASCADE) build = models.ForeignKey("management.Build", on_delete=models.CASCADE) class Meta: unique_together = ("case", "run", "case_text_version") def __str__(self): return "%s: %s" % (self.pk, self.case_id) def links(self): return LinkReference.objects.filter(execution=self.pk) def get_bugs(self): return self.links().filter(is_defect=True) def _get_absolute_url(self): # NOTE: this returns the URL to the TestRun containing this TestExecution! return reverse("testruns-get", args=[self.run_id]) @property def actual_duration(self): if self.stop_date is None or self.start_date is None: return None return self.stop_date - self.start_date
class TestRun(TCMSActionModel): history = KiwiHistoricalRecords() run_id = models.AutoField(primary_key=True) # todo: this field should be removed in favor of plan.product_version # no longer shown in edit forms product_version = models.ForeignKey('management.Version', related_name='version_run', on_delete=models.CASCADE) start_date = models.DateTimeField(auto_now_add=True, db_index=True) stop_date = models.DateTimeField(null=True, blank=True, db_index=True) summary = models.TextField() notes = models.TextField(blank=True) plan = models.ForeignKey('testplans.TestPlan', related_name='run', on_delete=models.CASCADE) build = models.ForeignKey('management.Build', related_name='build_run', on_delete=models.CASCADE) manager = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='manager', on_delete=models.CASCADE) default_tester = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='default_tester', on_delete=models.CASCADE) tag = models.ManyToManyField('management.Tag', through='testruns.TestRunTag', related_name='run') cc = models.ManyToManyField(settings.AUTH_USER_MODEL, through='testruns.TestRunCC') class Meta: unique_together = ('run_id', 'product_version') def __str__(self): return self.summary @classmethod def to_xmlrpc(cls, query=None): _query = query or {} qs = distinct_filter(TestRun, _query).order_by('pk') serializer = TestRunXMLRPCSerializer(model_class=cls, queryset=qs) return serializer.serialize_queryset() def _get_absolute_url(self): return reverse('testruns-get', args=[ self.pk, ]) def get_notify_addrs(self): """ Get the all related mails from the run """ send_to = [self.manager.email] send_to.extend(self.cc.values_list('email', flat=True)) if self.default_tester_id: send_to.append(self.default_tester.email) for tcr in self.case_run.select_related('assignee').all(): if tcr.assignee_id: send_to.append(tcr.assignee.email) send_to = set(send_to) # don't email author of last change send_to.discard( getattr( self.history.latest().history_user, # pylint: disable=no-member 'email', '')) return list(send_to) # FIXME: rewrite to use multiple values INSERT statement def add_case_run(self, case, status=1, assignee=None, case_text_version=None, build=None, sortkey=0):
class TestCase(TCMSActionModel): history = KiwiHistoricalRecords() case_id = models.AutoField(primary_key=True) create_date = models.DateTimeField(db_column='creation_date', auto_now_add=True) is_automated = models.IntegerField(db_column='isautomated', default=0) is_automated_proposed = models.BooleanField(default=False) script = models.TextField(blank=True, null=True) arguments = models.TextField(blank=True, null=True) extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True) summary = models.CharField(max_length=255) requirement = models.CharField(max_length=255, blank=True, null=True) alias = models.CharField(max_length=255, blank=True) estimated_time = models.DurationField(default=timedelta(0)) notes = models.TextField(blank=True, null=True) case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE) category = models.ForeignKey(Category, related_name='category_case', on_delete=models.CASCADE) priority = models.ForeignKey('management.Priority', related_name='priority_case', on_delete=models.CASCADE) author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_author', on_delete=models.CASCADE) default_tester = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_default_tester', blank=True, null=True, on_delete=models.CASCADE) reviewer = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_reviewer', null=True, on_delete=models.CASCADE) # FIXME: related_name should be cases instead of case. But now keep it # named case due to historical reason. plan = models.ManyToManyField('testplans.TestPlan', related_name='case', through='testcases.TestCasePlan') component = models.ManyToManyField('management.Component', related_name='cases', through='testcases.TestCaseComponent') tag = models.ManyToManyField('management.Tag', related_name='case', through='testcases.TestCaseTag') # todo: Auto-generated attributes from back-references: # 'texts' : list of TestCaseTexts (from TestCaseTexts.case) def __str__(self): return self.summary @classmethod def to_xmlrpc(cls, query=None): from tcms.xmlrpc.serializer import TestCaseXMLRPCSerializer from tcms.xmlrpc.utils import distinct_filter _query = query or {} qs = distinct_filter(TestCase, _query).order_by('pk') s = TestCaseXMLRPCSerializer(model_class=cls, queryset=qs) return s.serialize_queryset() @classmethod def create(cls, author, values): """ Create the case element based on models/forms. """ case = cls.objects.create( author=author, is_automated=values['is_automated'], is_automated_proposed=values['is_automated_proposed'], # sortkey = values['sortkey'], script=values['script'], arguments=values['arguments'], extra_link=values['extra_link'], summary=values['summary'], requirement=values['requirement'], alias=values['alias'], estimated_time=values['estimated_time'], case_status=values['case_status'], category=values['category'], priority=values['priority'], default_tester=values['default_tester'], notes=values['notes'], ) tags = values.get('tag') if tags: map(lambda c: case.add_tag(c), tags) return case @classmethod def list(cls, query, plan=None): """List the cases with request""" from django.db.models import Q if not plan: q = cls.objects else: q = cls.objects.filter(plan=plan) if query.get('case_id_set'): q = q.filter(pk__in=query['case_id_set']) if query.get('search'): q = q.filter( Q(pk__icontains=query['search']) | Q(summary__icontains=query['search']) | Q(author__email__startswith=query['search'])) if query.get('summary'): q = q.filter(Q(summary__icontains=query['summary'])) if query.get('author'): q = q.filter( Q(author__first_name__startswith=query['author']) | Q(author__last_name__startswith=query['author']) | Q(author__username__icontains=query['author']) | Q(author__email__startswith=query['author'])) if query.get('default_tester'): q = q.filter( Q(default_tester__first_name__startswith=query[ 'default_tester']) | Q(default_tester__last_name__startswith=query['default_tester'] ) | Q(default_tester__username__icontains=query['default_tester']) | Q(default_tester__email__startswith=query['default_tester'])) if query.get('tag__name__in'): q = q.filter(tag__name__in=query['tag__name__in']) if query.get('category'): q = q.filter(category__name=query['category'].name) if query.get('priority'): q = q.filter(priority__in=query['priority']) if query.get('case_status'): q = q.filter(case_status__in=query['case_status']) # If plan exists, remove leading and trailing whitespace from it. plan_str = query.get('plan', '').strip() if plan_str: try: # Is it an integer? If so treat as a plan_id: plan_id = int(plan_str) q = q.filter(plan__plan_id=plan_id) except ValueError: # Not an integer - treat plan_str as a plan name: q = q.filter(plan__name__icontains=plan_str) del plan_str if query.get('product'): q = q.filter(category__product=query['product']) if query.get('component'): q = q.filter(component=query['component']) if query.get('bug_id'): q = q.filter(case_bug__bug_id__in=query['bug_id']) if query.get('is_automated'): q = q.filter(is_automated=query['is_automated']) if query.get('is_automated_proposed'): q = q.filter(is_automated_proposed=query['is_automated_proposed']) return q.distinct() @classmethod def list_confirmed(cls): confirmed_case_status = TestCaseStatus.get_CONFIRMED() query = { 'case_status_id': confirmed_case_status.case_status_id, } return cls.list(query) def add_bug(self, bug_id, bug_system_id, summary=None, description=None, case_run=None, bz_external_track=False): bug, created = self.case_bug.get_or_create( bug_id=bug_id, case_run=case_run, bug_system_id=bug_system_id, summary=summary, description=description, ) if created: if bz_external_track: bug_system = BugSystem.objects.get(pk=bug_system_id) it = IssueTrackerType.from_name( bug_system.tracker_type)(bug_system) if not it.is_adding_testcase_to_issue_disabled(): it.add_testcase_to_issue([self], bug) else: raise ValueError( 'Enable linking test cases by configuring API parameters ' 'for this Issue Tracker!') else: raise ValueError('Bug %s already exist.' % bug_id) def add_component(self, component): return TestCaseComponent.objects.get_or_create(case=self, component=component) def add_tag(self, tag): return TestCaseTag.objects.get_or_create(case=self, tag=tag) def update_tags(self, new_tags): """ Update case.tag so that case.tag == new_tags """ if new_tags is None or not isinstance(new_tags, list): return owning_tags = set(self.tag.iterator()) new_tags = set(new_tags) tags_to_remove = owning_tags.difference(new_tags) tags_to_add = new_tags.difference(owning_tags) map(lambda c: self.add_tag(c), tags_to_add) map(lambda c: self.remove_tag(c), tags_to_remove) def add_text(self, action, effect, setup, breakdown, author=None, create_date=datetime.now(), case_text_version=1): if not author: author = self.author new_checksum = checksum(action + effect + setup + breakdown) latest_text = self.latest_text() old_checksum = checksum(latest_text.action + latest_text.effect + latest_text.setup + latest_text.breakdown) if old_checksum == new_checksum: return latest_text case_text_version = self.latest_text_version() + 1 return TestCaseText.objects.create(case=self, case_text_version=case_text_version, create_date=create_date, author=author, action=action, effect=effect, setup=setup, breakdown=breakdown) def add_to_plan(self, plan): TestCasePlan.objects.get_or_create(case=self, plan=plan) def clear_components(self): return TestCaseComponent.objects.filter(case=self, ).delete() def get_bugs(self): return Bug.objects.select_related( 'case_run', 'bug_system').filter(case__case_id=self.case_id) def get_components(self): return self.component.all() def get_component_names(self): return self.component.values_list('name', flat=True) def get_choiced(self, obj_value, choices): for x in choices: if x[0] == obj_value: return x[1] def get_is_automated(self): return self.get_choiced(self.is_automated, AUTOMATED_CHOICES) def get_is_automated_form_value(self): if self.is_automated == 2: return [0, 1] return (self.is_automated, ) def get_is_automated_status(self): return self.get_is_automated() + (self.is_automated_proposed and ' (Autoproposed)' or '') def get_previous_and_next(self, pk_list): current_idx = pk_list.index(self.pk) prev = TestCase.objects.get(pk=pk_list[current_idx - 1]) try: next = TestCase.objects.get(pk=pk_list[current_idx + 1]) except IndexError: next = TestCase.objects.get(pk=pk_list[0]) return (prev, next) def get_text_with_version(self, case_text_version=None): if case_text_version: try: return TestCaseText.objects.get( case__case_id=self.case_id, case_text_version=case_text_version) except TestCaseText.DoesNotExist: return NoneText return self.latest_text() def latest_text(self, text_required=True): text = self.text if not text_required: text = text.defer('action', 'effect', 'setup', 'breakdown') qs = text.order_by('-case_text_version')[0:1] return NoneText if len(qs) == 0 else qs[0] def latest_text_version(self): qs = self.text.order_by('-case_text_version').only( 'case_text_version')[0:1] return 0 if len(qs) == 0 else qs[0].case_text_version def mail(self, template, subject, context={}, to=[], request=None): from tcms.core.utils.mailto import mailto if not to: to = self.author.email to = list(set(to)) mailto(template, subject, to, context, request) def remove_bug(self, bug_id, run_id=None): query = Bug.objects.filter(bug_id=bug_id, case=self.pk) if run_id: query = query.filter(case_run=run_id) else: query = query.filter(case_run__isnull=True) query.delete() def remove_component(self, component): # note: cannot use self.component.remove(component) on a ManyToManyField # which specifies an intermediary model so we use the model manager! self.component.through.objects.filter(case=self.pk, component=component.pk).delete() def remove_plan(self, plan): self.plan.through.objects.filter(case=self.pk, plan=plan.pk).delete() def remove_tag(self, tag): self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete() def _get_absolute_url(self, request=None): return reverse('testcases-get', args=[ self.pk, ]) def _get_email_conf(self): try: return self.email_settings except ObjectDoesNotExist: return TestCaseEmailSettings.objects.create(case=self) emailing = property(_get_email_conf)
class TestRun(TCMSActionModel): history = KiwiHistoricalRecords() # todo: this field should be removed in favor of plan.product_version # no longer shown in edit forms product_version = models.ForeignKey("management.Version", related_name="version_run", on_delete=models.CASCADE) start_date = models.DateTimeField(auto_now_add=True, db_index=True) stop_date = models.DateTimeField(null=True, blank=True, db_index=True) summary = models.TextField() notes = models.TextField(blank=True) plan = models.ForeignKey("testplans.TestPlan", related_name="run", on_delete=models.CASCADE) build = models.ForeignKey("management.Build", related_name="build_run", on_delete=models.CASCADE) manager = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="manager", on_delete=models.CASCADE) default_tester = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, related_name="default_tester", on_delete=models.CASCADE, ) tag = models.ManyToManyField("management.Tag", through="testruns.TestRunTag", related_name="run") cc = models.ManyToManyField(settings.AUTH_USER_MODEL, through="testruns.TestRunCC") def __str__(self): return self.summary @classmethod def to_xmlrpc(cls, query=None): _query = query or {} qs = distinct_filter(TestRun, _query).order_by("pk") serializer = TestRunRPCSerializer(model_class=cls, queryset=qs) return serializer.serialize_queryset() def _get_absolute_url(self): return reverse( "testruns-get", args=[ self.pk, ], ) def get_absolute_url(self): return self._get_absolute_url() def get_notify_addrs(self): """ Get the all related mails from the run """ send_to = [self.manager.email] send_to.extend(self.cc.values_list("email", flat=True)) if self.default_tester_id: send_to.append(self.default_tester.email) for tcr in self.case_run.select_related("assignee").all(): if tcr.assignee_id: send_to.append(tcr.assignee.email) send_to = set(send_to) # don't email author of last change send_to.discard( getattr( self.history.latest().history_user, # pylint: disable=no-member "email", "", )) return list(send_to) def create_execution( self, case, status=None, assignee=None, case_text_version=None, build=None, sortkey=0, ):
class TestCase(models.Model, UrlMixin): history = KiwiHistoricalRecords() create_date = models.DateTimeField(auto_now_add=True) is_automated = models.BooleanField(default=False) script = models.TextField(blank=True, null=True) arguments = models.TextField(blank=True, null=True) extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True) summary = models.CharField(max_length=255, db_index=True) requirement = models.CharField(max_length=255, blank=True, null=True) notes = models.TextField(blank=True, null=True) text = models.TextField(blank=True) setup_duration = models.DurationField(db_index=True, null=True, blank=True) testing_duration = models.DurationField(db_index=True, null=True, blank=True) case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE) category = models.ForeignKey(Category, related_name="category_case", on_delete=models.CASCADE) priority = models.ForeignKey("management.Priority", related_name="priority_case", on_delete=models.CASCADE) author = models.ForeignKey( settings.AUTH_USER_MODEL, related_name="cases_as_author", on_delete=models.CASCADE, ) default_tester = models.ForeignKey( settings.AUTH_USER_MODEL, related_name="cases_as_default_tester", blank=True, null=True, on_delete=models.CASCADE, ) reviewer = models.ForeignKey( settings.AUTH_USER_MODEL, related_name="cases_as_reviewer", null=True, on_delete=models.CASCADE, ) plan = models.ManyToManyField("testplans.TestPlan", related_name="cases", through="testcases.TestCasePlan") component = models.ManyToManyField( "management.Component", related_name="cases", through="testcases.TestCaseComponent", ) tag = models.ManyToManyField("management.Tag", related_name="case", through="testcases.TestCaseTag") @property def expected_duration(self): result = timedelta(0) result += self.setup_duration or timedelta(0) result += self.testing_duration or timedelta(0) return result def __str__(self): return self.summary def add_component(self, component): return TestCaseComponent.objects.get_or_create(case=self, component=component) def add_tag(self, tag): return TestCaseTag.objects.get_or_create(case=self, tag=tag) def get_text_with_version(self, case_text_version=None): if case_text_version: try: return self.history.get( # pylint: disable=no-member history_id=case_text_version).text except ObjectDoesNotExist: return self.text return self.text def remove_component(self, component): # note: cannot use self.component.remove(component) on a ManyToManyField # which specifies an intermediary model so we use the model manager! self.component.through.objects.filter(case=self.pk, component=component.pk).delete() def remove_tag(self, tag): self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete() def _get_absolute_url(self, request=None): return reverse( "testcases-get", args=[ self.pk, ], ) def get_absolute_url(self): return self._get_absolute_url() def _get_email_conf(self): try: # note: this is the reverse_name of a 1-to-1 field return self.email_settings # pylint: disable=no-member except ObjectDoesNotExist: return TestCaseEmailSettings.objects.create(case=self) emailing = property(_get_email_conf) def clone(self, new_author, test_plans): values = self.__dict__.copy() del values["_state"] del values["id"] values["case_status_id"] = (TestCaseStatus.objects.filter( is_confirmed=False).first().pk) values["author_id"] = new_author.pk new_tc = self.__class__.objects.create(**values) # apply tags as well for tag in self.tag.all(): new_tc.add_tag(tag) for plan in test_plans: plan.add_case(new_tc) # clone TC category b/c we may be cloning a 'linked' # TC which has a different Product that doesn't have the # same categories yet try: tc_category = plan.product.category.get( name=self.category.name) except ObjectDoesNotExist: tc_category = plan.product.category.create( name=self.category.name, description=self.category.description, ) new_tc.category = tc_category new_tc.save() # clone TC components b/c we may be cloning a 'linked' # TC which has a different Product that doesn't have the # same components yet for component in self.component.all(): try: new_component = plan.product.component.get( name=component.name) except ObjectDoesNotExist: new_component = plan.product.component.create( name=component.name, initial_owner=new_author, description=component.description, ) new_tc.add_component(new_component) return new_tc
class TestCase(TCMSActionModel): history = KiwiHistoricalRecords() create_date = models.DateTimeField(auto_now_add=True) is_automated = models.BooleanField(default=False) script = models.TextField(blank=True, null=True) arguments = models.TextField(blank=True, null=True) extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True) summary = models.CharField(max_length=255, db_index=True) requirement = models.CharField(max_length=255, blank=True, null=True) notes = models.TextField(blank=True, null=True) text = models.TextField(blank=True) case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE) category = models.ForeignKey(Category, related_name="category_case", on_delete=models.CASCADE) priority = models.ForeignKey("management.Priority", related_name="priority_case", on_delete=models.CASCADE) author = models.ForeignKey( settings.AUTH_USER_MODEL, related_name="cases_as_author", on_delete=models.CASCADE, ) default_tester = models.ForeignKey( settings.AUTH_USER_MODEL, related_name="cases_as_default_tester", blank=True, null=True, on_delete=models.CASCADE, ) reviewer = models.ForeignKey( settings.AUTH_USER_MODEL, related_name="cases_as_reviewer", null=True, on_delete=models.CASCADE, ) plan = models.ManyToManyField("testplans.TestPlan", related_name="cases", through="testcases.TestCasePlan") component = models.ManyToManyField( "management.Component", related_name="cases", through="testcases.TestCaseComponent", ) tag = models.ManyToManyField("management.Tag", related_name="case", through="testcases.TestCaseTag") def __str__(self): return self.summary @classmethod def to_xmlrpc(cls, query=None): _query = query or {} qs = distinct_filter(TestCase, _query).order_by("pk") serializer = TestCaseRPCSerializer(model_class=cls, queryset=qs) return serializer.serialize_queryset() def add_component(self, component): return TestCaseComponent.objects.get_or_create(case=self, component=component) def add_tag(self, tag): return TestCaseTag.objects.get_or_create(case=self, tag=tag) def get_text_with_version(self, case_text_version=None): if case_text_version: try: return self.history.get(history_id=case_text_version).text except ObjectDoesNotExist: return self.text return self.text def remove_component(self, component): # note: cannot use self.component.remove(component) on a ManyToManyField # which specifies an intermediary model so we use the model manager! self.component.through.objects.filter(case=self.pk, component=component.pk).delete() def remove_tag(self, tag): self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete() def _get_absolute_url(self, request=None): return reverse( "testcases-get", args=[ self.pk, ], ) def get_absolute_url(self): return self._get_absolute_url() def _get_email_conf(self): try: return self.email_settings except ObjectDoesNotExist: return TestCaseEmailSettings.objects.create(case=self) emailing = property(_get_email_conf) def clone(self, new_author, test_plans): new_tc = self.__class__.objects.create( is_automated=self.is_automated, script=self.script, arguments=self.arguments, extra_link=self.extra_link, summary=self.summary, requirement=self.requirement, case_status=TestCaseStatus.objects.filter( is_confirmed=False).first(), category=self.category, priority=self.priority, notes=self.notes, text=self.text, author=new_author, default_tester=self.default_tester, ) # apply tags as well for tag in self.tag.all(): new_tc.add_tag(tag) for plan in test_plans: plan.add_case(new_tc) # clone TC category b/c we may be cloning a 'linked' # TC which has a different Product that doesn't have the # same categories yet try: tc_category = plan.product.category.get( name=self.category.name) except ObjectDoesNotExist: tc_category = plan.product.category.create( name=self.category.name, description=self.category.description, ) new_tc.category = tc_category new_tc.save() # clone TC components b/c we may be cloning a 'linked' # TC which has a different Product that doesn't have the # same components yet for component in self.component.all(): try: new_component = plan.product.component.get( name=component.name) except ObjectDoesNotExist: new_component = plan.product.component.create( name=component.name, initial_owner=new_author, description=component.description, ) new_tc.add_component(new_component) return new_tc
class TestRun(models.Model, UrlMixin): history = KiwiHistoricalRecords() start_date = models.DateTimeField(auto_now_add=True, db_index=True) stop_date = models.DateTimeField(null=True, blank=True, db_index=True) planned_start = models.DateTimeField(db_index=True, null=True, blank=True) planned_stop = models.DateTimeField(db_index=True, null=True, blank=True) summary = models.TextField() notes = models.TextField(blank=True) plan = models.ForeignKey("testplans.TestPlan", related_name="run", on_delete=models.CASCADE) build = models.ForeignKey("management.Build", related_name="build_run", on_delete=models.CASCADE) manager = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="manager", on_delete=models.CASCADE) default_tester = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, related_name="default_tester", on_delete=models.CASCADE, ) tag = models.ManyToManyField("management.Tag", through="testruns.TestRunTag", related_name="run") cc = models.ManyToManyField(settings.AUTH_USER_MODEL, through="testruns.TestRunCC") def __str__(self): return self.summary def _get_absolute_url(self): return reverse( "testruns-get", args=[ self.pk, ], ) def get_absolute_url(self): return self._get_absolute_url() def get_notify_addrs(self): """ Get the all related mails from the run """ send_to = [self.manager.email] send_to.extend(self.cc.values_list("email", flat=True)) if self.default_tester_id: send_to.append(self.default_tester.email) for execution in self.executions.select_related("assignee").all(): if execution.assignee_id: send_to.append(execution.assignee.email) send_to = set(send_to) # don't email author of last change send_to.discard( getattr( self.history.latest().history_user, # pylint: disable=no-member "email", "", )) return list(send_to) def create_execution( self, case, assignee=None, build=None, sortkey=0, ):
class TestCase(TCMSActionModel): history = KiwiHistoricalRecords() create_date = models.DateTimeField(auto_now_add=True) is_automated = models.BooleanField(default=False) script = models.TextField(blank=True, null=True) arguments = models.TextField(blank=True, null=True) extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True) summary = models.CharField(max_length=255, db_index=True) requirement = models.CharField(max_length=255, blank=True, null=True) notes = models.TextField(blank=True, null=True) text = models.TextField(blank=True) case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE) category = models.ForeignKey(Category, related_name='category_case', on_delete=models.CASCADE) priority = models.ForeignKey('management.Priority', related_name='priority_case', on_delete=models.CASCADE) author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_author', on_delete=models.CASCADE) default_tester = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_default_tester', blank=True, null=True, on_delete=models.CASCADE) reviewer = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_reviewer', null=True, on_delete=models.CASCADE) # FIXME: related_name should be cases instead of case. But now keep it # named case due to historical reason. plan = models.ManyToManyField('testplans.TestPlan', related_name='case', through='testcases.TestCasePlan') component = models.ManyToManyField('management.Component', related_name='cases', through='testcases.TestCaseComponent') tag = models.ManyToManyField('management.Tag', related_name='case', through='testcases.TestCaseTag') def __str__(self): return self.summary @classmethod def to_xmlrpc(cls, query=None): _query = query or {} qs = distinct_filter(TestCase, _query).order_by('pk') serializer = TestCaseRPCSerializer(model_class=cls, queryset=qs) return serializer.serialize_queryset() @classmethod def list(cls, query, plan=None): """List the cases with request""" if not plan: queryset = cls.objects else: queryset = cls.objects.filter(plan=plan) if query.get('case_id_set'): queryset = queryset.filter(pk__in=query['case_id_set']) if query.get('search'): queryset = queryset.filter( Q(pk__icontains=query['search']) | Q(summary__icontains=query['search']) | Q(author__email__startswith=query['search'])) if query.get('summary'): queryset = queryset.filter(Q(summary__icontains=query['summary'])) if query.get('author'): queryset = queryset.filter( Q(author__first_name__startswith=query['author']) | Q(author__last_name__startswith=query['author']) | Q(author__username__icontains=query['author']) | Q(author__email__startswith=query['author'])) if query.get('default_tester'): queryset = queryset.filter( Q(default_tester__first_name__startswith=query[ 'default_tester']) | Q(default_tester__last_name__startswith=query['default_tester'] ) | Q(default_tester__username__icontains=query['default_tester']) | Q(default_tester__email__startswith=query['default_tester'])) if query.get('tag__name__in'): queryset = queryset.filter(tag__name__in=query['tag__name__in']) if query.get('category'): queryset = queryset.filter(category__name=query['category'].name) if query.get('priority'): queryset = queryset.filter(priority__in=query['priority']) if query.get('case_status'): queryset = queryset.filter(case_status__in=query['case_status']) # If plan exists, remove leading and trailing whitespace from it. # todo: this is the same as the if condition above !!! - this entire method # should be removed in favor of API plan_str = query.get('plan', '').strip() if plan_str: try: # Is it an integer? If so treat as a plan_id: plan_id = int(plan_str) queryset = queryset.filter(plan__pk=plan_id) except ValueError: # Not an integer - treat plan_str as a plan name: queryset = queryset.filter(plan__name__icontains=plan_str) del plan_str if query.get('product'): queryset = queryset.filter(category__product=query['product']) if query.get('component'): queryset = queryset.filter(component=query['component']) if query.get('is_automated'): queryset = queryset.filter(is_automated=query['is_automated']) return queryset.distinct() def add_component(self, component): return TestCaseComponent.objects.get_or_create(case=self, component=component) def add_tag(self, tag): return TestCaseTag.objects.get_or_create(case=self, tag=tag) def get_text_with_version(self, case_text_version=None): if case_text_version: try: return self.history.get(history_id=case_text_version).text except ObjectDoesNotExist: return self.text return self.text def remove_component(self, component): # note: cannot use self.component.remove(component) on a ManyToManyField # which specifies an intermediary model so we use the model manager! self.component.through.objects.filter(case=self.pk, component=component.pk).delete() def remove_tag(self, tag): self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete() def _get_absolute_url(self, request=None): return reverse('testcases-get', args=[ self.pk, ]) def get_absolute_url(self): return self._get_absolute_url() def _get_email_conf(self): try: return self.email_settings except ObjectDoesNotExist: return TestCaseEmailSettings.objects.create(case=self) emailing = property(_get_email_conf) def clone(self, new_author, test_plans): new_tc = self.__class__.objects.create( is_automated=self.is_automated, script=self.script, arguments=self.arguments, extra_link=self.extra_link, summary=self.summary, requirement=self.requirement, case_status=TestCaseStatus.get_proposed(), category=self.category, priority=self.priority, notes=self.notes, text=self.text, author=new_author, default_tester=self.default_tester, ) # apply tags as well for tag in self.tag.all(): new_tc.add_tag(tag) for plan in test_plans: plan.add_case(new_tc) # clone TC category b/c we may be cloning a 'linked' # TC which has a different Product that doesn't have the # same categories yet try: tc_category = plan.product.category.get( name=self.category.name) except ObjectDoesNotExist: tc_category = plan.product.category.create( name=self.category.name, description=self.category.description, ) new_tc.category = tc_category new_tc.save() # clone TC components b/c we may be cloning a 'linked' # TC which has a different Product that doesn't have the # same components yet for component in self.component.all(): try: new_component = plan.product.component.get( name=component.name) except ObjectDoesNotExist: new_component = plan.product.component.create( name=component.name, initial_owner=new_author, description=component.description, ) new_tc.add_component(new_component) return new_tc
class TestRun(TCMSActionModel): history = KiwiHistoricalRecords() run_id = models.AutoField(primary_key=True) product_version = models.ForeignKey('management.Version', related_name='version_run', on_delete=models.CASCADE) start_date = models.DateTimeField(auto_now_add=True, db_index=True) stop_date = models.DateTimeField(null=True, blank=True, db_index=True) summary = models.TextField() notes = models.TextField(blank=True) estimated_time = models.DurationField(default=datetime.timedelta(0)) plan = models.ForeignKey('testplans.TestPlan', related_name='run', on_delete=models.CASCADE) environment_id = models.IntegerField(default=0) build = models.ForeignKey('management.Build', related_name='build_run', on_delete=models.CASCADE) manager = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='manager', on_delete=models.CASCADE) default_tester = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='default_tester', on_delete=models.CASCADE) env_value = models.ManyToManyField('management.EnvValue', through='testruns.EnvRunValueMap') tag = models.ManyToManyField('management.Tag', through='testruns.TestRunTag', related_name='run') cc = models.ManyToManyField('auth.User', through='testruns.TestRunCC') class Meta: unique_together = ('run_id', 'product_version') def __str__(self): return self.summary @classmethod def to_xmlrpc(cls, query=None): _query = query or {} qs = distinct_filter(TestRun, _query).order_by('pk') serializer = TestRunXMLRPCSerializer(model_class=cls, queryset=qs) return serializer.serialize_queryset() @classmethod def list(cls, query): mapping = { 'search': lambda value: Q(run_id__icontains=value) | Q(summary__icontains= value), 'summary': lambda value: Q(summary__icontains=value), 'product': lambda value: Q(build__product=value), 'product_version': lambda value: Q(product_version=value), 'plan': plan_by_id_or_name, 'build': lambda value: Q(build=value), 'env_group': lambda value: Q(plan__env_group=value), 'people_id': lambda value: Q(manager__id=value) | Q(default_tester__id=value), 'manager': lambda value: Q(manager=value), 'default_tester': lambda value: Q(default_tester=value), 'tag__name__in': lambda value: Q(tag__name__in=value), 'env_value__value__in': lambda value: Q(env_value__value__in=value), 'case_run__assignee': lambda value: Q(case_run__assignee=value), 'status': lambda value: { 'running': Q(stop_date__isnull=True), 'finished': Q(stop_date__isnull=False), }[value.lower()], 'people': lambda value: { 'default_tester': Q(default_tester=value), 'manager': Q(manager=value), 'people': Q(manager=value) | Q(default_tester=value), # TODO: Remove first one after upgrade to newer version. # query.set can return either '' or None sometimes, so # currently keeping these two lines here is a workaround. '': Q(manager=value) | Q(default_tester=value), None: Q(manager=value) | Q(default_tester=value), }[query.get('people_type')], } conditions = [ mapping[key](value) for key, value in query.items() if value and key in mapping ] runs = cls.objects.filter(*conditions) value = query.get('sortby') if value: runs = runs.order_by(value) return runs.distinct() def _get_absolute_url(self): return reverse('testruns-get', args=[ self.pk, ]) def get_notify_addrs(self): """ Get the all related mails from the run """ send_to = [self.manager.email] send_to.extend(self.cc.values_list('email', flat=True)) if self.default_tester_id: send_to.append(self.default_tester.email) for tcr in self.case_run.select_related('assignee').all(): if tcr.assignee_id: send_to.append(tcr.assignee.email) send_to = set(send_to) # don't email author of last change send_to.discard( getattr(self.history.latest().history_user, 'email', '')) return list(send_to) # FIXME: rewrite to use multiple values INSERT statement def add_case_run(self, case, case_run_status=1, assignee=None, case_text_version=None, build=None, notes=None, sortkey=0):
class TestCaseRun(TCMSActionModel): history = KiwiHistoricalRecords() objects = TestCaseRunManager() case_run_id = models.AutoField(primary_key=True) assignee = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='case_run_assignee', on_delete=models.CASCADE) tested_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='case_run_tester', on_delete=models.CASCADE) case_text_version = models.IntegerField() running_date = models.DateTimeField(null=True, blank=True) close_date = models.DateTimeField(null=True, blank=True) notes = models.TextField(null=True, blank=True) sortkey = models.IntegerField(null=True, blank=True) run = models.ForeignKey(TestRun, related_name='case_run', on_delete=models.CASCADE) case = models.ForeignKey('testcases.TestCase', related_name='case_run', on_delete=models.CASCADE) case_run_status = models.ForeignKey(TestCaseRunStatus, on_delete=models.CASCADE) build = models.ForeignKey('management.Build', on_delete=models.CASCADE) environment_id = models.IntegerField(default=0) class Meta: unique_together = ('case', 'run', 'case_text_version') def links(self): """ Returns all links attached to this object! """ return LinkReference.objects.filter(test_case_run=self.pk) def __str__(self): return '%s: %s' % (self.pk, self.case_id) @classmethod def to_xmlrpc(cls, query={}): qs = distinct_filter(TestCaseRun, query).order_by('pk') serializer = TestCaseRunXMLRPCSerializer(model_class=cls, queryset=qs) return serializer.serialize_queryset() def add_bug(self, bug_id, bug_system_id, summary=None, description=None, bz_external_track=False): return self.case.add_bug(bug_id=bug_id, bug_system_id=bug_system_id, summary=summary, description=description, case_run=self, bz_external_track=bz_external_track) def remove_bug(self, bug_id, run_id=None): self.case.remove_bug(bug_id=bug_id, run_id=run_id) def get_bugs(self): return Bug.objects.filter(case_run__case_run_id=self.case_run_id) def get_bugs_count(self): return self.get_bugs().count() def get_text_versions(self): return TestCaseText.objects.filter(case__pk=self.case.pk).values_list( 'case_text_version', flat=True) def get_text_with_version(self, case_text_version=None): if case_text_version: try: return TestCaseText.objects.get( case__case_id=self.case_id, case_text_version=case_text_version) except TestCaseText.DoesNotExist: return NoneText try: return TestCaseText.objects.get( case__case_id=self.case_id, case_text_version=self.case_text_version) except TestCaseText.DoesNotExist: return NoneText def latest_text(self): try: return TestCaseText.objects.filter( case__case_id=self.case_id).order_by('-case_text_version')[0] except IndexError: return NoneText def _get_absolute_url(self): # NOTE: this returns the URL to the TestRun containing this TestCaseRun! return reverse('testruns-get', args=[self.run_id])
class TestCase(TCMSActionModel): history = KiwiHistoricalRecords() case_id = models.AutoField(primary_key=True) create_date = models.DateTimeField(db_column='creation_date', auto_now_add=True) is_automated = models.BooleanField(default=False) script = models.TextField(blank=True, null=True) arguments = models.TextField(blank=True, null=True) extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True) summary = models.CharField(max_length=255) requirement = models.CharField(max_length=255, blank=True, null=True) notes = models.TextField(blank=True, null=True) text = models.TextField(blank=True) case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE) category = models.ForeignKey(Category, related_name='category_case', on_delete=models.CASCADE) priority = models.ForeignKey('management.Priority', related_name='priority_case', on_delete=models.CASCADE) author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_author', on_delete=models.CASCADE) default_tester = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_default_tester', blank=True, null=True, on_delete=models.CASCADE) reviewer = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_reviewer', null=True, on_delete=models.CASCADE) # FIXME: related_name should be cases instead of case. But now keep it # named case due to historical reason. plan = models.ManyToManyField('testplans.TestPlan', related_name='case', through='testcases.TestCasePlan') component = models.ManyToManyField('management.Component', related_name='cases', through='testcases.TestCaseComponent') tag = models.ManyToManyField('management.Tag', related_name='case', through='testcases.TestCaseTag') def __str__(self): return self.summary @classmethod def to_xmlrpc(cls, query=None): from tcms.xmlrpc.serializer import TestCaseXMLRPCSerializer from tcms.xmlrpc.utils import distinct_filter _query = query or {} qs = distinct_filter(TestCase, _query).order_by('pk') serializer = TestCaseXMLRPCSerializer(model_class=cls, queryset=qs) return serializer.serialize_queryset() # todo: does this check permissions ??? @classmethod def create(cls, author, values): """ Create the case element based on models/forms. """ case = cls.objects.create( author=author, is_automated=values['is_automated'], # sortkey = values['sortkey'], script=values['script'], arguments=values['arguments'], extra_link=values['extra_link'], summary=values['summary'], requirement=values['requirement'], case_status=values['case_status'], category=values['category'], priority=values['priority'], default_tester=values['default_tester'], notes=values['notes'], text=values['text'], ) # todo: should use add_tag tags = values.get('tag') if tags: map(case.add_tag, tags) return case @classmethod def list(cls, query, plan=None): """List the cases with request""" from django.db.models import Q if not plan: queryset = cls.objects else: queryset = cls.objects.filter(plan=plan) if query.get('case_id_set'): queryset = queryset.filter(pk__in=query['case_id_set']) if query.get('search'): queryset = queryset.filter( Q(pk__icontains=query['search']) | Q(summary__icontains=query['search']) | Q(author__email__startswith=query['search']) ) if query.get('summary'): queryset = queryset.filter(Q(summary__icontains=query['summary'])) if query.get('author'): queryset = queryset.filter( Q(author__first_name__startswith=query['author']) | Q(author__last_name__startswith=query['author']) | Q(author__username__icontains=query['author']) | Q(author__email__startswith=query['author']) ) if query.get('default_tester'): queryset = queryset.filter( Q(default_tester__first_name__startswith=query[ 'default_tester']) | Q(default_tester__last_name__startswith=query[ 'default_tester']) | Q(default_tester__username__icontains=query[ 'default_tester']) | Q(default_tester__email__startswith=query[ 'default_tester']) ) if query.get('tag__name__in'): queryset = queryset.filter(tag__name__in=query['tag__name__in']) if query.get('category'): queryset = queryset.filter(category__name=query['category'].name) if query.get('priority'): queryset = queryset.filter(priority__in=query['priority']) if query.get('case_status'): queryset = queryset.filter(case_status__in=query['case_status']) # If plan exists, remove leading and trailing whitespace from it. plan_str = query.get('plan', '').strip() if plan_str: try: # Is it an integer? If so treat as a plan_id: plan_id = int(plan_str) queryset = queryset.filter(plan__plan_id=plan_id) except ValueError: # Not an integer - treat plan_str as a plan name: queryset = queryset.filter(plan__name__icontains=plan_str) del plan_str if query.get('product'): queryset = queryset.filter(category__product=query['product']) if query.get('component'): queryset = queryset.filter(component=query['component']) if query.get('bug_id'): queryset = queryset.filter(case_bug__bug_id__in=query['bug_id']) if query.get('is_automated'): queryset = queryset.filter(is_automated=query['is_automated']) return queryset.distinct() def add_bug(self, bug_id, bug_system_id, summary=None, description=None, case_run=None, bz_external_track=False): bug, created = self.case_bug.get_or_create( bug_id=bug_id, case_run=case_run, bug_system_id=bug_system_id, summary=summary, description=description, ) if created: if bz_external_track: bug_system = BugSystem.objects.get(pk=bug_system_id) issue_tracker = IssueTrackerType.from_name(bug_system.tracker_type)(bug_system) if not issue_tracker.is_adding_testcase_to_issue_disabled(): issue_tracker.add_testcase_to_issue([self], bug) else: raise ValueError('Enable linking test cases by configuring API parameters ' 'for this Issue Tracker!') else: raise ValueError('Bug %s already exist.' % bug_id) def add_component(self, component): return TestCaseComponent.objects.get_or_create(case=self, component=component) def add_tag(self, tag): return TestCaseTag.objects.get_or_create(case=self, tag=tag) def get_bugs(self): return Bug.objects.select_related( 'case_run', 'bug_system' ).filter(case__case_id=self.case_id) def get_previous_and_next(self, pk_list): current_idx = pk_list.index(self.pk) prev = TestCase.objects.get(pk=pk_list[current_idx - 1]) try: _next = TestCase.objects.get(pk=pk_list[current_idx + 1]) except IndexError: _next = TestCase.objects.get(pk=pk_list[0]) return (prev, _next) def get_text_with_version(self, case_text_version=None): if case_text_version: try: return self.history.get(history_id=case_text_version).text except ObjectDoesNotExist: return self.text return self.text def remove_bug(self, bug_id, run_id=None): query = Bug.objects.filter( bug_id=bug_id, case=self.pk ) if run_id: query = query.filter(case_run=run_id) else: query = query.filter(case_run__isnull=True) query.delete() def remove_component(self, component): # note: cannot use self.component.remove(component) on a ManyToManyField # which specifies an intermediary model so we use the model manager! self.component.through.objects.filter(case=self.pk, component=component.pk).delete() def remove_tag(self, tag): self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete() def _get_absolute_url(self, request=None): return reverse('testcases-get', args=[self.pk, ]) def _get_email_conf(self): try: return self.email_settings except ObjectDoesNotExist: return TestCaseEmailSettings.objects.create(case=self) emailing = property(_get_email_conf)