pyOpenSourceProjects 0.0.9__tar.gz → 0.1.1__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.
- pyopensourceprojects-0.1.1/.github/workflows/build.yml +41 -0
- pyopensourceprojects-0.1.1/.github/workflows/upload-to-pypi.yml +27 -0
- pyopensourceprojects-0.1.1/.gitignore +131 -0
- pyopensourceprojects-0.1.1/.project +17 -0
- pyopensourceprojects-0.1.1/.pydevproject +8 -0
- {pyOpenSourceProjects-0.0.9/pyOpenSourceProjects.egg-info → pyopensourceprojects-0.1.1}/PKG-INFO +18 -13
- pyopensourceprojects-0.1.1/osprojects/__init__.py +1 -0
- pyopensourceprojects-0.1.1/osprojects/osproject.py +407 -0
- pyopensourceprojects-0.1.1/pyproject.toml +53 -0
- pyopensourceprojects-0.1.1/scripts/blackisort +7 -0
- pyopensourceprojects-0.1.1/scripts/doc +88 -0
- pyopensourceprojects-0.1.1/scripts/install +4 -0
- pyopensourceprojects-0.1.1/scripts/installAndTest +7 -0
- pyopensourceprojects-0.1.1/scripts/test +9 -0
- pyopensourceprojects-0.1.1/tests/basetest.py +114 -0
- pyopensourceprojects-0.1.1/tests/test_osproject.py +120 -0
- pyOpenSourceProjects-0.0.9/PKG-INFO +0 -35
- pyOpenSourceProjects-0.0.9/osprojects/osproject.py +0 -305
- pyOpenSourceProjects-0.0.9/pyOpenSourceProjects.egg-info/SOURCES.txt +0 -11
- pyOpenSourceProjects-0.0.9/pyOpenSourceProjects.egg-info/dependency_links.txt +0 -1
- pyOpenSourceProjects-0.0.9/pyOpenSourceProjects.egg-info/entry_points.txt +0 -3
- pyOpenSourceProjects-0.0.9/pyOpenSourceProjects.egg-info/requires.txt +0 -2
- pyOpenSourceProjects-0.0.9/pyOpenSourceProjects.egg-info/top_level.txt +0 -1
- pyOpenSourceProjects-0.0.9/setup.cfg +0 -4
- pyOpenSourceProjects-0.0.9/setup.py +0 -48
- {pyOpenSourceProjects-0.0.9 → pyopensourceprojects-0.1.1}/LICENSE +0 -0
- {pyOpenSourceProjects-0.0.9 → pyopensourceprojects-0.1.1}/README.md +0 -0
- {pyOpenSourceProjects-0.0.9/osprojects → pyopensourceprojects-0.1.1/tests}/__init__.py +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
|
3
|
+
|
|
4
|
+
name: Build
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [ main ]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [ main ]
|
|
11
|
+
|
|
12
|
+
env:
|
|
13
|
+
GHACTIONS: ACTIVE
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build:
|
|
17
|
+
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
strategy:
|
|
20
|
+
matrix:
|
|
21
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
22
|
+
#os: [ubuntu-latest]
|
|
23
|
+
python-version: [3.9, "3.10", "3.11", "3.12"]
|
|
24
|
+
#python-version: ["3.10"]
|
|
25
|
+
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
29
|
+
uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: ${{ matrix.python-version }}
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: |
|
|
34
|
+
python -m pip install --upgrade pip
|
|
35
|
+
pip install sphinx
|
|
36
|
+
pip install sphinx_rtd_theme
|
|
37
|
+
scripts/install
|
|
38
|
+
scripts/doc
|
|
39
|
+
- name: Run tests
|
|
40
|
+
run: |
|
|
41
|
+
scripts/installAndTest
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Upload Python Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [created]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
deploy:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
# IMPORTANT: this permission is mandatory for trusted publishing
|
|
12
|
+
id-token: write
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- name: Set up Python
|
|
16
|
+
uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: '3.x'
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: |
|
|
21
|
+
python -m pip install --upgrade pip
|
|
22
|
+
pip install hatch
|
|
23
|
+
- name: Build and publish
|
|
24
|
+
run: |
|
|
25
|
+
hatch build
|
|
26
|
+
- name: Publish distribution to PyPI
|
|
27
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
target/
|
|
76
|
+
|
|
77
|
+
# Jupyter Notebook
|
|
78
|
+
.ipynb_checkpoints
|
|
79
|
+
|
|
80
|
+
# IPython
|
|
81
|
+
profile_default/
|
|
82
|
+
ipython_config.py
|
|
83
|
+
|
|
84
|
+
# pyenv
|
|
85
|
+
.python-version
|
|
86
|
+
|
|
87
|
+
# pipenv
|
|
88
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
89
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
90
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
91
|
+
# install all needed dependencies.
|
|
92
|
+
#Pipfile.lock
|
|
93
|
+
|
|
94
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
95
|
+
__pypackages__/
|
|
96
|
+
|
|
97
|
+
# Celery stuff
|
|
98
|
+
celerybeat-schedule
|
|
99
|
+
celerybeat.pid
|
|
100
|
+
|
|
101
|
+
# SageMath parsed files
|
|
102
|
+
*.sage.py
|
|
103
|
+
|
|
104
|
+
# Environments
|
|
105
|
+
.env
|
|
106
|
+
.venv
|
|
107
|
+
env/
|
|
108
|
+
venv/
|
|
109
|
+
ENV/
|
|
110
|
+
env.bak/
|
|
111
|
+
venv.bak/
|
|
112
|
+
|
|
113
|
+
# Spyder project settings
|
|
114
|
+
.spyderproject
|
|
115
|
+
.spyproject
|
|
116
|
+
|
|
117
|
+
# Rope project settings
|
|
118
|
+
.ropeproject
|
|
119
|
+
|
|
120
|
+
# mkdocs documentation
|
|
121
|
+
/site
|
|
122
|
+
|
|
123
|
+
# mypy
|
|
124
|
+
.mypy_cache/
|
|
125
|
+
.dmypy.json
|
|
126
|
+
dmypy.json
|
|
127
|
+
|
|
128
|
+
# Pyre type checker
|
|
129
|
+
.pyre/
|
|
130
|
+
# no docs
|
|
131
|
+
docs
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<projectDescription>
|
|
3
|
+
<name>pyOpenSourceProjects</name>
|
|
4
|
+
<comment></comment>
|
|
5
|
+
<projects>
|
|
6
|
+
</projects>
|
|
7
|
+
<buildSpec>
|
|
8
|
+
<buildCommand>
|
|
9
|
+
<name>org.python.pydev.PyDevBuilder</name>
|
|
10
|
+
<arguments>
|
|
11
|
+
</arguments>
|
|
12
|
+
</buildCommand>
|
|
13
|
+
</buildSpec>
|
|
14
|
+
<natures>
|
|
15
|
+
<nature>org.python.pydev.pythonNature</nature>
|
|
16
|
+
</natures>
|
|
17
|
+
</projectDescription>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<?eclipse-pydev version="1.0"?><pydev_project>
|
|
3
|
+
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
|
4
|
+
<path>/${PROJECT_DIR_NAME}</path>
|
|
5
|
+
</pydev_pathproperty>
|
|
6
|
+
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
|
|
7
|
+
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
|
8
|
+
</pydev_project>
|
{pyOpenSourceProjects-0.0.9/pyOpenSourceProjects.egg-info → pyopensourceprojects-0.1.1}/PKG-INFO
RENAMED
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: pyOpenSourceProjects
|
|
3
|
-
Version: 0.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Author-email: wf@bitplan.com
|
|
7
|
-
Maintainer: Wolfgang Fahl
|
|
8
|
-
License: Apache License
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Dynamic: Summary
|
|
5
|
+
Project-URL: Home, https://github.com/WolfgangFahl/pyOpenSourceProjects
|
|
9
6
|
Project-URL: Documentation, http://wiki.bitplan.com/index.php/pyOpenSourceProjects
|
|
10
|
-
Project-URL:
|
|
11
|
-
|
|
7
|
+
Project-URL: Source, https://github.com/WolfgangFahl/pyOpenSourceProjects
|
|
8
|
+
Author-email: Wolfgang Fahl <wf@bitplan.com>
|
|
9
|
+
Maintainer-email: Wolfgang Fahl <wf@bitplan.com>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
12
|
Classifier: Programming Language :: Python
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Requires-Dist: py-3rdparty-mediawiki>=0.11.3
|
|
19
|
+
Requires-Dist: pylodstorage>=0.11.6
|
|
20
|
+
Requires-Dist: python-dateutil>=2.8.2
|
|
21
|
+
Provides-Extra: test
|
|
16
22
|
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
23
|
|
|
19
24
|
# pyOpenSourceProjects
|
|
20
25
|
Helper Library to organize open source Projects
|
|
@@ -32,4 +37,4 @@ Helper Library to organize open source Projects
|
|
|
32
37
|
|
|
33
38
|
### Authors
|
|
34
39
|
* [Tim Holzheim](https://www.semantic-mediawiki.org/wiki/Tim_Holzheim)
|
|
35
|
-
* [Wolfgang Fahl](http://www.bitplan.com/Wolfgang_Fahl)
|
|
40
|
+
* [Wolfgang Fahl](http://www.bitplan.com/Wolfgang_Fahl)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.1"
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Created on 2022-01-24
|
|
3
|
+
|
|
4
|
+
@author: wf
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
import argparse
|
|
9
|
+
import datetime
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from typing import List, Type
|
|
15
|
+
|
|
16
|
+
import requests
|
|
17
|
+
from dateutil.parser import parse
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TicketSystem(object):
|
|
21
|
+
"""
|
|
22
|
+
platform for hosting OpenSourceProjects and their issues
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def getIssues(self, project: OsProject, **kwargs) -> List[Ticket]:
|
|
27
|
+
"""
|
|
28
|
+
get issues from the TicketSystem for a project
|
|
29
|
+
"""
|
|
30
|
+
return NotImplemented
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def projectUrl(project: OsProject):
|
|
34
|
+
"""
|
|
35
|
+
url of the project
|
|
36
|
+
"""
|
|
37
|
+
return NotImplemented
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def ticketUrl(project: OsProject):
|
|
41
|
+
"""
|
|
42
|
+
url of the ticket/issue list
|
|
43
|
+
"""
|
|
44
|
+
return NotImplemented
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def commitUrl(project: OsProject, id: str):
|
|
48
|
+
"""
|
|
49
|
+
url of the ticket/issue list
|
|
50
|
+
"""
|
|
51
|
+
return NotImplemented
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class GitHub(TicketSystem):
|
|
55
|
+
"""
|
|
56
|
+
wrapper for the GitHub api
|
|
57
|
+
"""
|
|
58
|
+
@classmethod
|
|
59
|
+
def load_access_token(cls)->str:
|
|
60
|
+
"""
|
|
61
|
+
if $HOME/.github/access_token.json exists read the token from there
|
|
62
|
+
"""
|
|
63
|
+
# Specify the path to the access token file
|
|
64
|
+
token_file_path = os.path.join(os.getenv('HOME'), '.github', 'access_token.json')
|
|
65
|
+
|
|
66
|
+
# Check if the file exists and read the token
|
|
67
|
+
if os.path.exists(token_file_path):
|
|
68
|
+
with open(token_file_path, 'r') as token_file:
|
|
69
|
+
token_data = json.load(token_file)
|
|
70
|
+
return token_data.get('access_token')
|
|
71
|
+
|
|
72
|
+
# Return None if no token file is found
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def getIssues(cls,
|
|
77
|
+
project: OsProject,
|
|
78
|
+
access_token:str=None,
|
|
79
|
+
limit: int = None,
|
|
80
|
+
**params) -> List[Ticket]:
|
|
81
|
+
payload = {}
|
|
82
|
+
headers = {}
|
|
83
|
+
if access_token is None:
|
|
84
|
+
access_token = cls.load_access_token()
|
|
85
|
+
if access_token:
|
|
86
|
+
headers = {
|
|
87
|
+
'Authorization': f'token {access_token}'
|
|
88
|
+
}
|
|
89
|
+
issues = []
|
|
90
|
+
nextResults = True
|
|
91
|
+
params["per_page"] = 100
|
|
92
|
+
params["page"] = 1
|
|
93
|
+
fetched_count = 0 # Counter to track the number of issues fetched
|
|
94
|
+
while nextResults:
|
|
95
|
+
response = requests.request(
|
|
96
|
+
"GET",
|
|
97
|
+
GitHub.ticketUrl(project),
|
|
98
|
+
headers=headers,
|
|
99
|
+
data=payload,
|
|
100
|
+
params=params,
|
|
101
|
+
)
|
|
102
|
+
if response.status_code == 403 and 'rate limit' in response.text:
|
|
103
|
+
raise Exception("rate limit - you might want to use an access token")
|
|
104
|
+
issue_records = json.loads(response.text)
|
|
105
|
+
for record in issue_records:
|
|
106
|
+
tr = {
|
|
107
|
+
"project": project,
|
|
108
|
+
"title": record.get("title"),
|
|
109
|
+
"body": record.get("body", ""),
|
|
110
|
+
"createdAt": parse(record.get("created_at"))
|
|
111
|
+
if record.get("created_at")
|
|
112
|
+
else "",
|
|
113
|
+
"closedAt": parse(record.get("closed_at"))
|
|
114
|
+
if record.get("closed_at")
|
|
115
|
+
else "",
|
|
116
|
+
"state": record.get("state"),
|
|
117
|
+
"number": record.get("number"),
|
|
118
|
+
"url": f"{cls.projectUrl(project)}/issues/{record.get('number')}",
|
|
119
|
+
}
|
|
120
|
+
issues.append(Ticket.init_from_dict(**tr))
|
|
121
|
+
fetched_count += 1
|
|
122
|
+
# Check if we have reached the limit
|
|
123
|
+
if limit is not None and fetched_count >= limit:
|
|
124
|
+
nextResults=False
|
|
125
|
+
break
|
|
126
|
+
|
|
127
|
+
if len(issue_records) < 100:
|
|
128
|
+
nextResults = False
|
|
129
|
+
else:
|
|
130
|
+
params["page"] += 1
|
|
131
|
+
return issues
|
|
132
|
+
|
|
133
|
+
@staticmethod
|
|
134
|
+
def projectUrl(project: OsProject):
|
|
135
|
+
return f"https://github.com/{project.owner}/{project.id}"
|
|
136
|
+
|
|
137
|
+
@staticmethod
|
|
138
|
+
def ticketUrl(project: OsProject):
|
|
139
|
+
return f"https://api.github.com/repos/{project.owner}/{project.id}/issues"
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def commitUrl(project: OsProject, id: str):
|
|
143
|
+
return f"{GitHub.projectUrl(project)}/commit/{id}"
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def resolveProjectUrl(url: str) -> (str, str):
|
|
147
|
+
"""
|
|
148
|
+
Resolve project url to owner and project name
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
(owner, project)
|
|
152
|
+
"""
|
|
153
|
+
# https://www.rfc-editor.org/rfc/rfc3986#appendix-B
|
|
154
|
+
pattern = r"((https?:\/\/github\.com\/)|(git@github\.com:))(?P<owner>[^/?#]+)\/(?P<project>[^\./?#]+)(\.git)?"
|
|
155
|
+
match = re.match(pattern=pattern, string=url)
|
|
156
|
+
owner = match.group("owner")
|
|
157
|
+
project = match.group("project")
|
|
158
|
+
if owner and project:
|
|
159
|
+
return owner, project
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class Jira(TicketSystem):
|
|
163
|
+
"""
|
|
164
|
+
wrapper for Jira api
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class OsProject(object):
|
|
169
|
+
"""
|
|
170
|
+
an Open Source Project
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
def __init__(
|
|
174
|
+
self,
|
|
175
|
+
owner: str = None,
|
|
176
|
+
id: str = None,
|
|
177
|
+
ticketSystem: Type[TicketSystem] = GitHub,
|
|
178
|
+
):
|
|
179
|
+
"""
|
|
180
|
+
Constructor
|
|
181
|
+
"""
|
|
182
|
+
self.owner = owner
|
|
183
|
+
self.id = id
|
|
184
|
+
self.ticketSystem = ticketSystem
|
|
185
|
+
|
|
186
|
+
@staticmethod
|
|
187
|
+
def getSamples():
|
|
188
|
+
samples = [
|
|
189
|
+
{
|
|
190
|
+
"id": "pyOpenSourceProjects",
|
|
191
|
+
"state": "",
|
|
192
|
+
"owner": "WolfgangFahl",
|
|
193
|
+
"title": "pyOpenSourceProjects",
|
|
194
|
+
"url": "https://github.com/WolfgangFahl/pyOpenSourceProjects",
|
|
195
|
+
"version": "",
|
|
196
|
+
"desciption": "Helper Library to organize open source Projects",
|
|
197
|
+
"date": datetime.datetime(year=2022, month=1, day=24),
|
|
198
|
+
"since": "",
|
|
199
|
+
"until": "",
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
return samples
|
|
203
|
+
|
|
204
|
+
@classmethod
|
|
205
|
+
def fromRepo(cls):
|
|
206
|
+
"""
|
|
207
|
+
Init OsProject from repo in current working directory
|
|
208
|
+
"""
|
|
209
|
+
url = subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
|
|
210
|
+
url = url.decode().strip("\n")
|
|
211
|
+
osProject = cls.fromUrl(url)
|
|
212
|
+
return osProject
|
|
213
|
+
|
|
214
|
+
@classmethod
|
|
215
|
+
def fromUrl(cls, url: str) -> OsProject:
|
|
216
|
+
"""
|
|
217
|
+
Init OsProject from given url
|
|
218
|
+
"""
|
|
219
|
+
if "github.com" in url:
|
|
220
|
+
owner, project = GitHub.resolveProjectUrl(url)
|
|
221
|
+
if owner and project:
|
|
222
|
+
return OsProject(owner=owner, id=project, ticketSystem=GitHub)
|
|
223
|
+
raise Exception(f"Could not resolve the url '{url}' to a OsProject object")
|
|
224
|
+
|
|
225
|
+
def getIssues(self, **params) -> list:
|
|
226
|
+
tickets = self.ticketSystem.getIssues(self, **params)
|
|
227
|
+
tickets.sort(key=lambda r: getattr(r, "number"), reverse=True)
|
|
228
|
+
return tickets
|
|
229
|
+
|
|
230
|
+
def getAllTickets(self, limit:int=None,**params):
|
|
231
|
+
"""
|
|
232
|
+
Get all Tickets of the project - closed and open ones
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
limit(int): if set limit the number of tickets retrieved
|
|
236
|
+
"""
|
|
237
|
+
issues= self.getIssues(state="all",limit=limit, **params)
|
|
238
|
+
return issues
|
|
239
|
+
|
|
240
|
+
def getCommits(self) -> List[Commit]:
|
|
241
|
+
commits = []
|
|
242
|
+
gitlogCmd = [
|
|
243
|
+
"git",
|
|
244
|
+
"--no-pager",
|
|
245
|
+
"log",
|
|
246
|
+
"--reverse",
|
|
247
|
+
r'--pretty=format:{"name":"%cn","date":"%cI","hash":"%h"}',
|
|
248
|
+
]
|
|
249
|
+
gitLogCommitSubject = ["git", "log", "--format=%s", "-n", "1"]
|
|
250
|
+
rawCommitLogs = subprocess.check_output(gitlogCmd).decode()
|
|
251
|
+
for rawLog in rawCommitLogs.split("\n"):
|
|
252
|
+
log = json.loads(rawLog)
|
|
253
|
+
if log.get("date", None) is not None:
|
|
254
|
+
log["date"] = datetime.datetime.fromisoformat(log["date"])
|
|
255
|
+
log["project"] = self.id
|
|
256
|
+
log["host"] = self.ticketSystem.projectUrl(self)
|
|
257
|
+
log["path"] = ""
|
|
258
|
+
log["subject"] = subprocess.check_output(
|
|
259
|
+
[*gitLogCommitSubject, log["hash"]]
|
|
260
|
+
)[
|
|
261
|
+
:-1
|
|
262
|
+
].decode() # seperate query to avoid json escaping issues
|
|
263
|
+
commit = Commit()
|
|
264
|
+
for k, v in log.items():
|
|
265
|
+
setattr(commit, k, v)
|
|
266
|
+
commits.append(commit)
|
|
267
|
+
return commits
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class Ticket(object):
|
|
271
|
+
"""
|
|
272
|
+
a Ticket
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
@staticmethod
|
|
276
|
+
def getSamples():
|
|
277
|
+
samples = [
|
|
278
|
+
{
|
|
279
|
+
"number": 2,
|
|
280
|
+
"title": "Get Tickets in Wiki notation from github API",
|
|
281
|
+
"createdAt": datetime.datetime.fromisoformat(
|
|
282
|
+
"2022-01-24 07:41:29+00:00"
|
|
283
|
+
),
|
|
284
|
+
"closedAt": datetime.datetime.fromisoformat(
|
|
285
|
+
"2022-01-25 07:43:04+00:00"
|
|
286
|
+
),
|
|
287
|
+
"url": "https://github.com/WolfgangFahl/pyOpenSourceProjects/issues/2",
|
|
288
|
+
"project": "pyOpenSourceProjects",
|
|
289
|
+
"state": "closed",
|
|
290
|
+
}
|
|
291
|
+
]
|
|
292
|
+
return samples
|
|
293
|
+
|
|
294
|
+
@classmethod
|
|
295
|
+
def init_from_dict(cls, **records):
|
|
296
|
+
"""
|
|
297
|
+
inits Ticket from given args
|
|
298
|
+
"""
|
|
299
|
+
issue = Ticket()
|
|
300
|
+
for k, v in records.items():
|
|
301
|
+
setattr(issue, k, v)
|
|
302
|
+
return issue
|
|
303
|
+
|
|
304
|
+
def toWikiMarkup(self) -> str:
|
|
305
|
+
"""
|
|
306
|
+
Returns Ticket in wiki markup
|
|
307
|
+
"""
|
|
308
|
+
return f"""# {{{{Ticket
|
|
309
|
+
|number={self.number}
|
|
310
|
+
|title={self.title}
|
|
311
|
+
|project={self.project.id}
|
|
312
|
+
|createdAt={self.createdAt if self.createdAt else ""}
|
|
313
|
+
|closedAt={self.closedAt if self.closedAt else ""}
|
|
314
|
+
|state={self.state}
|
|
315
|
+
}}}}"""
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class Commit(object):
|
|
319
|
+
"""
|
|
320
|
+
a commit
|
|
321
|
+
"""
|
|
322
|
+
|
|
323
|
+
@staticmethod
|
|
324
|
+
def getSamples():
|
|
325
|
+
samples = [
|
|
326
|
+
{
|
|
327
|
+
"host": "https://github.com/WolfgangFahl/pyOpenSourceProjects",
|
|
328
|
+
"path": "",
|
|
329
|
+
"project": "pyOpenSourceProjects",
|
|
330
|
+
"subject": "Initial commit",
|
|
331
|
+
"name": "GitHub", # TicketSystem
|
|
332
|
+
"date": datetime.datetime.fromisoformat("2022-01-24 07:02:55+01:00"),
|
|
333
|
+
"hash": "106254f",
|
|
334
|
+
}
|
|
335
|
+
]
|
|
336
|
+
return samples
|
|
337
|
+
|
|
338
|
+
def toWikiMarkup(self):
|
|
339
|
+
"""
|
|
340
|
+
Returns Commit as wiki markup
|
|
341
|
+
"""
|
|
342
|
+
params = [
|
|
343
|
+
f"{attr}={getattr(self, attr, '')}" for attr in self.getSamples()[0].keys()
|
|
344
|
+
]
|
|
345
|
+
markup = f"{{{{commit|{'|'.join(params)}|storemode=subobject|viewmode=line}}}}"
|
|
346
|
+
return markup
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def gitlog2wiki(_argv=None):
|
|
350
|
+
"""
|
|
351
|
+
cmdline interface to get gitlog entries in wiki markup
|
|
352
|
+
"""
|
|
353
|
+
parser = argparse.ArgumentParser(description="gitlog2wiki")
|
|
354
|
+
if _argv:
|
|
355
|
+
_args = parser.parse_args(args=_argv)
|
|
356
|
+
|
|
357
|
+
osProject = OsProject.fromRepo()
|
|
358
|
+
commits = osProject.getCommits()
|
|
359
|
+
print("\n".join([c.toWikiMarkup() for c in commits]))
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def main(_argv=None):
|
|
363
|
+
"""
|
|
364
|
+
main command line entry point
|
|
365
|
+
"""
|
|
366
|
+
parser = argparse.ArgumentParser(description="Issue2ticket")
|
|
367
|
+
parser.add_argument("-o", "--owner", help="project owner or organization")
|
|
368
|
+
parser.add_argument("-p", "--project", help="name of the project")
|
|
369
|
+
parser.add_argument(
|
|
370
|
+
"--repo",
|
|
371
|
+
action="store_true",
|
|
372
|
+
help="get needed information form repository of current location",
|
|
373
|
+
)
|
|
374
|
+
parser.add_argument(
|
|
375
|
+
"-ts",
|
|
376
|
+
"--ticketsystem",
|
|
377
|
+
default="github",
|
|
378
|
+
choices=["github", "jira"],
|
|
379
|
+
help="platform the project is hosted",
|
|
380
|
+
)
|
|
381
|
+
parser.add_argument(
|
|
382
|
+
"-s",
|
|
383
|
+
"--state",
|
|
384
|
+
choices=["open", "closed", "all"],
|
|
385
|
+
default="all",
|
|
386
|
+
help="only issues with the given state",
|
|
387
|
+
)
|
|
388
|
+
parser.add_argument("-V", "--version", action="version", version="gitlog2wiki 0.1")
|
|
389
|
+
|
|
390
|
+
args = parser.parse_args(args=_argv)
|
|
391
|
+
# resolve ticketsystem
|
|
392
|
+
ticketSystem = GitHub
|
|
393
|
+
if args.ticketsystem == "jira":
|
|
394
|
+
ticketSystem = Jira
|
|
395
|
+
if args.project and args.owner:
|
|
396
|
+
osProject = OsProject(
|
|
397
|
+
owner=args.owner, id=args.project, ticketSystem=ticketSystem
|
|
398
|
+
)
|
|
399
|
+
else:
|
|
400
|
+
osProject = OsProject.fromRepo()
|
|
401
|
+
tickets = osProject.getIssues(state=args.state)
|
|
402
|
+
print("\n".join([t.toWikiMarkup() for t in tickets]))
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
if __name__ == "__main__":
|
|
406
|
+
# sys.exit(main())
|
|
407
|
+
sys.exit(gitlog2wiki())
|