1
1
import time
2
+ from urllib .parse import urlparse
2
3
3
4
import requests
4
5
from django .conf import settings
@@ -20,6 +21,7 @@ def add_arguments(self, parser):
20
21
21
22
def handle (self , * args , ** kwargs ):
22
23
project_id = kwargs .get ("project_id" )
24
+
23
25
if not project_id :
24
26
self .stdout .write (self .style .ERROR ("Please provide a project ID using --project_id" ))
25
27
return
@@ -30,27 +32,62 @@ def handle(self, *args, **kwargs):
30
32
self .stdout .write (self .style .ERROR (f"Project with ID { project_id } does not exist" ))
31
33
return
32
34
35
+ repo = None
36
+
37
+ if not hasattr (project , "url" ):
38
+ self .stdout .write (self .style .ERROR (f"Project { project .name } does not have a URL attribute" ))
39
+ return
40
+
41
+ if not project .url :
42
+ self .stdout .write (self .style .ERROR (f"Project { project .name } has an empty URL" ))
43
+ return
44
+
45
+ try :
46
+ parsed_url = urlparse (project .url .strip ())
47
+
48
+ if parsed_url .netloc == "github.com" :
49
+ repo_path = parsed_url .path .strip ("/" )
50
+ if repo_path and repo_path .count ("/" ) == 1 :
51
+ repo = repo_path
52
+ else :
53
+ self .stdout .write (
54
+ self .style .ERROR (f"Invalid GitHub repository format: { parsed_url .path } . Expected 'owner/repo'" )
55
+ )
56
+ return
57
+ else :
58
+ self .stdout .write (self .style .ERROR (f"Project URL is not a GitHub repository URL: { project .url } " ))
59
+ return
60
+ except Exception as e :
61
+ self .stdout .write (self .style .ERROR (f"Error parsing URL { project .url } : { str (e )} " ))
62
+ return
63
+
64
+ if not repo :
65
+ self .stdout .write (self .style .ERROR ("Could not extract valid GitHub repository information" ))
66
+ return
67
+
33
68
headers = {
34
69
"Authorization" : f"token { settings .GITHUB_TOKEN } " ,
35
70
"Content-Type" : "application/json" ,
36
71
}
37
72
38
- owner_repo = project .github_url .rstrip ("/" ).split ("/" )[- 2 :]
39
- repo_name = f"{ owner_repo [0 ]} /{ owner_repo [1 ]} "
40
73
contributors = []
41
74
42
75
# Fetch contributors
43
76
page = 1
44
77
while True :
45
- url = f"https://api.github.com/repos/{ repo_name } /contributors?per_page=100&page={ page } "
78
+ url = f"https://api.github.com/repos/{ repo } /contributors?per_page=100&page={ page } "
79
+ self .stdout .write (self .style .SUCCESS (f"Fetching from: { url } " ))
80
+
46
81
response = requests .get (url , headers = headers )
47
82
48
83
if response .status_code != 200 :
49
84
self .stdout .write (
50
- self .style .WARNING (
51
- f"Failed to fetch contributors for { repo_name } page { page } : { response .status_code } "
52
- )
85
+ self .style .WARNING (f"Failed to fetch contributors for { repo } page { page } : { response .status_code } " )
53
86
)
87
+ if response .status_code == 404 :
88
+ self .stdout .write (self .style .ERROR (f"Repository '{ repo } ' not found. Please check the project URL." ))
89
+ elif response .status_code == 403 :
90
+ self .stdout .write (self .style .ERROR ("API rate limit exceeded or authentication issue." ))
54
91
break
55
92
56
93
contributors_data = response .json ()
@@ -59,30 +96,53 @@ def handle(self, *args, **kwargs):
59
96
60
97
for c in contributors_data :
61
98
try :
99
+ name = str (c ["login" ])[:255 ]
100
+ github_url = str (c ["html_url" ])[:255 ]
101
+ avatar_url = str (c ["avatar_url" ])[:255 ]
102
+
62
103
contributor , created = Contributor .objects .get_or_create (
63
104
github_id = c ["id" ],
64
105
defaults = {
65
- "name" : c [ "login" ] ,
66
- "github_url" : c [ "html_url" ] ,
67
- "avatar_url" : c [ " avatar_url" ] ,
68
- "contributor_type" : c ["type" ],
106
+ "name" : name ,
107
+ "github_url" : github_url ,
108
+ "avatar_url" : avatar_url ,
109
+ "contributor_type" : str ( c ["type" ])[: 255 ],
69
110
"contributions" : c ["contributions" ],
70
111
},
71
112
)
113
+
114
+ if not created :
115
+ contributor .contributions = c ["contributions" ]
116
+ contributor .save ()
117
+
72
118
contributors .append (contributor )
119
+
120
+ if created :
121
+ self .stdout .write (f"Created new contributor: { name } " )
122
+ else :
123
+ self .stdout .write (f"Updated existing contributor: { name } " )
124
+
73
125
except MultipleObjectsReturned :
126
+ self .stdout .write (self .style .WARNING (f"Multiple records found for { c ['login' ]} " ))
74
127
contributor = Contributor .objects .filter (github_id = c ["id" ]).first ()
75
128
contributors .append (contributor )
129
+ except Exception as e :
130
+ self .stdout .write (self .style .ERROR (f"Error processing contributor { c ['login' ]} : { str (e )} " ))
76
131
77
132
if "next" not in response .links :
78
133
break
79
134
80
135
page += 1
81
- # Respect GitHub's rate limits
82
136
time .sleep (1 )
83
137
84
- project .contributors .set (contributors )
85
- project .contributor_count = len (contributors )
86
- project .save ()
138
+ # Link contributors to project
139
+ if hasattr (project , "contributors" ):
140
+ project .contributors .set (contributors )
141
+ self .stdout .write (f"Set { len (contributors )} contributors for project { project .name } " )
142
+
143
+ if hasattr (project , "contributor_count" ):
144
+ project .contributor_count = len (contributors )
145
+ project .save ()
146
+ self .stdout .write (f"Updated contributor count to { len (contributors )} " )
87
147
88
148
self .stdout .write (self .style .SUCCESS (f"Successfully fetched contributors for project { project .name } " ))
0 commit comments