@@ -2358,7 +2358,11 @@ def save(self, *args, **kwargs):
23582358 if not self .pk :
23592359 self .schedule_work_id = self .create_new_job (execute_now = True )
23602360 elif self .pk and (existing := PipelineSchedule .objects .get (pk = self .pk )):
2361- if existing .is_active != self .is_active or existing .run_interval != self .run_interval :
2361+ if (
2362+ existing .is_active != self .is_active
2363+ or existing .run_interval != self .run_interval
2364+ or existing .run_priority != self .run_priority
2365+ ):
23622366 self .schedule_work_id = self .create_new_job ()
23632367 self .full_clean ()
23642368 return super ().save (* args , ** kwargs )
@@ -2390,6 +2394,11 @@ def all_runs(self):
23902394 def latest_run (self ):
23912395 return self .pipelineruns .first () if self .pipelineruns .exists () else None
23922396
2397+ @property
2398+ def latest_successful_run (self ):
2399+ successful_runs = self .pipelineruns .filter (run_end_date__isnull = False , run_exitcode = 0 )
2400+ return successful_runs .first () if successful_runs .exists () else None
2401+
23932402 @property
23942403 def earliest_run (self ):
23952404 return self .pipelineruns .earliest ("run_start_date" ) if self .pipelineruns .exists () else None
@@ -2874,21 +2883,10 @@ def to_dict(self):
28742883
28752884class AdvisoryV2QuerySet (BaseQuerySet ):
28762885 def latest_for_avid (self , avid : str ):
2877- return (
2878- self .filter (avid = avid )
2879- .order_by (
2880- F ("date_collected" ).desc (nulls_last = True ),
2881- "-id" ,
2882- )
2883- .first ()
2884- )
2886+ return self .get (avid = avid , is_latest = True )
28852887
28862888 def latest_per_avid (self ):
2887- return self .order_by (
2888- "avid" ,
2889- F ("date_collected" ).desc (nulls_last = True ),
2890- "-id" ,
2891- ).distinct ("avid" )
2889+ return self .filter (is_latest = True )
28922890
28932891 def latest_for_avids (self , avids ):
28942892 return self .filter (avid__in = avids ).latest_per_avid ()
@@ -3005,6 +3003,7 @@ class AdvisoryV2(models.Model):
30053003 max_length = 200 ,
30063004 blank = False ,
30073005 null = False ,
3006+ db_index = True ,
30083007 help_text = "Unique ID for the datasource used for this advisory ." "e.g.: nginx_importer_v2" ,
30093008 )
30103009
@@ -3014,6 +3013,7 @@ class AdvisoryV2(models.Model):
30143013 blank = False ,
30153014 null = False ,
30163015 unique = False ,
3016+ db_index = True ,
30173017 help_text = "An advisory is a unique vulnerability identifier in some database, "
30183018 "such as PYSEC-2020-2233" ,
30193019 )
@@ -3088,6 +3088,14 @@ class AdvisoryV2(models.Model):
30883088 help_text = "UTC Date on which the advisory was collected" ,
30893089 )
30903090
3091+ is_latest = models .BooleanField (
3092+ default = False ,
3093+ blank = False ,
3094+ null = False ,
3095+ db_index = True ,
3096+ help_text = "Indicates whether this is the latest version of the advisory identified by its AVID." ,
3097+ )
3098+
30913099 original_advisory_text = models .TextField (
30923100 blank = True ,
30933101 null = True ,
@@ -3140,6 +3148,11 @@ class AdvisoryV2(models.Model):
31403148 class Meta :
31413149 unique_together = ["datasource_id" , "advisory_id" , "unique_content_id" ]
31423150 ordering = ["datasource_id" , "advisory_id" , "date_published" , "unique_content_id" ]
3151+ constraints = [
3152+ models .UniqueConstraint (
3153+ fields = ["avid" ], condition = Q (is_latest = True ), name = "unique_latest_per_avid"
3154+ )
3155+ ]
31433156 indexes = [
31443157 models .Index (
31453158 fields = ["avid" , "-date_collected" , "-id" ],
0 commit comments