• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dzhovi / repositories-reasoner / 18337968304

08 Oct 2025 08:02AM UTC coverage: 34.906% (-0.3%) from 35.238%
18337968304

Pull #21

github

web-flow
Merge ffa4cb304 into 7064cceb3
Pull Request #21: Update dependency python to 3.14

37 of 106 relevant lines covered (34.91%)

0.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/source/cli.py
1
# The MIT License (MIT)
2
#
3
# Copyright (c) 2024 Vakhidov Dzhovidon
4
#
5
# Permission is hereby granted, free of charge, to any person obtaining a copy
6
# of this software and associated documentation files (the "Software"), to deal
7
# in the Software without restriction, including without limitation the rights
8
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
# copies of the Software, and to permit persons to whom the Software is
10
# furnished to do so, subject to the following conditions:
11
#
12
# The above copyright notice and this permission notice shall be included
13
# in all copies or substantial portions of the Software.
14
#
15
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
18
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
# SOFTWARE.
22

23
"""
×
24
Cli runner.
25
"""
26
import csv
×
27
from typing import Optional
×
28
import typer
×
29
from typer.cli import app
×
30
from source.github_repository import GitHubRepository
×
31
from langchain_core.messages import HumanMessage, SystemMessage
×
32
from langchain_gigachat.chat_models import GigaChat
×
33
from source import NAME
×
34
import os
×
35

36

37
@app.command()
×
38
def filter_unmaintained(
×
39
    repositories: str = typer.Option(
40
        ..., "--repositories", help="Path to the input repositories CSV file."
41
    ),
42
    output: str = typer.Option(
43
        ..., "--output", help="Path to the output CSV file."
44
    ),
45
    api_key: str = typer.Option(
46
        ..., "--key", help="Your API key to access LLM."
47
    ),
48
    model: str = typer.Option(
49
        "GigaChat", "--model", help="Name of Gigachat Model"
50
    ),
51
):
52
    """
53
    Filter repositories to identify maintained ones.
54
    """
55
    try:
×
56
        maintained_repos = []
×
57

58
        # Read the input CSV file
59
        with open(repositories, mode="r", encoding="utf-8") as csvfile:
×
60
            reader = csv.DictReader(csvfile)
×
61

62
            for row in reader:
×
63
                repository = row["full_name"]
×
64

65
                # Call is_maintained for each repository
66
                maintained = is_maintained(repository, model, api_key).lower()
×
67
                if maintained == "yes":
×
68
                    maintained_repos.append(row)
×
69

70
        # Write the maintained repositories to the output CSV file
71
        with open(output, mode="w", encoding="utf-8", newline="") as csvfile:
×
72
            writer = csv.DictWriter(csvfile, fieldnames=reader.fieldnames)
×
73
            writer.writeheader()
×
74
            writer.writerows(maintained_repos)
×
75

76
        typer.echo(f"Filtered repositories written to {output}")
×
77

78
    except Exception as e:
×
79
        typer.echo(f"An unexpected error occurred: {e}")
×
80

81

82
@app.command()
×
83
def is_maintained(
×
84
    repository: str = typer.Option(
85
        ..., "--repository", help="GitHub repository name (e.g., 'owner/repo')"
86
    ),
87
    model: str = typer.Option(
88
        "GigaChat", "--model", help="Name of Gigachat Model"
89
    ),
90
    api_key: str = typer.Option(
91
        ..., "--key", help="your api key to access llm"
92
    ),
93
    out: str = typer.Option(..., "--out", help="output csv file"),
94
):
95
    """
96
    Decides whether the repository is maintained or not and
97
    provides a reason, then writes the result to a CSV.
98
    """
99
    try:
×
100
        # Initialize the GitHubRepository class
101
        github_repo = GitHubRepository(repository)
×
102

103
        # Fetch repository data
104
        github_repo.fetch_repository_data()
×
105

106
        # Extract metrics
107
        metrics = github_repo.get_key_metrics()
×
108

109
        # Log details for debugging
110
        typer.echo(
×
111
            f"Metrics: Stars={metrics.stars}, Forks={metrics.forks}, "
112
            f"Last Push={metrics.last_push}, "
113
            f"Open Issues={metrics.open_issues}, "
114
            f"Archived={metrics.archived}"
115
        )
116

117
        # GigaChat initialization
118
        llm = GigaChat(
×
119
            credentials=api_key,
120
            scope="GIGACHAT_API_PERS",
121
            model=str(model),
122
            verify_ssl_certs=False,
123
            streaming=False,
124
        )
125

126
        # System message for GigaChat
127
        system_message = SystemMessage(
×
128
            content=(
129
                "You are an AI assistant that analyzes GitHub repositories"
130
                " to determine if they are maintained."
131
                " You will use the following metrics:"
132
                " stars, forks, last push date, open issues,"
133
                " and archived status."
134
                "Your answer must be 2 lines:"
135
                'In first line respond with "yes" or "no"'
136
                "In the second line provide a very brief reason "
137
                "for your decision. "
138
                "Keep the justification under 50 characters."
139
            )
140
        )
141

142
        # Human message with repository metrics
143
        user_message = HumanMessage(
×
144
            content=(
145
                f"Here are the repository metrics:\n"
146
                f"- Stars: {metrics.stars}\n"
147
                f"- Forks: {metrics.forks}\n"
148
                f"- Last Push: {metrics.last_push}\n"
149
                f"- Open Issues: {metrics.open_issues}\n"
150
                f"- Archived: {metrics.archived}\n\n"
151
                f"Is the repository maintained? Please provide a reason."
152
            )
153
        )
154

155
        # GigaChat invocation
156
        messages = [system_message, user_message]
×
157
        response = llm.invoke(messages)
×
158

159
        # Parse response content: Expected format: "yes" or "no" and a reason
160
        result = response.content.strip().split("\n", 1)
×
161
        status = result[0].strip()
×
162
        reason = result[1].strip() if len(result) > 1 else "No reason provided"
×
163

164
        # Print the result in a fixed format
165
        typer.echo(f"Repository: {repository}")
×
166
        typer.echo(f"Maintained: {status}")
×
167
        typer.echo(f"Reason: {reason}")
×
168

169
        # Check if the output file exists
170
        file_exists = os.path.isfile(out)
×
171

172
        # Open the CSV file in append mode
173
        with open(out, mode="a", encoding="utf-8", newline="") as csvfile:
×
174
            fieldnames = ["repository", "maintained", "reason"]
×
175

176
            if not file_exists or os.stat(out).st_size == 0:
×
177
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
×
178
                writer.writeheader()
×
179

180
            # Write the results to the file
181
            writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
×
182
            writer.writerow(
×
183
                {
184
                    "repository": repository,
185
                    "maintained": status,
186
                    "reason": reason,
187
                }
188
            )
189

190
        return status, reason
×
191

192
    except ValueError as ve:
×
193
        typer.echo(str(ve))
×
194
    except Exception as e:
×
195
        typer.echo(f"An unexpected error occurred: {e}")
×
196

197

198
# Run it.
199
@app.callback()
×
200
def main(
×
201
    # pylint: disable=unused-argument
202
    version: Optional[bool] = typer.Option(
203
        None,
204
        "--version",
205
        help="Show the application's version and exit.",
206
        is_eager=True,
207
    )
208
) -> None:
209
    f"""
×
210
    {NAME}
211
    """
212
    return
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc