pyOpenSourceProjects 0.1.2__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/PKG-INFO +3 -2
  2. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/README.md +2 -1
  3. pyopensourceprojects-0.2.0/mkdocs.yml +18 -0
  4. pyopensourceprojects-0.2.0/osprojects/__init__.py +1 -0
  5. pyopensourceprojects-0.2.0/osprojects/checkos.py +138 -0
  6. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/osprojects/osproject.py +175 -45
  7. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/pyproject.toml +6 -3
  8. pyopensourceprojects-0.2.0/scripts/doc +88 -0
  9. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/tests/basetest.py +1 -0
  10. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/tests/test_osproject.py +72 -1
  11. pyopensourceprojects-0.1.2/osprojects/__init__.py +0 -1
  12. pyopensourceprojects-0.1.2/scripts/doc +0 -88
  13. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/.github/workflows/build.yml +0 -0
  14. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/.github/workflows/upload-to-pypi.yml +0 -0
  15. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/.gitignore +0 -0
  16. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/.project +0 -0
  17. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/.pydevproject +0 -0
  18. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/LICENSE +0 -0
  19. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/scripts/blackisort +0 -0
  20. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/scripts/install +0 -0
  21. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/scripts/installAndTest +0 -0
  22. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/scripts/test +0 -0
  23. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.0}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pyOpenSourceProjects
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Dynamic: Summary
5
5
  Project-URL: Home, https://github.com/WolfgangFahl/pyOpenSourceProjects
6
6
  Project-URL: Documentation, http://wiki.bitplan.com/index.php/pyOpenSourceProjects
@@ -25,10 +25,11 @@ Description-Content-Type: text/markdown
25
25
  Helper Library to organize open source Projects
26
26
 
27
27
  [![pypi](https://img.shields.io/pypi/pyversions/pyOpenSourceProjects)](https://pypi.org/project/pyOpenSourceProjects/)
28
- [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/workflows/Build/badge.svg?branch=main)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions?query=workflow%3ABuild+branch%3Amain)
28
+ [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml)
29
29
  [![PyPI Status](https://img.shields.io/pypi/v/pyOpenSourceProjects.svg)](https://pypi.python.org/pypi/pyOpenSourceProjects/)
30
30
  [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues)
31
31
  [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues/?q=is%3Aissue+is%3Aclosed)
32
+ [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyOpenSourceProjects/)
32
33
  [![License](https://img.shields.io/github/license/WolfgangFahl/pyOpenSourceProjects.svg)](https://www.apache.org/licenses/LICENSE-2.0)
33
34
 
34
35
 
@@ -2,10 +2,11 @@
2
2
  Helper Library to organize open source Projects
3
3
 
4
4
  [![pypi](https://img.shields.io/pypi/pyversions/pyOpenSourceProjects)](https://pypi.org/project/pyOpenSourceProjects/)
5
- [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/workflows/Build/badge.svg?branch=main)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions?query=workflow%3ABuild+branch%3Amain)
5
+ [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml)
6
6
  [![PyPI Status](https://img.shields.io/pypi/v/pyOpenSourceProjects.svg)](https://pypi.python.org/pypi/pyOpenSourceProjects/)
7
7
  [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues)
8
8
  [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues/?q=is%3Aissue+is%3Aclosed)
9
+ [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyOpenSourceProjects/)
9
10
  [![License](https://img.shields.io/github/license/WolfgangFahl/pyOpenSourceProjects.svg)](https://www.apache.org/licenses/LICENSE-2.0)
10
11
 
11
12
 
@@ -0,0 +1,18 @@
1
+ site_name: pyOpenSourceProjects API Documentation
2
+ theme:
3
+ name: material
4
+ plugins:
5
+ - search
6
+ - mkdocstrings:
7
+ handlers:
8
+ python:
9
+ setup_commands:
10
+ - import sys
11
+ - import os
12
+ - sys.path.insert(0, os.path.abspath("."))
13
+ selection:
14
+ docstring_style: google
15
+ rendering:
16
+ show_source: true
17
+ nav:
18
+ - API: index.md
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Created on 2024-07-30
4
+
5
+ @author: wf
6
+ """
7
+ from dataclasses import dataclass
8
+ import argparse
9
+ from argparse import Namespace
10
+ import os
11
+ from typing import List
12
+
13
+ from osprojects.osproject import GitHub, OsProject
14
+
15
+ @dataclass
16
+ class Check:
17
+ ok: bool=False
18
+ msg: str=""
19
+
20
+ @property
21
+ def marker(self) -> str:
22
+ return f"✅" if self.ok else f"❌"
23
+
24
+ class CheckOS:
25
+ """
26
+ check the open source projects
27
+ """
28
+
29
+ def __init__(self, args:Namespace,project:OsProject):
30
+ self.args=args
31
+ self.verbose= args.verbose
32
+ self.workspace = args.workspace
33
+ self.project=project
34
+ self.project_path = os.path.join(self.workspace, project.id)
35
+
36
+
37
+ def check_local(self)->Check:
38
+ local=Check(ok=os.path.exists(self.project_path),msg=f"{self.project_path}")
39
+ return local
40
+
41
+ def check_readme(self) -> List[Check]:
42
+ checks=[]
43
+ readme_path = os.path.join(self.project_path, "README.md")
44
+ readme_exists=Check(ok=os.path.exists(readme_path),msg=readme_path)
45
+ checks.append(readme_exists)
46
+ if readme_exists.ok:
47
+ with open(readme_path, "r") as readme_file:
48
+ readme_content = readme_file.read()
49
+ badge_lines = [
50
+ "[![pypi](https://img.shields.io/pypi/pyversions/{self.project.id})](https://pypi.org/project/{self.project.id}/)",
51
+ "[![Github Actions Build](https://github.com/{self.project.fqid}/actions/workflows/build.yml/badge.svg)](https://github.com/{self.project.fqid}/actions/workflows/build.yml)",
52
+ "[![PyPI Status](https://img.shields.io/pypi/v/{self.project.id}.svg)](https://pypi.python.org/pypi/{self.project.id}/)",
53
+ "[![GitHub issues](https://img.shields.io/github/issues/{self.project.fqid}.svg)](https://github.com/{self.project.fqid}/issues)",
54
+ "[![GitHub closed issues](https://img.shields.io/github/issues-closed/{self.project.fqid}.svg)](https://github.com/{self.project.fqid}/issues/?q=is%3Aissue+is%3Aclosed)",
55
+ "[![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://{self.project.owner}.github.io/{self.project.id}/)",
56
+ "[![License](https://img.shields.io/github/license/{self.project.fqid}.svg)](https://www.apache.org/licenses/LICENSE-2.0)"
57
+ ]
58
+ for line in badge_lines:
59
+ formatted_line = line.format(self=self)
60
+ checks.append(Check(ok=formatted_line in readme_content, msg=formatted_line))
61
+
62
+
63
+ return checks
64
+
65
+ def check(self) -> List[Check]:
66
+ """
67
+ Check the given project and print results
68
+ """
69
+ checks = []
70
+ checks.append(self.check_local())
71
+ checks.extend(self.check_readme())
72
+ total=len(checks)
73
+ ok_checks = [check for check in checks if check.ok]
74
+ failed_checks = [check for check in checks if not check.ok]
75
+ #ok_count=len(ok_checks)
76
+ failed_count=len(failed_checks)
77
+ summary=f"❌ {failed_count}/{total}" if failed_count>0 else "✅"
78
+ print(f"{self.project} {summary}: {self.project.url}")
79
+ if failed_count>0:
80
+ sorted_checks = ok_checks + failed_checks if self.verbose else failed_checks
81
+
82
+ for i,check in enumerate(sorted_checks):
83
+ print(f" {i+1:3}{check.marker}:{check.msg}")
84
+ return checks
85
+
86
+ def main(_argv=None):
87
+ """
88
+ main command line entry point
89
+ """
90
+ parser = argparse.ArgumentParser(description="Check open source projects")
91
+ parser.add_argument(
92
+ "-o", "--owner", help="project owner or organization", required=True
93
+ )
94
+ parser.add_argument("-p", "--project", help="name of the project")
95
+ parser.add_argument("-l", "--language", help="filter projects by language")
96
+ parser.add_argument(
97
+ "--local", action="store_true", help="check only locally available projects"
98
+ )
99
+ parser.add_argument(
100
+ "-v","--verbose", action="store_true", help="show verbose output"
101
+ )
102
+ parser.add_argument(
103
+ "-ws",
104
+ "--workspace",
105
+ help="(Eclipse) workspace directory",
106
+ default=os.path.expanduser("~/py-workspace"),
107
+ )
108
+
109
+ args = parser.parse_args(args=_argv)
110
+
111
+ github = GitHub()
112
+ if args.project:
113
+ # Check specific project
114
+ projects = [
115
+ github.list_projects_as_os_projects(args.owner, project_name=args.project)
116
+ ]
117
+ else:
118
+ # Check all projects
119
+ projects = github.list_projects_as_os_projects(args.owner)
120
+
121
+ if args.language:
122
+ projects = [p for p in projects if p.language == args.language]
123
+
124
+ if args.local:
125
+ local_projects = []
126
+ for project in projects:
127
+ checker = CheckOS(args=args,project=project)
128
+ if checker.check_local().ok:
129
+ local_projects.append(project)
130
+ projects=local_projects
131
+
132
+
133
+ for project in projects:
134
+ checker = CheckOS(args=args,project=project)
135
+ checker.check()
136
+
137
+ if __name__ == "__main__":
138
+ main()
@@ -3,15 +3,17 @@ Created on 2022-01-24
3
3
 
4
4
  @author: wf
5
5
  """
6
+
6
7
  from __future__ import annotations
7
- import os
8
+
8
9
  import argparse
9
10
  import datetime
10
11
  import json
12
+ import os
11
13
  import re
12
14
  import subprocess
13
15
  import sys
14
- from typing import List, Type
16
+ from typing import List, Optional, Type
15
17
 
16
18
  import requests
17
19
  from dateutil.parser import parse
@@ -55,23 +57,26 @@ class GitHub(TicketSystem):
55
57
  """
56
58
  wrapper for the GitHub api
57
59
  """
60
+
58
61
  @classmethod
59
- def load_access_token(cls)->str:
62
+ def load_access_token(cls) -> str:
60
63
  """
61
64
  if $HOME/.github/access_token.json exists read the token from there
62
65
  """
63
66
  # Specify the path to the access token file
64
- token_file_path = os.path.join(os.getenv('HOME'), '.github', 'access_token.json')
65
-
67
+ token_file_path = os.path.join(
68
+ os.getenv("HOME"), ".github", "access_token.json"
69
+ )
70
+
66
71
  # Check if the file exists and read the token
67
72
  if os.path.exists(token_file_path):
68
- with open(token_file_path, 'r') as token_file:
73
+ with open(token_file_path, "r") as token_file:
69
74
  token_data = json.load(token_file)
70
- return token_data.get('access_token')
71
-
75
+ return token_data.get("access_token")
76
+
72
77
  # Return None if no token file is found
73
78
  return None
74
-
79
+
75
80
  @classmethod
76
81
  def prepare_headers(cls, access_token: str = None) -> dict:
77
82
  """
@@ -80,15 +85,108 @@ class GitHub(TicketSystem):
80
85
  if access_token is None:
81
86
  access_token = cls.load_access_token()
82
87
 
83
- headers = {'Authorization': f'token {access_token}'} if access_token else {}
88
+ headers = {"Authorization": f"token {access_token}"} if access_token else {}
84
89
  return headers
85
90
 
86
91
  @classmethod
87
- def getIssues(cls,
88
- project: OsProject,
89
- access_token:str=None,
90
- limit: int = None,
91
- **params) -> List[Ticket]:
92
+ def list_projects_as_os_projects(
93
+ cls, owner: str, access_token: str = None, project_name: Optional[str] = None
94
+ ) -> List[OsProject]:
95
+ """
96
+ List all public repositories for a given owner and return them as OsProject instances.
97
+
98
+ Args:
99
+ owner (str): The GitHub username or organization name.
100
+ access_token (str, optional): GitHub personal access token for authentication.
101
+ project_name (str, optional): If provided, return only this specific project.
102
+
103
+ Returns:
104
+ List[OsProject]: A list of OsProject instances representing the repositories.
105
+ """
106
+ headers = cls.prepare_headers(access_token)
107
+
108
+ if project_name:
109
+ url = f"https://api.github.com/repos/{owner}/{project_name}"
110
+ response = requests.get(url, headers=headers)
111
+ if response.status_code != 200:
112
+ raise Exception(
113
+ f"Failed to fetch repository: {response.status_code} - {response.text}"
114
+ )
115
+ repos = [response.json()]
116
+ else:
117
+ url = f"https://api.github.com/users/{owner}/repos"
118
+ params = {
119
+ "type": "all",
120
+ "per_page": 100,
121
+ } # Include all repo types, 100 per page
122
+ all_repos = []
123
+ page = 1
124
+
125
+ while True:
126
+ params["page"] = page
127
+ response = requests.get(url, headers=headers, params=params)
128
+
129
+ if response.status_code != 200:
130
+ raise Exception(
131
+ f"Failed to fetch repositories: {response.status_code} - {response.text}"
132
+ )
133
+
134
+ repos = response.json()
135
+ if not repos:
136
+ break # No more repositories to fetch
137
+
138
+ all_repos.extend(repos)
139
+ page += 1
140
+
141
+ repos = all_repos
142
+
143
+ return [
144
+ OsProject(
145
+ owner=owner,
146
+ id=repo["name"],
147
+ ticketSystem=cls,
148
+ title=repo["name"],
149
+ url=repo["html_url"],
150
+ description=repo["description"],
151
+ language=repo["language"],
152
+ created_at=datetime.datetime.fromisoformat(
153
+ repo["created_at"].rstrip("Z")
154
+ ),
155
+ updated_at=datetime.datetime.fromisoformat(
156
+ repo["updated_at"].rstrip("Z")
157
+ ),
158
+ stars=repo["stargazers_count"],
159
+ forks=repo["forks_count"],
160
+ )
161
+ for repo in repos
162
+ ]
163
+
164
+ @classmethod
165
+ def get_project(
166
+ cls, owner: str, project_id: str, access_token: str = None
167
+ ) -> OsProject:
168
+ """
169
+ Get a specific project as an OsProject instance.
170
+
171
+ Args:
172
+ owner (str): The GitHub username or organization name.
173
+ project_id (str): The name of the project.
174
+ access_token (str, optional): GitHub personal access token for authentication.
175
+
176
+ Returns:
177
+ OsProject: An OsProject instance representing the repository.
178
+ """
179
+ projects = cls.list_projects_as_os_projects(
180
+ owner, access_token, project_name=project_id
181
+ )
182
+ if projects:
183
+ return projects[0]
184
+ raise Exception(f"Project {owner}/{project_id} not found")
185
+
186
+ @classmethod
187
+ def getIssues(
188
+ cls, project: OsProject, access_token: str = None, limit: int = None, **params
189
+ ) -> List[Ticket]:
92
190
  payload = {}
93
191
  headers = cls.prepare_headers(access_token)
94
192
  issues = []
@@ -104,20 +202,24 @@ class GitHub(TicketSystem):
104
202
  data=payload,
105
203
  params=params,
106
204
  )
107
- if response.status_code == 403 and 'rate limit' in response.text:
205
+ if response.status_code == 403 and "rate limit" in response.text:
108
206
  raise Exception("rate limit - you might want to use an access token")
109
207
  issue_records = json.loads(response.text)
110
208
  for record in issue_records:
111
209
  tr = {
112
210
  "project": project,
113
211
  "title": record.get("title"),
114
- "body": record.get("body", ""),
115
- "createdAt": parse(record.get("created_at"))
116
- if record.get("created_at")
117
- else "",
118
- "closedAt": parse(record.get("closed_at"))
119
- if record.get("closed_at")
120
- else "",
212
+ "body": record.get("body", ""),
213
+ "createdAt": (
214
+ parse(record.get("created_at"))
215
+ if record.get("created_at")
216
+ else ""
217
+ ),
218
+ "closedAt": (
219
+ parse(record.get("closed_at"))
220
+ if record.get("closed_at")
221
+ else ""
222
+ ),
121
223
  "state": record.get("state"),
122
224
  "number": record.get("number"),
123
225
  "url": f"{cls.projectUrl(project)}/issues/{record.get('number')}",
@@ -126,7 +228,7 @@ class GitHub(TicketSystem):
126
228
  fetched_count += 1
127
229
  # Check if we have reached the limit
128
230
  if limit is not None and fetched_count >= limit:
129
- nextResults=False
231
+ nextResults = False
130
232
  break
131
233
 
132
234
  if len(issue_records) < 100:
@@ -134,9 +236,11 @@ class GitHub(TicketSystem):
134
236
  else:
135
237
  params["page"] += 1
136
238
  return issues
137
-
239
+
138
240
  @classmethod
139
- def getComments(cls, project: OsProject, issue_number: int, access_token: str = None) -> List[dict]:
241
+ def getComments(
242
+ cls, project: OsProject, issue_number: int, access_token: str = None
243
+ ) -> List[dict]:
140
244
  """
141
245
  Fetch all comments for a specific issue number from GitHub.
142
246
  """
@@ -146,7 +250,9 @@ class GitHub(TicketSystem):
146
250
  if response.status_code == 200:
147
251
  return response.json()
148
252
  else:
149
- raise Exception(f"Failed to fetch comments: {response.status_code} - {response.text}")
253
+ raise Exception(
254
+ f"Failed to fetch comments: {response.status_code} - {response.text}"
255
+ )
150
256
  return []
151
257
 
152
258
  @staticmethod
@@ -160,7 +266,7 @@ class GitHub(TicketSystem):
160
266
  @staticmethod
161
267
  def commitUrl(project: OsProject, id: str):
162
268
  return f"{GitHub.projectUrl(project)}/commit/{id}"
163
-
269
+
164
270
  @staticmethod
165
271
  def commentUrl(project: OsProject, issue_number: int):
166
272
  """
@@ -168,7 +274,6 @@ class GitHub(TicketSystem):
168
274
  """
169
275
  return f"https://api.github.com/repos/{project.owner}/{project.id}/issues/{issue_number}/comments"
170
276
 
171
-
172
277
  @staticmethod
173
278
  def resolveProjectUrl(url: str) -> (str, str):
174
279
  """
@@ -201,29 +306,53 @@ class OsProject(object):
201
306
  self,
202
307
  owner: str = None,
203
308
  id: str = None,
204
- ticketSystem: Type[TicketSystem] = GitHub,
309
+ ticketSystem: Type[TicketSystem] = None,
310
+ title: str = None,
311
+ url: str = None,
312
+ description: str = None,
313
+ language: str = None,
314
+ created_at: datetime.datetime = None,
315
+ updated_at: datetime.datetime = None,
316
+ stars: int = 0,
317
+ forks: int = 0,
205
318
  ):
206
319
  """
207
320
  Constructor
208
321
  """
209
322
  self.owner = owner
210
323
  self.id = id
211
- self.ticketSystem = ticketSystem
324
+ self.ticketSystem = ticketSystem or GitHub
325
+ self.title = title
326
+ self.url = url
327
+ self.description = description
328
+ self.language = language
329
+ self.created_at = created_at
330
+ self.updated_at = updated_at
331
+ self.stars = stars
332
+ self.forks = forks
333
+
334
+ @property
335
+ def fqid(self):
336
+ fqid=f"{self.owner}/{self.id}"
337
+ return fqid
338
+
339
+ def __str__(self):
340
+ return self.fqid
212
341
 
213
342
  @staticmethod
214
343
  def getSamples():
215
344
  samples = [
216
345
  {
217
346
  "id": "pyOpenSourceProjects",
218
- "state": "",
219
347
  "owner": "WolfgangFahl",
220
348
  "title": "pyOpenSourceProjects",
221
349
  "url": "https://github.com/WolfgangFahl/pyOpenSourceProjects",
222
- "version": "",
223
- "desciption": "Helper Library to organize open source Projects",
224
- "date": datetime.datetime(year=2022, month=1, day=24),
225
- "since": "",
226
- "until": "",
350
+ "description": "Helper Library to organize open source Projects",
351
+ "language": "Python",
352
+ "created_at": datetime.datetime(year=2022, month=1, day=24),
353
+ "updated_at": datetime.datetime(year=2022, month=1, day=24),
354
+ "stars": 5,
355
+ "forks": 2,
227
356
  }
228
357
  ]
229
358
  return samples
@@ -235,8 +364,7 @@ class OsProject(object):
235
364
  """
236
365
  url = subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
237
366
  url = url.decode().strip("\n")
238
- osProject = cls.fromUrl(url)
239
- return osProject
367
+ return cls.fromUrl(url)
240
368
 
241
369
  @classmethod
242
370
  def fromUrl(cls, url: str) -> OsProject:
@@ -244,9 +372,11 @@ class OsProject(object):
244
372
  Init OsProject from given url
245
373
  """
246
374
  if "github.com" in url:
247
- owner, project = GitHub.resolveProjectUrl(url)
248
- if owner and project:
249
- return OsProject(owner=owner, id=project, ticketSystem=GitHub)
375
+ owner, project_id = GitHub.resolveProjectUrl(url)
376
+ if owner and project_id:
377
+ github = GitHub()
378
+ project = github.get_project(owner, project_id)
379
+ return project
250
380
  raise Exception(f"Could not resolve the url '{url}' to a OsProject object")
251
381
 
252
382
  def getIssues(self, **params) -> list:
@@ -254,14 +384,14 @@ class OsProject(object):
254
384
  tickets.sort(key=lambda r: getattr(r, "number"), reverse=True)
255
385
  return tickets
256
386
 
257
- def getAllTickets(self, limit:int=None,**params):
387
+ def getAllTickets(self, limit: int = None, **params):
258
388
  """
259
389
  Get all Tickets of the project - closed and open ones
260
-
390
+
261
391
  Args:
262
392
  limit(int): if set limit the number of tickets retrieved
263
393
  """
264
- issues= self.getIssues(state="all",limit=limit, **params)
394
+ issues = self.getIssues(state="all", limit=limit, **params)
265
395
  return issues
266
396
 
267
397
  def getCommits(self) -> List[Commit]:
@@ -44,10 +44,13 @@ path = "osprojects/__init__.py"
44
44
  test = []
45
45
 
46
46
  [tool.hatch.build.targets.wheel]
47
- packages = [
48
- "osprojects",
49
- ]
47
+ only-include = ["osprojects"]
48
+
49
+ [tool.hatch.build.targets.wheel.sources]
50
+ "osprojects" = "osprojects"
51
+
50
52
 
51
53
  [project.scripts]
52
54
  issue2ticket = "osprojects.osproject:main"
53
55
  gitlog2wiki = "osprojects.osproject:gitlog2wiki"
56
+ checkos = "osprojects.checkos:main"
@@ -0,0 +1,88 @@
1
+ #!/bin/bash
2
+ # create docs for a configurable project
3
+ # WF 2024-07-30 - updated
4
+
5
+ # Extract project name from pyproject.toml
6
+ PROJECT_NAME=$(grep "\[project\]" pyproject.toml -A1 | grep name | cut -d '=' -f2 | tr -d ' "')
7
+ PACKAGE_NAME=$(grep "\[tool.hatch.build.targets.wheel.sources\]" pyproject.toml -A1 | tail -1 | cut -d '=' -f2 | tr -d ' "')
8
+
9
+
10
+ # Function to print usage information
11
+ print_usage() {
12
+ echo "Usage: $0 [OPTIONS]"
13
+ echo "Options:"
14
+ echo " -pr, --project NAME Set the project name (default: $PROJECT_NAME)"
15
+ echo " -pa, --package NAME Set the package name (default: $PACKAGE_NAME)"
16
+ echo " -d, --deploy Deploy the documentation after building"
17
+ echo " -h, --help Display this help message"
18
+ }
19
+
20
+ # Parse command line arguments
21
+ DEPLOY=false
22
+ while [[ "$#" -gt 0 ]]; do
23
+ case $1 in
24
+ -pr|--project) PROJECT_NAME="$2"; shift ;;
25
+ -pa|--package) PACKAGE_NAME="$2"; shift ;;
26
+ -d|--deploy) DEPLOY=true ;;
27
+ -h|--help) print_usage; exit 0 ;;
28
+ *) echo "Unknown parameter: $1"; print_usage; exit 1 ;;
29
+ esac
30
+ shift
31
+ done
32
+
33
+ # Ensure we're in the correct directory
34
+ if [[ ! -d "$PACKAGE_NAME" ]]; then
35
+ echo "Error: $PACKAGE_NAME package directory not found. Are you in the correct directory?"
36
+ exit 1
37
+ fi
38
+
39
+ # Check if mkdocs is installed
40
+ if ! command -v mkdocs &> /dev/null; then
41
+ pip install mkdocs mkdocs-material mkdocstrings[python]
42
+ fi
43
+
44
+ # Create or update mkdocs.yml
45
+ cat << EOF > mkdocs.yml
46
+ site_name: $PROJECT_NAME API Documentation
47
+ theme:
48
+ name: material
49
+ plugins:
50
+ - search
51
+ - mkdocstrings:
52
+ handlers:
53
+ python:
54
+ setup_commands:
55
+ - import sys
56
+ - import os
57
+ - sys.path.insert(0, os.path.abspath("."))
58
+ selection:
59
+ docstring_style: google
60
+ rendering:
61
+ show_source: true
62
+ nav:
63
+ - API: index.md
64
+ EOF
65
+
66
+ # Create or update index.md
67
+ index_md=docs/index.md
68
+ mkdir -p docs
69
+ cat << EOF > $index_md
70
+ # $PROJECT_NAME API Documentation
71
+
72
+ ::: $PACKAGE_NAME
73
+ options:
74
+ show_submodules: true
75
+ EOF
76
+
77
+ # Ignore DeprecationWarnings during build
78
+ export PYTHONWARNINGS="ignore::DeprecationWarning"
79
+
80
+ # Build the documentation
81
+ mkdocs build --config-file ./mkdocs.yml
82
+
83
+ # Deploy if requested
84
+ if [ "$DEPLOY" = true ]; then
85
+ mkdocs gh-deploy --force --config-file ./mkdocs.yml
86
+ fi
87
+
88
+ echo "Documentation process completed for $PROJECT_NAME."
@@ -3,6 +3,7 @@ Created on 2021-08-19
3
3
 
4
4
  @author: wf
5
5
  """
6
+
6
7
  import getpass
7
8
  import io
8
9
  import os
@@ -3,6 +3,7 @@ Created on 2022-01-24
3
3
 
4
4
  @author: wf
5
5
  """
6
+
6
7
  import unittest
7
8
 
8
9
  from osprojects.osproject import Commit, GitHub, OsProject, Ticket, gitlog2wiki, main
@@ -23,7 +24,7 @@ class TestOsProject(BaseTest):
23
24
  expectedTicket = self.getSampleById(Ticket, "number", 2)
24
25
  expectedTicket.project = osProject
25
26
  comparison_ticket_dict = tickets[-2].__dict__
26
- comparison_ticket_dict.pop('body', None)
27
+ comparison_ticket_dict.pop("body", None)
27
28
  self.assertDictEqual(expectedTicket.__dict__, comparison_ticket_dict)
28
29
  commit = Commit()
29
30
  ticket = Ticket()
@@ -102,6 +103,76 @@ class TestGitHub(BaseTest):
102
103
  self.assertEqual(expectedOwner, owner)
103
104
  self.assertEqual(expectedProject, project)
104
105
 
106
+ def testListProjects(self):
107
+ """
108
+ tests the list_projects_as_os_projects method
109
+ """
110
+ owner = "WolfgangFahl"
111
+ github = GitHub()
112
+
113
+ # Test list_projects_as_os_projects
114
+ projects = github.list_projects_as_os_projects(owner)
115
+ debug = self.debug
116
+ debug = True
117
+ if debug:
118
+ for project in projects:
119
+ print(project)
120
+ self.assertIsInstance(projects, list)
121
+ self.assertTrue(len(projects) > 0, "No projects found for WolfgangFahl")
122
+
123
+ # Check if pyOpenSourceProjects is in the list
124
+ pyosp_found = any(project.id == "pyOpenSourceProjects" for project in projects)
125
+ self.assertTrue(
126
+ pyosp_found, "pyOpenSourceProjects not found in the list of projects"
127
+ )
128
+
129
+ # Test a sample project's structure
130
+ sample_project = projects[0]
131
+ expected_attributes = {
132
+ "id",
133
+ "owner",
134
+ "title",
135
+ "url",
136
+ "description",
137
+ "language",
138
+ "created_at",
139
+ "updated_at",
140
+ "stars",
141
+ "forks",
142
+ }
143
+ self.assertTrue(
144
+ all(hasattr(sample_project, attr) for attr in expected_attributes),
145
+ "OsProject instance is missing expected attributes",
146
+ )
147
+
148
+ # Check if all items are OsProject instances
149
+ self.assertTrue(
150
+ all(isinstance(project, OsProject) for project in projects),
151
+ "Not all items are OsProject instances",
152
+ )
153
+
154
+ # Test a sample OsProject
155
+ sample_os_project = projects[0]
156
+ self.assertEqual(sample_os_project.owner, owner)
157
+ self.assertIsInstance(sample_os_project.id, str)
158
+ self.assertEqual(sample_os_project.ticketSystem, GitHub)
159
+
160
+ def testGetSpecificProject(self):
161
+ """
162
+ tests getting a specific project
163
+ """
164
+ owner = "WolfgangFahl"
165
+ project_name = "pyOpenSourceProjects"
166
+ github = GitHub()
167
+
168
+ project = github.list_projects_as_os_projects(owner, project_name=project_name)[
169
+ 0
170
+ ]
171
+ self.assertIsInstance(project, OsProject)
172
+ self.assertEqual(project.id, project_name)
173
+ self.assertEqual(project.owner, owner)
174
+ self.assertEqual(project.ticketSystem, GitHub)
175
+
105
176
 
106
177
  class TestCommit(BaseTest):
107
178
  """
@@ -1 +0,0 @@
1
- __version__ = "0.1.2"
@@ -1,88 +0,0 @@
1
- #!/bin/bash
2
- # WF 2020-01-31
3
-
4
- #
5
- # check whether the given command is installed
6
- #
7
- checkinstalled() {
8
- local l_cmd="$1"
9
- which $l_cmd > /dev/null
10
- if [ $? -ne 0 ]
11
- then
12
- echo "$l_cmd need to be installed" 1>&2
13
- exit 1
14
- fi
15
- }
16
-
17
- fixconf() {
18
- local l_year="$1"
19
- local l_author="$2"
20
- conf=conf.py
21
- # fix sys path
22
- # https://stackoverflow.com/questions/10324393/sphinx-build-fail-autodoc-cant-import-find-module
23
- grep "# sys.path" $conf
24
- if [ $? -eq 0 ]
25
- then
26
- tmpconf=/tmp/conf$$.py
27
- cat $conf | awk -v author="$l_author" -v year="$l_year" '
28
- BEGIN {
29
- quote="\x27"
30
- squote="\047"
31
- }
32
- /# import os/ { next }
33
- /# import sys/ { next }
34
- /copyright/ {
35
- printf "copyright = %s%s, %s%s\n",squote,year,author,squote
36
- next
37
- }
38
- /author/ {
39
- printf "author = %s%s%s\n",squote,author,squote
40
- next
41
- }
42
- /html_theme = / {
43
- # html_theme = 'alabaster'
44
- printf "html_theme = %ssphinx_rtd_theme%s\n",squote,squote
45
- printf "master_doc = %sindex%s\n",squote,squote
46
- next
47
- }
48
- # add sphinx_rtd extension
49
- /extensions = / {
50
- print $0
51
- printf "\t%ssphinx_rtd_theme%s,\n",squote,squote
52
- printf "\t%ssphinx.ext.napoleon%s,\n",squote,squote
53
- next
54
- }
55
- /# sys.path/ {
56
- print("#https://stackoverflow.com/a/44980548/1497139")
57
- print("import os")
58
- print("import sys")
59
- print("import sphinx_rtd_theme")
60
- printf("basepath=os.path.abspath(%s../..%s)\n",squote,squote)
61
- printf("print(%sadding basepath %%s%s %% (basepath))\n",squote,squote)
62
- print("sys.path.insert(0, basepath)")
63
- printf("print(%ssys.path is now: %%s%s %% (sys.path))\n",squote,squote)
64
- next
65
- }
66
- { print }
67
- END {
68
- print ("#additional settings")
69
- print ("#https://stackoverflow.com/a/5599712/1497139")
70
- print ("autoclass_content = '\''both'\''")
71
- }' > $tmpconf
72
- #diff $tmpconf $conf
73
- mv $tmpconf $conf
74
- echo "$src/conf.py has been fixed"
75
- fi
76
- }
77
-
78
- src=docs/source
79
- checkinstalled sphinx-apidoc
80
- sphinx-apidoc --full -f -o $src .
81
- cd $src
82
-
83
- fixconf 2020-2021 "Wolfgang Fahl"
84
- make clean html
85
- if [ "$GHACTIONS" != "ACTIVE" ]
86
- then
87
- open _build/html/index.html
88
- fi