7
7
import yaml
8
8
from django .db import models
9
9
from django .db .models import Sum
10
+ from requests .exceptions import RequestException
10
11
11
12
from apps .common .open_ai import OpenAi
12
13
from apps .github .constants import (
@@ -40,6 +41,9 @@ class Meta:
40
41
)
41
42
is_active = models .BooleanField (verbose_name = "Is active" , default = True )
42
43
44
+ leaders_raw = models .JSONField (
45
+ verbose_name = "Entity leaders list" , default = list , blank = True , null = True
46
+ )
43
47
tags = models .JSONField (verbose_name = "OWASP metadata tags" , default = list )
44
48
topics = models .JSONField (
45
49
verbose_name = "GitHub repository topics" , default = list , blank = True , null = True
@@ -62,6 +66,16 @@ def github_url(self):
62
66
"""Get GitHub URL."""
63
67
return f"https://github.com/owasp/{ self .key } "
64
68
69
+ @property
70
+ def leaders_md_raw_url (self ):
71
+ """Return entity's raw leaders.md GitHub URL."""
72
+ return (
73
+ "https://raw.githubusercontent.com/OWASP/"
74
+ f"{ self .owasp_repository .key } /{ self .owasp_repository .default_branch } /leaders.md"
75
+ if self .owasp_repository
76
+ else None
77
+ )
78
+
65
79
@property
66
80
def owasp_name (self ):
67
81
"""Get OWASP name."""
@@ -79,6 +93,9 @@ def deactivate(self):
79
93
80
94
def from_github (self , field_mapping , repository ):
81
95
"""Update instance based on GitHub repository data."""
96
+ # Get leaders.
97
+ self .leaders_raw = self .get_leaders ()
98
+
82
99
# Normalize tags.
83
100
self .tags = (
84
101
[tag .strip (", " ) for tag in self .tags .split ("," if "," in self .tags else " " )]
@@ -126,6 +143,34 @@ def get_index_md_raw_url(self, repository=None):
126
143
else None
127
144
)
128
145
146
+ def get_leaders (self ):
147
+ """Get leaders from leaders.md file on GitHub."""
148
+ leaders = []
149
+
150
+ try :
151
+ content = get_repository_file_content (self .leaders_md_raw_url )
152
+ except (RequestException , ValueError ) as e :
153
+ logger .exception (
154
+ "Failed to fetch leaders.md file" ,
155
+ extra = {"URL" : self .leaders_md_raw_url , "error" : str (e )},
156
+ )
157
+ return leaders
158
+
159
+ if not content :
160
+ return leaders
161
+
162
+ try :
163
+ for line in content .split ("\n " ):
164
+ logger .debug ("Processing line: %s" , line )
165
+ # Match both standard Markdown list items with links and variations.
166
+ leaders .extend (re .findall (r"\*\s*\[([^\]]+)\](?:\([^)]*\))?" , line ))
167
+ except AttributeError :
168
+ logger .exception (
169
+ "Unable to parse leaders.md content" , extra = {"URL" : self .leaders_md_raw_url }
170
+ )
171
+
172
+ return sorted (leaders )
173
+
129
174
def get_top_contributors (self , repositories = ()):
130
175
"""Get top contributors."""
131
176
return [
@@ -153,9 +198,6 @@ class GenericEntityModel(models.Model):
153
198
class Meta :
154
199
abstract = True
155
200
156
- leaders_raw = models .JSONField (
157
- verbose_name = "Entity leaders list" , default = list , blank = True , null = True
158
- )
159
201
related_urls = models .JSONField (
160
202
verbose_name = "Entity related URLs" , default = list , blank = True , null = True
161
203
)
0 commit comments