Spaces:
Sleeping
Sleeping
espejelomar
commited on
Commit
•
0472866
1
Parent(s):
20b860f
Upload folder using huggingface_hub
Browse files- data/figures/developer_engagement_journey_2024-03-04.png +0 -0
- data/figures/developer_survival_curve_2024-03-04.png +0 -0
- data/source/all_networks_developer_classification.csv +0 -0
- debug.csv +0 -0
- github_metrics/__pycache__/utils.cpython-311.pyc +0 -0
- github_metrics/developer_survival_plot.py +174 -0
- github_metrics/main.py +343 -85
- github_metrics/utils.py +24 -0
- poetry.lock +283 -1
- pyproject.toml +6 -0
data/figures/developer_engagement_journey_2024-03-04.png
ADDED
data/figures/developer_survival_curve_2024-03-04.png
ADDED
data/source/all_networks_developer_classification.csv
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
debug.csv
ADDED
The diff for this file is too large to render.
See raw diff
|
|
github_metrics/__pycache__/utils.cpython-311.pyc
ADDED
Binary file (1.7 kB). View file
|
|
github_metrics/developer_survival_plot.py
ADDED
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import matplotlib.pyplot as plt
|
2 |
+
import numpy as np
|
3 |
+
import pandas as pd
|
4 |
+
import seaborn as sns
|
5 |
+
from lifelines import KaplanMeierFitter
|
6 |
+
from matplotlib.colors import LinearSegmentedColormap
|
7 |
+
|
8 |
+
from utils import save_plot
|
9 |
+
|
10 |
+
|
11 |
+
def load_and_prepare_data(file_path):
|
12 |
+
"""
|
13 |
+
Load CSV data, convert 'month_year' to datetime, and prepare cohort and duration calculations.
|
14 |
+
Filter data to include only entries from 2021 onwards and adjust the cohort calculation based on the first active month.
|
15 |
+
Additionally, eliminate all months with a negative 'Order' so we only get the months after the cohort of the individual.
|
16 |
+
"""
|
17 |
+
df = pd.read_csv(file_path)
|
18 |
+
df["month_year"] = pd.to_datetime(df["month_year"], format="%B_%Y")
|
19 |
+
df = df[df["month_year"] >= "2021-09-01"]
|
20 |
+
df["Active"] = df["total_commits"] > 0
|
21 |
+
df.sort_values(by=["developer", "month_year"], inplace=True)
|
22 |
+
|
23 |
+
first_active_month = (
|
24 |
+
df[df["Active"]].groupby("developer")["month_year"].min().reset_index()
|
25 |
+
)
|
26 |
+
first_active_month.rename(columns={"month_year": "FirstActiveMonth"}, inplace=True)
|
27 |
+
|
28 |
+
df = df.merge(first_active_month, on="developer", how="left")
|
29 |
+
|
30 |
+
df["Cohort"] = df["FirstActiveMonth"].dt.to_period("M")
|
31 |
+
|
32 |
+
def calculate_order(row):
|
33 |
+
if pd.isnull(row["Cohort"]):
|
34 |
+
return None
|
35 |
+
return (row["month_year"].to_period("M") - row["Cohort"]).n
|
36 |
+
|
37 |
+
df["Order"] = df.apply(calculate_order, axis=1)
|
38 |
+
|
39 |
+
df = df[df["Order"] >= 0]
|
40 |
+
df["Inactive_Month"] = df.groupby("developer")["Active"].transform(
|
41 |
+
lambda x: x.rolling(window=2, min_periods=2).sum() == 0
|
42 |
+
)
|
43 |
+
df["inactive_for_two_months"] = (
|
44 |
+
df.groupby("developer")["Inactive_Month"].transform("max").astype(int)
|
45 |
+
)
|
46 |
+
|
47 |
+
df["duration"] = df.groupby("developer")["month_year"].transform("nunique")
|
48 |
+
df.to_csv("debug.csv", index=False)
|
49 |
+
|
50 |
+
return df
|
51 |
+
|
52 |
+
|
53 |
+
def visualize_developer_retention(df):
|
54 |
+
cohort_counts = (
|
55 |
+
df[~df["Inactive_Month"]]
|
56 |
+
.groupby(["Cohort", "Order"])
|
57 |
+
.developer.nunique()
|
58 |
+
.unstack(0)
|
59 |
+
)
|
60 |
+
|
61 |
+
cohort_sizes = cohort_counts.iloc[0]
|
62 |
+
retention = cohort_counts.divide(cohort_sizes, axis=1)
|
63 |
+
|
64 |
+
colors = [(0, "#FF0000"), (0.15, "#FFA500"), (0.2, "#FFFF00"), (1, "#008000")]
|
65 |
+
cmap = LinearSegmentedColormap.from_list("custom_cmap", colors, N=256)
|
66 |
+
plt.figure(figsize=(12, 8)) # Adjusted figure size for better visibility
|
67 |
+
sns.heatmap(retention.T, annot=False, cmap=cmap)
|
68 |
+
plt.title("Journey Through Code: Tracking Developer Engagement Over Time", pad=20)
|
69 |
+
|
70 |
+
plt.subplots_adjust(bottom=0.3)
|
71 |
+
|
72 |
+
description_text = (
|
73 |
+
"This heatmap visualizes the engagement journey of developers, tracked monthly across cohorts."
|
74 |
+
" Each cohort represents developers who began contributing in the same month."
|
75 |
+
" The color gradient from red to green signifies the evolution of active engagement over time,"
|
76 |
+
" with red indicating lower engagement levels and green denoting higher activity."
|
77 |
+
" Cohorts are plotted on the y-axis, and the actual months since the start of the cohort on the x-axis."
|
78 |
+
" This visualization offers insights into how developer activity trends evolve,"
|
79 |
+
" highlighting periods of increased or decreased engagement and aiding in understanding"
|
80 |
+
" the effectiveness of retention strategies over time."
|
81 |
+
" Parameters:"
|
82 |
+
"(a) A developer is considered inactive if they have at least 2 continuous inactive months."
|
83 |
+
"(b) With one commit in a month, the developer is considered active."
|
84 |
+
"(c) The data is filtered to include only entries from September 2021 onwards."
|
85 |
+
)
|
86 |
+
plt.figtext(0.5, -0.0001, description_text, ha="center", fontsize=9, wrap=True)
|
87 |
+
|
88 |
+
save_plot(plt, "developer_engagement_journey")
|
89 |
+
|
90 |
+
|
91 |
+
def survival_curve_analysis_and_plot(df):
|
92 |
+
"""
|
93 |
+
Perform analysis on the DataFrame to calculate durations and generate visualizations, with annotations explaining the analysis.
|
94 |
+
Adjust the event definition and perform Log-Rank Test.
|
95 |
+
"""
|
96 |
+
summary_df = (
|
97 |
+
df.groupby("developer")
|
98 |
+
.agg({"duration": "first", "inactive_for_two_months": "last"})
|
99 |
+
.reset_index()
|
100 |
+
)
|
101 |
+
|
102 |
+
kmf = KaplanMeierFitter()
|
103 |
+
kmf.fit(
|
104 |
+
durations=summary_df["duration"],
|
105 |
+
event_observed=summary_df["inactive_for_two_months"],
|
106 |
+
label="Developer Survival Probability",
|
107 |
+
)
|
108 |
+
|
109 |
+
plt.figure(figsize=(10, 6))
|
110 |
+
ax = plt.subplot(111)
|
111 |
+
kmf.plot_survival_function(ax=ax)
|
112 |
+
|
113 |
+
plt.title("Developer Survival Curve: Probability of Active Contribution Over Time")
|
114 |
+
plt.grid(True, which="both", linestyle="--", linewidth=0.5)
|
115 |
+
median_survival_time = kmf.median_survival_time_
|
116 |
+
ax.axhline(y=0.5, color="red", linestyle="--")
|
117 |
+
ax.text(
|
118 |
+
median_survival_time,
|
119 |
+
0.48,
|
120 |
+
"Median Survival Time",
|
121 |
+
verticalalignment="center",
|
122 |
+
color="red",
|
123 |
+
fontsize=8,
|
124 |
+
)
|
125 |
+
ax.axvline(x=3, color="green", linestyle="--")
|
126 |
+
ax.text(
|
127 |
+
3,
|
128 |
+
0.95,
|
129 |
+
"Inactive Month + 1",
|
130 |
+
verticalalignment="top",
|
131 |
+
horizontalalignment="center",
|
132 |
+
color="green",
|
133 |
+
fontsize=8,
|
134 |
+
)
|
135 |
+
ax.axvline(x=median_survival_time, color="green", linestyle="--")
|
136 |
+
ax.text(
|
137 |
+
len(df["duration"].unique()),
|
138 |
+
0.9,
|
139 |
+
f"After month {int(median_survival_time)} the probability of developers staying is lower than 50 percent",
|
140 |
+
verticalalignment="top",
|
141 |
+
horizontalalignment="right",
|
142 |
+
color="green",
|
143 |
+
fontsize=8,
|
144 |
+
)
|
145 |
+
ax.set_yticks(np.arange(0, 1.1, 0.1))
|
146 |
+
|
147 |
+
# Setting the x-axis and y-axis labels as per the request
|
148 |
+
plt.xlabel("Months since the developer started committing code")
|
149 |
+
plt.ylabel("Probability of a developer staying in the ecosystem")
|
150 |
+
|
151 |
+
description_text = (
|
152 |
+
"The Kaplan-Meier survival curve shows the probability of developers continuing to contribute over time."
|
153 |
+
"Parameters:"
|
154 |
+
"(a) A developer is consider as inactive if they have at least 2 continuous inactive months."
|
155 |
+
"(b) With one commit in a month, the developer is considered active."
|
156 |
+
"(c) The data is filtered to include only entries from September 2021 onwards."
|
157 |
+
"The Kaplan-Meier estimator is a non-parametric statistic used to estimate the survival function from lifetime data."
|
158 |
+
"It requires to know the duration each subject was observed for, and whether the event of interest"
|
159 |
+
"(in this case, becoming inactive for two months) was observed."
|
160 |
+
"The 'Median Survival Time' shows when the chance of further contributions drops below 50%. "
|
161 |
+
"This analysis helps in understanding the retention of developers and predicting future contribution patterns."
|
162 |
+
)
|
163 |
+
plt.figtext(0.1, -0.1, description_text, ha="left", fontsize=8, wrap=True)
|
164 |
+
|
165 |
+
save_plot(plt, "developer_survival_curve")
|
166 |
+
|
167 |
+
|
168 |
+
if __name__ == "__main__":
|
169 |
+
csv_path = "data/source/all_networks_developer_classification.csv"
|
170 |
+
df = load_and_prepare_data(csv_path)
|
171 |
+
|
172 |
+
visualize_developer_retention(df)
|
173 |
+
|
174 |
+
survival_curve_analysis_and_plot(df)
|
github_metrics/main.py
CHANGED
@@ -1,112 +1,370 @@
|
|
1 |
import gradio as gr
|
2 |
import pandas as pd
|
3 |
-
import
|
4 |
-
|
5 |
from termcolor import colored
|
|
|
|
|
6 |
|
7 |
-
|
8 |
-
def load_dataset():
|
9 |
-
try:
|
10 |
-
print(colored("Loading dataset...", "blue"))
|
11 |
-
df = pd.read_csv("data/source/all_networks_developer_classification.csv")
|
12 |
-
# Ensure the month_year column is in the correct datetime format
|
13 |
-
df['month_year'] = pd.to_datetime(df['month_year'], format='%B_%Y') # Adjust format if necessary
|
14 |
-
return df
|
15 |
-
except Exception as e:
|
16 |
-
print(colored(f"Error loading dataset: {e}", "red"))
|
17 |
-
raise
|
18 |
-
|
19 |
-
# Process input and generate plot and classification with debug prints
|
20 |
-
def process_input(input_text, uploaded_file):
|
21 |
try:
|
22 |
print(colored("Processing input...", "blue"))
|
23 |
-
|
24 |
-
# Check if a file was uploaded
|
25 |
if uploaded_file is not None:
|
26 |
print(colored("Reading from uploaded file...", "blue"))
|
27 |
-
# Decode the bytes object to string
|
28 |
file_content = uploaded_file.decode("utf-8")
|
29 |
-
|
30 |
-
github_handles = [handle.strip() for handle in file_content.split('\n') if handle.strip()]
|
31 |
else:
|
32 |
github_handles = [handle.strip() for handle in input_text.split(",")]
|
33 |
-
|
34 |
print(colored(f"GitHub handles: {github_handles}", "blue"))
|
35 |
|
36 |
-
|
37 |
-
df = load_dataset()
|
38 |
-
|
39 |
-
# Filter dataset for the provided GitHub handles
|
40 |
print(colored("Filtering dataset...", "blue"))
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
|
|
58 |
print(colored("Classifying developers...", "blue"))
|
59 |
-
|
60 |
-
for handle in github_handles:
|
61 |
-
dev_df = filtered_df[filtered_df['developer'] == handle]
|
62 |
-
last_3_months = pd.Timestamp.now() - pd.DateOffset(months=3)
|
63 |
-
recent_activity = dev_df[dev_df['month_year'] >= last_3_months]
|
64 |
-
total_recent_commits = recent_activity['total_commits'].sum()
|
65 |
-
|
66 |
-
if dev_df.empty:
|
67 |
-
status = "Always been inactive"
|
68 |
-
elif recent_activity.empty:
|
69 |
-
status = "Previously active but no longer"
|
70 |
-
elif total_recent_commits < 20:
|
71 |
-
status = "Low-level active"
|
72 |
-
else:
|
73 |
-
status = "Highly involved"
|
74 |
-
|
75 |
-
classification.append((handle, status))
|
76 |
-
|
77 |
-
classification_df = pd.DataFrame(classification, columns=["Developer", "Classification"]).sort_values("Classification", ascending=False)
|
78 |
print(colored("Classification completed.", "blue"))
|
79 |
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
82 |
except Exception as e:
|
83 |
print(colored(f"Error processing input: {e}", "red"))
|
84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
|
86 |
-
# Gradio interface with descriptions and debug prints
|
87 |
with gr.Blocks() as app:
|
88 |
-
gr.Markdown("
|
89 |
-
gr.Markdown(
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
with gr.Row():
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
|
105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
|
109 |
if __name__ == "__main__":
|
110 |
print(colored("Launching app...", "blue"))
|
111 |
-
app.launch(share=True)
|
112 |
-
|
|
|
1 |
import gradio as gr
|
2 |
import pandas as pd
|
3 |
+
import plotly.express as px
|
4 |
+
import plotly.graph_objects as go
|
5 |
from termcolor import colored
|
6 |
+
from scipy.stats import mannwhitneyu
|
7 |
+
from utils import load_all_developers_dataset
|
8 |
|
9 |
+
def process_input(input_text, uploaded_file, program_end_date=None, event_name=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
try:
|
11 |
print(colored("Processing input...", "blue"))
|
|
|
|
|
12 |
if uploaded_file is not None:
|
13 |
print(colored("Reading from uploaded file...", "blue"))
|
|
|
14 |
file_content = uploaded_file.decode("utf-8")
|
15 |
+
github_handles = [handle.strip() for handle in file_content.split("\n") if handle.strip()]
|
|
|
16 |
else:
|
17 |
github_handles = [handle.strip() for handle in input_text.split(",")]
|
|
|
18 |
print(colored(f"GitHub handles: {github_handles}", "blue"))
|
19 |
|
20 |
+
df = load_all_developers_dataset()
|
|
|
|
|
|
|
21 |
print(colored("Filtering dataset...", "blue"))
|
22 |
+
one_year_ago = pd.Timestamp.now() - pd.DateOffset(years=1)
|
23 |
+
filtered_df = df[(df["developer"].isin(github_handles)) & (df["month_year"] >= one_year_ago)]
|
24 |
+
filtered_df = filtered_df.sort_values(by=["developer", "month_year"])
|
25 |
+
filtered_df.loc[:, "month_year"] = pd.to_datetime(filtered_df["month_year"])
|
26 |
+
|
27 |
+
line_fig = create_line_plot(filtered_df, github_handles, program_end_date)
|
28 |
+
analysis_result = perform_statistical_analysis(filtered_df, github_handles, program_end_date)
|
29 |
+
new_developers_count = count_new_developers(filtered_df, github_handles, program_end_date)
|
30 |
+
|
31 |
+
last_3_months = pd.Timestamp.now() - pd.DateOffset(months=3)
|
32 |
+
recent_activity_user = filtered_df[filtered_df["month_year"] >= last_3_months]
|
33 |
+
all_devs_df = load_all_developers_dataset()
|
34 |
+
all_devs_filtered_df = all_devs_df[(all_devs_df["month_year"] >= last_3_months)]
|
35 |
+
other_devs_recent_activity = all_devs_filtered_df[~all_devs_filtered_df["developer"].isin(github_handles)]
|
36 |
+
|
37 |
+
user_specified_active = recent_activity_user[recent_activity_user["total_commits"] > 0]
|
38 |
+
other_developers_active = other_devs_recent_activity[other_devs_recent_activity["total_commits"] > 0]
|
39 |
+
box_fig = create_box_plot(user_specified_active, other_developers_active)
|
40 |
+
|
41 |
print(colored("Classifying developers...", "blue"))
|
42 |
+
classification_df = classify_developers(github_handles, recent_activity_user)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
print(colored("Classification completed.", "blue"))
|
44 |
|
45 |
+
comparison_result = compare_user_developers_to_others(user_specified_active, other_developers_active, df, program_end_date)
|
46 |
+
growth_rate_result = compare_growth_rate(user_specified_active, other_developers_active, df)
|
47 |
+
|
48 |
+
tldr_summary = generate_tldr_summary(github_handles, classification_df, analysis_result, new_developers_count, comparison_result, growth_rate_result, event_name)
|
49 |
+
|
50 |
+
return line_fig, box_fig, classification_df, analysis_result, new_developers_count, comparison_result, growth_rate_result, tldr_summary
|
51 |
except Exception as e:
|
52 |
print(colored(f"Error processing input: {e}", "red"))
|
53 |
+
return None, None, None, None, "Error in processing input.", None, None, "Error in processing input."
|
54 |
+
|
55 |
+
def create_line_plot(filtered_df, github_handles, program_end_date):
|
56 |
+
all_developers = pd.DataFrame({"developer": github_handles, "month_year": pd.Timestamp.now(), "total_commits": 0})
|
57 |
+
plot_df = pd.concat([filtered_df, all_developers])
|
58 |
+
plot_df = plot_df.groupby(["developer", "month_year"])["total_commits"].sum().reset_index()
|
59 |
+
line_fig = px.line(
|
60 |
+
plot_df,
|
61 |
+
x="month_year",
|
62 |
+
y="total_commits",
|
63 |
+
color="developer",
|
64 |
+
labels={"month_year": "Month", "total_commits": "Number of Commits"},
|
65 |
+
title="Commits per Month",
|
66 |
+
)
|
67 |
+
if program_end_date:
|
68 |
+
program_end_date = pd.to_datetime(program_end_date)
|
69 |
+
line_fig.add_vline(x=program_end_date, line_width=2, line_dash="dash", line_color="red")
|
70 |
+
return line_fig
|
71 |
+
|
72 |
+
def create_box_plot(user_specified_active, other_developers_active):
|
73 |
+
box_fig = go.Figure()
|
74 |
+
box_fig.add_trace(go.Box(y=user_specified_active["total_commits"], name="User Specified Developers"))
|
75 |
+
box_fig.add_trace(go.Box(y=other_developers_active["total_commits"], name="Other Developers"))
|
76 |
+
box_fig.update_layout(
|
77 |
+
title="Comparison of Monthly Commits in the Last 3 Months: User Specified vs. Other Developers (Active Only)",
|
78 |
+
yaxis_title="Total Monthly Commits",
|
79 |
+
yaxis=dict(range=[0, 50]),
|
80 |
+
)
|
81 |
+
return box_fig
|
82 |
+
|
83 |
+
def classify_developers(github_handles, recent_activity_user):
|
84 |
+
classification = []
|
85 |
+
for handle in github_handles:
|
86 |
+
dev_df = recent_activity_user[recent_activity_user["developer"] == handle]
|
87 |
+
total_recent_commits = dev_df["total_commits"].sum()
|
88 |
+
if dev_df.empty or total_recent_commits == 0:
|
89 |
+
status = "Always been inactive"
|
90 |
+
elif total_recent_commits < 20:
|
91 |
+
status = "Low-level active"
|
92 |
+
else:
|
93 |
+
status = "Highly involved"
|
94 |
+
classification.append((handle, status, total_recent_commits))
|
95 |
+
|
96 |
+
sort_keys = {
|
97 |
+
"Highly involved": 1,
|
98 |
+
"Low-level active": 2,
|
99 |
+
"Previously active but no longer": 3,
|
100 |
+
"Always been inactive": 4,
|
101 |
+
}
|
102 |
+
classification_df = pd.DataFrame(classification, columns=["Developer", "Classification", "Total Recent Commits"])
|
103 |
+
classification_df["Sort Key"] = classification_df["Classification"].map(sort_keys)
|
104 |
+
classification_df.sort_values(by=["Sort Key", "Total Recent Commits"], ascending=[True, False], inplace=True)
|
105 |
+
classification_df.drop(["Sort Key", "Total Recent Commits"], axis=1, inplace=True)
|
106 |
+
return classification_df
|
107 |
+
|
108 |
+
def perform_statistical_analysis(filtered_df, github_handles, program_end_date_str):
|
109 |
+
if program_end_date_str is None:
|
110 |
+
return "Program end date not provided. Unable to perform statistical analysis."
|
111 |
+
|
112 |
+
program_end_date = pd.to_datetime(program_end_date_str)
|
113 |
+
before_program = filtered_df[filtered_df["month_year"] < program_end_date]
|
114 |
+
after_program = filtered_df[filtered_df["month_year"] >= program_end_date]
|
115 |
+
|
116 |
+
before_counts = before_program.groupby("developer")["total_commits"].median()
|
117 |
+
after_counts = after_program.groupby("developer")["total_commits"].median()
|
118 |
+
|
119 |
+
all_developers = pd.Series(0, index=github_handles)
|
120 |
+
before_counts = before_counts.reindex(all_developers.index, fill_value=0)
|
121 |
+
after_counts = after_counts.reindex(all_developers.index, fill_value=0)
|
122 |
+
|
123 |
+
if len(before_counts) < 2 or len(after_counts) < 2:
|
124 |
+
return "Not enough data for statistical analysis."
|
125 |
+
|
126 |
+
stat, p_value = mannwhitneyu(after_counts, before_counts)
|
127 |
+
analysis_result = f"Mann-Whitney U test statistic: {stat:.3f}, P-value: {p_value:.3f}\n"
|
128 |
+
|
129 |
+
if p_value < 0.2:
|
130 |
+
if stat > 0:
|
131 |
+
analysis_result += "Difference in commit activity before and after the program is considered significant. " \
|
132 |
+
"The commit activity is higher after the program."
|
133 |
+
else:
|
134 |
+
analysis_result += "Difference in commit activity before and after the program is considered significant. " \
|
135 |
+
"The commit activity is lower after the program."
|
136 |
+
else:
|
137 |
+
analysis_result += "No significant difference in commit activity before and after the program."
|
138 |
+
|
139 |
+
return analysis_result
|
140 |
+
|
141 |
+
def count_new_developers(filtered_df, github_handles, program_end_date_str):
|
142 |
+
if program_end_date_str is None:
|
143 |
+
return "Program end date not provided. Unable to count new developers."
|
144 |
+
|
145 |
+
program_end_date = pd.to_datetime(program_end_date_str)
|
146 |
+
two_months_after_program = program_end_date + pd.DateOffset(months=2)
|
147 |
+
|
148 |
+
before_program = filtered_df[filtered_df["month_year"] < program_end_date]
|
149 |
+
after_program = filtered_df[(filtered_df["month_year"] >= program_end_date) & (filtered_df["month_year"] <= two_months_after_program)]
|
150 |
+
|
151 |
+
before_developers = before_program["developer"].unique()
|
152 |
+
after_developers = after_program["developer"].unique()
|
153 |
+
|
154 |
+
new_developers = set(after_developers) - set(before_developers)
|
155 |
+
new_developers_str = ", ".join(new_developers)
|
156 |
+
|
157 |
+
return f"Number of new developers committing code within 2 months after the program: {len(new_developers)}\nNew developers: {new_developers_str}"
|
158 |
+
|
159 |
+
def compare_user_developers_to_others(user_specified_active, other_developers_active, df, program_end_date_str):
|
160 |
+
if program_end_date_str is None:
|
161 |
+
return "Program end date not provided. Unable to compare user-specified developers to others."
|
162 |
+
|
163 |
+
program_end_date = pd.to_datetime(program_end_date_str)
|
164 |
+
|
165 |
+
user_commits = df[(df["developer"].isin(user_specified_active["developer"])) & (df["month_year"] >= program_end_date)]["total_commits"]
|
166 |
+
other_commits = df[(df["developer"].isin(other_developers_active["developer"])) & (df["month_year"] >= program_end_date)]["total_commits"]
|
167 |
+
|
168 |
+
stat, p_value = mannwhitneyu(user_commits, other_commits)
|
169 |
+
comparison_result = f"Mann-Whitney U test statistic: {stat:.3f}, P-value: {p_value:.3f}\n"
|
170 |
+
|
171 |
+
if p_value < 0.25:
|
172 |
+
if stat > 0:
|
173 |
+
comparison_result += "The user-specified developers have a significantly higher number of commits compared to other developers since the program end date."
|
174 |
+
else:
|
175 |
+
comparison_result += "The user-specified developers have a significantly lower number of commits compared to other developers since the program end date."
|
176 |
+
else:
|
177 |
+
comparison_result += "There is no significant difference in the number of commits between user-specified developers and other developers since the program end date."
|
178 |
+
|
179 |
+
return comparison_result
|
180 |
+
|
181 |
+
def compare_growth_rate(user_specified_active, other_developers_active, df):
|
182 |
+
user_growth_rates = []
|
183 |
+
other_growth_rates = []
|
184 |
+
|
185 |
+
for developer in user_specified_active["developer"].unique():
|
186 |
+
user_df = df[df["developer"] == developer]
|
187 |
+
user_df = user_df.sort_values("month_year")
|
188 |
+
user_commits = user_df["total_commits"].tolist()
|
189 |
+
user_growth_rate = calculate_average_growth_rate(user_commits)
|
190 |
+
user_growth_rates.append(user_growth_rate)
|
191 |
+
|
192 |
+
for developer in other_developers_active["developer"].unique():
|
193 |
+
other_df = df[df["developer"] == developer]
|
194 |
+
other_df = other_df.sort_values("month_year")
|
195 |
+
other_commits = other_df["total_commits"].tolist()
|
196 |
+
other_growth_rate = calculate_average_growth_rate(other_commits)
|
197 |
+
other_growth_rates.append(other_growth_rate)
|
198 |
+
|
199 |
+
stat, p_value = mannwhitneyu(user_growth_rates, other_growth_rates)
|
200 |
+
comparison_result = f"Mann-Whitney U test statistic: {stat:.3f}, P-value: {p_value:.3f}\n"
|
201 |
+
|
202 |
+
if p_value < 0.25:
|
203 |
+
if stat > 0:
|
204 |
+
comparison_result += "The user-specified developers have a significantly higher average growth rate of commit activity compared to other developers."
|
205 |
+
else:
|
206 |
+
comparison_result += "The user-specified developers have a significantly lower average growth rate of commit activity compared to other developers."
|
207 |
+
else:
|
208 |
+
comparison_result += "There is no significant difference in the average growth rate of commit activity between user-specified developers and other developers."
|
209 |
+
|
210 |
+
return comparison_result
|
211 |
+
|
212 |
+
def calculate_average_growth_rate(commits):
|
213 |
+
growth_rates = []
|
214 |
+
for i in range(1, len(commits)):
|
215 |
+
if commits[i - 1] != 0:
|
216 |
+
growth_rate = (commits[i] - commits[i - 1]) / commits[i - 1]
|
217 |
+
growth_rates.append(growth_rate)
|
218 |
+
if len(growth_rates) > 0:
|
219 |
+
return sum(growth_rates) / len(growth_rates)
|
220 |
+
else:
|
221 |
+
return 0
|
222 |
+
|
223 |
+
def generate_tldr_summary(github_handles, classification_df, analysis_result, new_developers_count, comparison_result, growth_rate_result, event_name):
|
224 |
+
summary = f"### 📝 TLDR Summary for {', '.join(github_handles)}\n\n"
|
225 |
+
|
226 |
+
highly_involved_devs = classification_df[classification_df["Classification"] == "Highly involved"]["Developer"].tolist()
|
227 |
+
if highly_involved_devs:
|
228 |
+
summary += f"**🌟 High Performers:** {', '.join(highly_involved_devs)}\n\n"
|
229 |
+
|
230 |
+
if "higher after the program" in analysis_result:
|
231 |
+
summary += "**📈 Commit Activity:** Significantly higher after the program.\n\n"
|
232 |
+
elif "lower after the program" in analysis_result:
|
233 |
+
summary += "**📉 Commit Activity:** Significantly lower after the program.\n\n"
|
234 |
+
else:
|
235 |
+
summary += "**🔄 Commit Activity:** No significant change after the program.\n\n"
|
236 |
+
|
237 |
+
if new_developers_count.startswith("Number of new developers"):
|
238 |
+
summary += f"**🆕 New Developers:** {new_developers_count.split(':')[1].strip()}\n\n"
|
239 |
+
|
240 |
+
if "significantly higher number of commits" in comparison_result:
|
241 |
+
summary += "**🔍 Comparison with Other Developers:** User-specified developers have a significantly higher number of commits.\n\n"
|
242 |
+
elif "significantly lower number of commits" in comparison_result:
|
243 |
+
summary += "**🔍 Comparison with Other Developers:** User-specified developers have a significantly lower number of commits.\n\n"
|
244 |
+
else:
|
245 |
+
summary += "**🔍 Comparison with Other Developers:** No significant difference in the number of commits.\n\n"
|
246 |
+
|
247 |
+
if "significantly higher average growth rate" in growth_rate_result:
|
248 |
+
summary += "**📈 Growth Rate:** User-specified developers have a significantly higher average growth rate.\n\n"
|
249 |
+
elif "significantly lower average growth rate" in growth_rate_result:
|
250 |
+
summary += "**📉 Growth Rate:** User-specified developers have a significantly lower average growth rate.\n\n"
|
251 |
+
else:
|
252 |
+
summary += "**🔄 Growth Rate:** No significant difference in the average growth rate.\n\n"
|
253 |
+
|
254 |
+
if event_name:
|
255 |
+
summary += f"*Note: The analysis is based on the {event_name} event.*\n\n"
|
256 |
+
|
257 |
+
return summary
|
258 |
+
|
259 |
|
|
|
260 |
with gr.Blocks() as app:
|
261 |
+
gr.Markdown("# 🚀 GitHub Starknet Developer Insights")
|
262 |
+
gr.Markdown(
|
263 |
+
"""
|
264 |
+
This tool allows you to analyze the GitHub activity of developers within the Starknet ecosystem.
|
265 |
+
Enter GitHub handles separated by commas or upload a CSV file with GitHub handles in a single column
|
266 |
+
to see their monthly commit activity, involvement classification, and comparisons with other developers.
|
267 |
+
"""
|
268 |
+
)
|
269 |
+
with gr.Row():
|
270 |
+
with gr.Column():
|
271 |
+
text_input = gr.Textbox(
|
272 |
+
label="Enter GitHub handles separated by commas",
|
273 |
+
placeholder="e.g., user1,user2,user3",
|
274 |
+
)
|
275 |
+
file_input = gr.File(
|
276 |
+
label="Or upload a CSV file with GitHub handles in a single column",
|
277 |
+
type="binary",
|
278 |
+
)
|
279 |
+
gr.Markdown(
|
280 |
+
"""
|
281 |
+
*Note:* When uploading a CSV, ensure it contains a single column of GitHub handles without a header row.
|
282 |
+
"""
|
283 |
+
)
|
284 |
+
with gr.Row():
|
285 |
+
program_end_date_input = gr.Textbox(label="Program End Date (YYYY-MM-DD)", placeholder="e.g., 2023-06-30")
|
286 |
+
event_name_input = gr.Textbox(label="Event Name (optional)", placeholder="e.g., Basecamp, Hackathon")
|
287 |
+
gr.Markdown(
|
288 |
+
"""
|
289 |
+
💡 *Tip: Specifying a program end date allows you to analyze the impact of events like Basecamp or Hackathons on developer activity. Leave it blank to analyze overall activity.*
|
290 |
+
"""
|
291 |
+
)
|
292 |
+
btn = gr.Button("Analyze")
|
293 |
+
|
294 |
+
with gr.Column():
|
295 |
+
tldr_output = gr.Markdown(label="📝 TLDR Summary")
|
296 |
+
|
297 |
with gr.Row():
|
298 |
+
with gr.Column():
|
299 |
+
plot_output = gr.Plot(label="📈 Commits per Month")
|
300 |
+
with gr.Column():
|
301 |
+
box_plot_output = gr.Plot(label="📊 Box Plot Comparison (Last 3 Months)")
|
302 |
+
|
303 |
+
with gr.Accordion("📊 Statistical Analysis", open=False):
|
304 |
+
stat_analysis_output = gr.Textbox(label="Statistical Analysis Results")
|
305 |
+
gr.Markdown(
|
306 |
+
"""
|
307 |
+
The Mann-Whitney U test is used to compare the commit activity of developers before and after the program.
|
308 |
+
- The test statistic measures the difference in the distribution of commits between the two groups (before and after).
|
309 |
+
- The p-value indicates the probability of observing such a difference by chance, assuming there is no real difference between the groups.
|
310 |
+
- A p-value less than 0.2 suggests that the difference is considered significant.
|
311 |
+
- A positive test statistic indicates that the commit activity is higher after the program, while a negative value indicates lower activity.
|
312 |
+
"""
|
313 |
+
)
|
314 |
+
|
315 |
+
with gr.Accordion("🆕 New Developers", open=False):
|
316 |
+
new_developers_output = gr.Textbox(label="Number of New Developers")
|
317 |
+
|
318 |
+
with gr.Accordion("🏆 Developer Classification", open=False):
|
319 |
+
table_output = gr.Dataframe(label="Developer Classification")
|
320 |
+
gr.Markdown(
|
321 |
+
"""
|
322 |
+
### Developer Classification Criteria
|
323 |
+
- **Always been inactive**: No commits have been recorded in the dataset.
|
324 |
+
- **Previously active but no longer**: Had commits earlier but none in the last 3 months.
|
325 |
+
- **Low-level active**: Fewer than 20 commits in the last 3 months.
|
326 |
+
- **Highly involved**: 20 or more commits in the last 3 months.
|
327 |
+
"""
|
328 |
+
)
|
329 |
|
330 |
+
with gr.Accordion("🔍 Comparison with Other Developers", open=False):
|
331 |
+
comparison_output = gr.Textbox(label="Comparison with Other Developers")
|
332 |
+
gr.Markdown(
|
333 |
+
"""
|
334 |
+
The Mann-Whitney U test is used to compare the commit activity of the user-specified developers with the rest of the developers in the database since the program end date.
|
335 |
+
- The test statistic measures the difference in the distribution of commits between the two groups.
|
336 |
+
- The p-value indicates the probability of observing such a difference by chance, assuming there is no real difference between the groups.
|
337 |
+
- A p-value less than 0.25 suggests that the difference is considered significant.
|
338 |
+
- If the test statistic is positive, it means the user-specified developers have a higher number of commits compared to other developers, and vice versa.
|
339 |
+
"""
|
340 |
+
)
|
341 |
|
342 |
+
with gr.Accordion("📈 Growth Rate Comparison", open=False):
|
343 |
+
growth_rate_output = gr.Textbox(label="Growth Rate Comparison")
|
344 |
+
gr.Markdown(
|
345 |
+
"""
|
346 |
+
The average growth rate of commit activity is compared between the user-specified developers and other developers.
|
347 |
+
- The growth rate is calculated as the relative change in the number of commits from one month to the next.
|
348 |
+
- The Mann-Whitney U test is used to compare the average growth rates between the two groups.
|
349 |
+
- A p-value less than 0.25 suggests that the difference in average growth rates is statistically significant.
|
350 |
+
- If the test statistic is positive, it means the user-specified developers have a higher average growth rate compared to other developers, and vice versa.
|
351 |
+
"""
|
352 |
+
)
|
353 |
+
|
354 |
+
gr.Markdown(
|
355 |
+
"""
|
356 |
+
💡 *Disclaimer: This information is only for open-source repos and should be taken with a grain of salt. Commits in certain repos may be more important than others, and there are many private repos from several teams that are not included in this analysis.*
|
357 |
+
"""
|
358 |
+
)
|
359 |
+
|
360 |
+
btn.click(
|
361 |
+
process_input,
|
362 |
+
inputs=[text_input, file_input, program_end_date_input, event_name_input],
|
363 |
+
outputs=[plot_output, box_plot_output, table_output, stat_analysis_output, new_developers_output, comparison_output, growth_rate_output, tldr_output],
|
364 |
+
)
|
365 |
+
|
366 |
+
print(colored("Gradio app initialized.", "blue"))
|
367 |
|
368 |
if __name__ == "__main__":
|
369 |
print(colored("Launching app...", "blue"))
|
370 |
+
app.launch(share=True)
|
|
github_metrics/utils.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from datetime import datetime
|
2 |
+
from termcolor import colored
|
3 |
+
import pandas as pd
|
4 |
+
|
5 |
+
|
6 |
+
def load_all_developers_dataset():
|
7 |
+
try:
|
8 |
+
print(colored("Loading dataset...", "blue"))
|
9 |
+
df = pd.read_csv("data/source/all_networks_developer_classification.csv")
|
10 |
+
df["month_year"] = pd.to_datetime(df["month_year"], format="%B_%Y")
|
11 |
+
return df
|
12 |
+
except Exception as e:
|
13 |
+
print(colored(f"Error loading dataset: {e}", "red"))
|
14 |
+
raise
|
15 |
+
|
16 |
+
|
17 |
+
def save_plot(plt, base_filename):
|
18 |
+
"""
|
19 |
+
Save a matplotlib plot to a file with a timestamped filename.
|
20 |
+
"""
|
21 |
+
current_date = datetime.now().strftime("%Y-%m-%d")
|
22 |
+
filename = f"data/figures/{base_filename}_{current_date}.png"
|
23 |
+
plt.savefig(filename, dpi=300, bbox_inches="tight")
|
24 |
+
plt.close()
|
poetry.lock
CHANGED
@@ -84,6 +84,35 @@ tests = ["attrs[tests-no-zope]", "zope-interface"]
|
|
84 |
tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
|
85 |
tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
|
86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
[[package]]
|
88 |
name = "certifi"
|
89 |
version = "2024.2.2"
|
@@ -407,6 +436,29 @@ ufo = ["fs (>=2.2.0,<3)"]
|
|
407 |
unicode = ["unicodedata2 (>=15.1.0)"]
|
408 |
woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
|
409 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
410 |
[[package]]
|
411 |
name = "fsspec"
|
412 |
version = "2024.2.0"
|
@@ -442,6 +494,17 @@ smb = ["smbprotocol"]
|
|
442 |
ssh = ["paramiko"]
|
443 |
tqdm = ["tqdm"]
|
444 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
445 |
[[package]]
|
446 |
name = "gradio"
|
447 |
version = "4.19.2"
|
@@ -484,6 +547,23 @@ uvicorn = ">=0.14.0"
|
|
484 |
[package.extras]
|
485 |
oauth = ["authlib", "itsdangerous"]
|
486 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
487 |
[[package]]
|
488 |
name = "gradio-client"
|
489 |
version = "0.10.1"
|
@@ -617,6 +697,17 @@ files = [
|
|
617 |
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
|
618 |
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"]
|
619 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
620 |
[[package]]
|
621 |
name = "jinja2"
|
622 |
version = "3.1.3"
|
@@ -782,6 +873,26 @@ files = [
|
|
782 |
{file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"},
|
783 |
]
|
784 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
785 |
[[package]]
|
786 |
name = "markdown-it-py"
|
787 |
version = "3.0.0"
|
@@ -1206,6 +1317,21 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
|
|
1206 |
typing = ["typing-extensions"]
|
1207 |
xmp = ["defusedxml"]
|
1208 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1209 |
[[package]]
|
1210 |
name = "pydantic"
|
1211 |
version = "2.6.2"
|
@@ -1642,6 +1768,69 @@ files = [
|
|
1642 |
{file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"},
|
1643 |
]
|
1644 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1645 |
[[package]]
|
1646 |
name = "semantic-version"
|
1647 |
version = "2.10.0"
|
@@ -1707,6 +1896,20 @@ anyio = ">=3.4.0,<5"
|
|
1707 |
[package.extras]
|
1708 |
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
|
1709 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1710 |
[[package]]
|
1711 |
name = "termcolor"
|
1712 |
version = "2.4.0"
|
@@ -1923,7 +2126,86 @@ files = [
|
|
1923 |
{file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"},
|
1924 |
]
|
1925 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1926 |
[metadata]
|
1927 |
lock-version = "2.0"
|
1928 |
python-versions = "^3.11"
|
1929 |
-
content-hash = "
|
|
|
84 |
tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
|
85 |
tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
|
86 |
|
87 |
+
[[package]]
|
88 |
+
name = "autograd"
|
89 |
+
version = "1.6.2"
|
90 |
+
description = "Efficiently computes derivatives of numpy code."
|
91 |
+
optional = false
|
92 |
+
python-versions = "*"
|
93 |
+
files = [
|
94 |
+
{file = "autograd-1.6.2-py3-none-any.whl", hash = "sha256:208dde2a938e63b4f8f5049b1985505139e529068b0d26f8cd7771fd3eb145d5"},
|
95 |
+
{file = "autograd-1.6.2.tar.gz", hash = "sha256:8731e08a0c4e389d8695a40072ada4512641c113b6cace8f4cfbe8eb7e9aedeb"},
|
96 |
+
]
|
97 |
+
|
98 |
+
[package.dependencies]
|
99 |
+
future = ">=0.15.2"
|
100 |
+
numpy = ">=1.12"
|
101 |
+
|
102 |
+
[[package]]
|
103 |
+
name = "autograd-gamma"
|
104 |
+
version = "0.5.0"
|
105 |
+
description = "Autograd compatible approximations to the gamma family of functions"
|
106 |
+
optional = false
|
107 |
+
python-versions = "*"
|
108 |
+
files = [
|
109 |
+
{file = "autograd-gamma-0.5.0.tar.gz", hash = "sha256:f27abb7b8bb9cffc8badcbf59f3fe44a9db39e124ecacf1992b6d952934ac9c4"},
|
110 |
+
]
|
111 |
+
|
112 |
+
[package.dependencies]
|
113 |
+
autograd = ">=1.2.0"
|
114 |
+
scipy = ">=1.2.0"
|
115 |
+
|
116 |
[[package]]
|
117 |
name = "certifi"
|
118 |
version = "2024.2.2"
|
|
|
436 |
unicode = ["unicodedata2 (>=15.1.0)"]
|
437 |
woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
|
438 |
|
439 |
+
[[package]]
|
440 |
+
name = "formulaic"
|
441 |
+
version = "1.0.1"
|
442 |
+
description = "An implementation of Wilkinson formulas."
|
443 |
+
optional = false
|
444 |
+
python-versions = ">=3.7.2"
|
445 |
+
files = [
|
446 |
+
{file = "formulaic-1.0.1-py3-none-any.whl", hash = "sha256:be9e6127b98ba0293654be528fd070ede264f64d0247477c4d5af799b0b12cf2"},
|
447 |
+
{file = "formulaic-1.0.1.tar.gz", hash = "sha256:64dd7992a7aa5bbceb1e40679d0f01fc6f0ba12b7d23d78094a88c2edc68fba1"},
|
448 |
+
]
|
449 |
+
|
450 |
+
[package.dependencies]
|
451 |
+
interface-meta = ">=1.2.0"
|
452 |
+
numpy = ">=1.16.5"
|
453 |
+
pandas = ">=1.0"
|
454 |
+
scipy = ">=1.6"
|
455 |
+
typing-extensions = ">=4.2.0"
|
456 |
+
wrapt = ">=1.0"
|
457 |
+
|
458 |
+
[package.extras]
|
459 |
+
arrow = ["pyarrow (>=1)"]
|
460 |
+
calculus = ["sympy (>=1.3,!=1.10)"]
|
461 |
+
|
462 |
[[package]]
|
463 |
name = "fsspec"
|
464 |
version = "2024.2.0"
|
|
|
494 |
ssh = ["paramiko"]
|
495 |
tqdm = ["tqdm"]
|
496 |
|
497 |
+
[[package]]
|
498 |
+
name = "future"
|
499 |
+
version = "1.0.0"
|
500 |
+
description = "Clean single-source support for Python 3 and 2"
|
501 |
+
optional = false
|
502 |
+
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
503 |
+
files = [
|
504 |
+
{file = "future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216"},
|
505 |
+
{file = "future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05"},
|
506 |
+
]
|
507 |
+
|
508 |
[[package]]
|
509 |
name = "gradio"
|
510 |
version = "4.19.2"
|
|
|
547 |
[package.extras]
|
548 |
oauth = ["authlib", "itsdangerous"]
|
549 |
|
550 |
+
[[package]]
|
551 |
+
name = "gradio-calendar"
|
552 |
+
version = "0.0.4"
|
553 |
+
description = "Gradio component for selecting dates with a calendar 📆"
|
554 |
+
optional = false
|
555 |
+
python-versions = ">=3.8"
|
556 |
+
files = [
|
557 |
+
{file = "gradio_calendar-0.0.4-py3-none-any.whl", hash = "sha256:32f738ea94ed4dea1e4069a117d907fc3e8a2757fb2f4b0ce67a801f434928df"},
|
558 |
+
{file = "gradio_calendar-0.0.4.tar.gz", hash = "sha256:e4ea93cc7f6284bb8e547bb34d891776f1f0bebd320eae80bd644c0cd30694ce"},
|
559 |
+
]
|
560 |
+
|
561 |
+
[package.dependencies]
|
562 |
+
gradio = ">=4.0,<5.0"
|
563 |
+
|
564 |
+
[package.extras]
|
565 |
+
dev = ["build", "twine"]
|
566 |
+
|
567 |
[[package]]
|
568 |
name = "gradio-client"
|
569 |
version = "0.10.1"
|
|
|
697 |
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
|
698 |
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"]
|
699 |
|
700 |
+
[[package]]
|
701 |
+
name = "interface-meta"
|
702 |
+
version = "1.3.0"
|
703 |
+
description = "`interface_meta` provides a convenient way to expose an extensible API with enforced method signatures and consistent documentation."
|
704 |
+
optional = false
|
705 |
+
python-versions = ">=3.7,<4.0"
|
706 |
+
files = [
|
707 |
+
{file = "interface_meta-1.3.0-py3-none-any.whl", hash = "sha256:de35dc5241431886e709e20a14d6597ed07c9f1e8b4bfcffde2190ca5b700ee8"},
|
708 |
+
{file = "interface_meta-1.3.0.tar.gz", hash = "sha256:8a4493f8bdb73fb9655dcd5115bc897e207319e36c8835f39c516a2d7e9d79a1"},
|
709 |
+
]
|
710 |
+
|
711 |
[[package]]
|
712 |
name = "jinja2"
|
713 |
version = "3.1.3"
|
|
|
873 |
{file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"},
|
874 |
]
|
875 |
|
876 |
+
[[package]]
|
877 |
+
name = "lifelines"
|
878 |
+
version = "0.28.0"
|
879 |
+
description = "Survival analysis in Python, including Kaplan Meier, Nelson Aalen and regression"
|
880 |
+
optional = false
|
881 |
+
python-versions = ">=3.9"
|
882 |
+
files = [
|
883 |
+
{file = "lifelines-0.28.0-py3-none-any.whl", hash = "sha256:eacade0527a62d6086cb76f7dbf1e94f2f445a696dd7168723bc7a86e4737a99"},
|
884 |
+
{file = "lifelines-0.28.0.tar.gz", hash = "sha256:eecf726453fd409c94fef8a521f8e593bcd09337f920fe885131f01cfe58b25e"},
|
885 |
+
]
|
886 |
+
|
887 |
+
[package.dependencies]
|
888 |
+
autograd = ">=1.5"
|
889 |
+
autograd-gamma = ">=0.3"
|
890 |
+
formulaic = ">=0.2.2"
|
891 |
+
matplotlib = ">=3.0"
|
892 |
+
numpy = ">=1.14.0,<2.0"
|
893 |
+
pandas = ">=1.2.0"
|
894 |
+
scipy = ">=1.2.0"
|
895 |
+
|
896 |
[[package]]
|
897 |
name = "markdown-it-py"
|
898 |
version = "3.0.0"
|
|
|
1317 |
typing = ["typing-extensions"]
|
1318 |
xmp = ["defusedxml"]
|
1319 |
|
1320 |
+
[[package]]
|
1321 |
+
name = "plotly"
|
1322 |
+
version = "5.19.0"
|
1323 |
+
description = "An open-source, interactive data visualization library for Python"
|
1324 |
+
optional = false
|
1325 |
+
python-versions = ">=3.8"
|
1326 |
+
files = [
|
1327 |
+
{file = "plotly-5.19.0-py3-none-any.whl", hash = "sha256:906abcc5f15945765328c5d47edaa884bc99f5985fbc61e8cd4dc361f4ff8f5a"},
|
1328 |
+
{file = "plotly-5.19.0.tar.gz", hash = "sha256:5ea91a56571292ade3e3bc9bf712eba0b95a1fb0a941375d978cc79432e055f4"},
|
1329 |
+
]
|
1330 |
+
|
1331 |
+
[package.dependencies]
|
1332 |
+
packaging = "*"
|
1333 |
+
tenacity = ">=6.2.0"
|
1334 |
+
|
1335 |
[[package]]
|
1336 |
name = "pydantic"
|
1337 |
version = "2.6.2"
|
|
|
1768 |
{file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"},
|
1769 |
]
|
1770 |
|
1771 |
+
[[package]]
|
1772 |
+
name = "scipy"
|
1773 |
+
version = "1.12.0"
|
1774 |
+
description = "Fundamental algorithms for scientific computing in Python"
|
1775 |
+
optional = false
|
1776 |
+
python-versions = ">=3.9"
|
1777 |
+
files = [
|
1778 |
+
{file = "scipy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b"},
|
1779 |
+
{file = "scipy-1.12.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1"},
|
1780 |
+
{file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563"},
|
1781 |
+
{file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c"},
|
1782 |
+
{file = "scipy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd"},
|
1783 |
+
{file = "scipy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2"},
|
1784 |
+
{file = "scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08"},
|
1785 |
+
{file = "scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c"},
|
1786 |
+
{file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467"},
|
1787 |
+
{file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a"},
|
1788 |
+
{file = "scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba"},
|
1789 |
+
{file = "scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70"},
|
1790 |
+
{file = "scipy-1.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372"},
|
1791 |
+
{file = "scipy-1.12.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3"},
|
1792 |
+
{file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc"},
|
1793 |
+
{file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c"},
|
1794 |
+
{file = "scipy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338"},
|
1795 |
+
{file = "scipy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c"},
|
1796 |
+
{file = "scipy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:913d6e7956c3a671de3b05ccb66b11bc293f56bfdef040583a7221d9e22a2e35"},
|
1797 |
+
{file = "scipy-1.12.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba1b0c7256ad75401c73e4b3cf09d1f176e9bd4248f0d3112170fb2ec4db067"},
|
1798 |
+
{file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:730badef9b827b368f351eacae2e82da414e13cf8bd5051b4bdfd720271a5371"},
|
1799 |
+
{file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6546dc2c11a9df6926afcbdd8a3edec28566e4e785b915e849348c6dd9f3f490"},
|
1800 |
+
{file = "scipy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:196ebad3a4882081f62a5bf4aeb7326aa34b110e533aab23e4374fcccb0890dc"},
|
1801 |
+
{file = "scipy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:b360f1b6b2f742781299514e99ff560d1fe9bd1bff2712894b52abe528d1fd1e"},
|
1802 |
+
{file = "scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3"},
|
1803 |
+
]
|
1804 |
+
|
1805 |
+
[package.dependencies]
|
1806 |
+
numpy = ">=1.22.4,<1.29.0"
|
1807 |
+
|
1808 |
+
[package.extras]
|
1809 |
+
dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"]
|
1810 |
+
doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"]
|
1811 |
+
test = ["asv", "gmpy2", "hypothesis", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
|
1812 |
+
|
1813 |
+
[[package]]
|
1814 |
+
name = "seaborn"
|
1815 |
+
version = "0.13.2"
|
1816 |
+
description = "Statistical data visualization"
|
1817 |
+
optional = false
|
1818 |
+
python-versions = ">=3.8"
|
1819 |
+
files = [
|
1820 |
+
{file = "seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987"},
|
1821 |
+
{file = "seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7"},
|
1822 |
+
]
|
1823 |
+
|
1824 |
+
[package.dependencies]
|
1825 |
+
matplotlib = ">=3.4,<3.6.1 || >3.6.1"
|
1826 |
+
numpy = ">=1.20,<1.24.0 || >1.24.0"
|
1827 |
+
pandas = ">=1.2"
|
1828 |
+
|
1829 |
+
[package.extras]
|
1830 |
+
dev = ["flake8", "flit", "mypy", "pandas-stubs", "pre-commit", "pytest", "pytest-cov", "pytest-xdist"]
|
1831 |
+
docs = ["ipykernel", "nbconvert", "numpydoc", "pydata_sphinx_theme (==0.10.0rc2)", "pyyaml", "sphinx (<6.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-issues"]
|
1832 |
+
stats = ["scipy (>=1.7)", "statsmodels (>=0.12)"]
|
1833 |
+
|
1834 |
[[package]]
|
1835 |
name = "semantic-version"
|
1836 |
version = "2.10.0"
|
|
|
1896 |
[package.extras]
|
1897 |
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
|
1898 |
|
1899 |
+
[[package]]
|
1900 |
+
name = "tenacity"
|
1901 |
+
version = "8.2.3"
|
1902 |
+
description = "Retry code until it succeeds"
|
1903 |
+
optional = false
|
1904 |
+
python-versions = ">=3.7"
|
1905 |
+
files = [
|
1906 |
+
{file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"},
|
1907 |
+
{file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"},
|
1908 |
+
]
|
1909 |
+
|
1910 |
+
[package.extras]
|
1911 |
+
doc = ["reno", "sphinx", "tornado (>=4.5)"]
|
1912 |
+
|
1913 |
[[package]]
|
1914 |
name = "termcolor"
|
1915 |
version = "2.4.0"
|
|
|
2126 |
{file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"},
|
2127 |
]
|
2128 |
|
2129 |
+
[[package]]
|
2130 |
+
name = "wrapt"
|
2131 |
+
version = "1.16.0"
|
2132 |
+
description = "Module for decorators, wrappers and monkey patching."
|
2133 |
+
optional = false
|
2134 |
+
python-versions = ">=3.6"
|
2135 |
+
files = [
|
2136 |
+
{file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
|
2137 |
+
{file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
|
2138 |
+
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
|
2139 |
+
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
|
2140 |
+
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
|
2141 |
+
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
|
2142 |
+
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
|
2143 |
+
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
|
2144 |
+
{file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
|
2145 |
+
{file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
|
2146 |
+
{file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
|
2147 |
+
{file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
|
2148 |
+
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
|
2149 |
+
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
|
2150 |
+
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
|
2151 |
+
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
|
2152 |
+
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
|
2153 |
+
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
|
2154 |
+
{file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
|
2155 |
+
{file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
|
2156 |
+
{file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
|
2157 |
+
{file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
|
2158 |
+
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
|
2159 |
+
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
|
2160 |
+
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
|
2161 |
+
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
|
2162 |
+
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
|
2163 |
+
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
|
2164 |
+
{file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
|
2165 |
+
{file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
|
2166 |
+
{file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
|
2167 |
+
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
|
2168 |
+
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
|
2169 |
+
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
|
2170 |
+
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
|
2171 |
+
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
|
2172 |
+
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
|
2173 |
+
{file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
|
2174 |
+
{file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
|
2175 |
+
{file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
|
2176 |
+
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
|
2177 |
+
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
|
2178 |
+
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
|
2179 |
+
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
|
2180 |
+
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
|
2181 |
+
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
|
2182 |
+
{file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
|
2183 |
+
{file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
|
2184 |
+
{file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
|
2185 |
+
{file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
|
2186 |
+
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
|
2187 |
+
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
|
2188 |
+
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
|
2189 |
+
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
|
2190 |
+
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
|
2191 |
+
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
|
2192 |
+
{file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
|
2193 |
+
{file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
|
2194 |
+
{file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
|
2195 |
+
{file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
|
2196 |
+
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
|
2197 |
+
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
|
2198 |
+
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
|
2199 |
+
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
|
2200 |
+
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
|
2201 |
+
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
|
2202 |
+
{file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
|
2203 |
+
{file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
|
2204 |
+
{file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
|
2205 |
+
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
|
2206 |
+
]
|
2207 |
+
|
2208 |
[metadata]
|
2209 |
lock-version = "2.0"
|
2210 |
python-versions = "^3.11"
|
2211 |
+
content-hash = "93c07a0e554c6697440ab4476e4eaa56b3e41e1d857b3052df1d13e27ba77c4c"
|
pyproject.toml
CHANGED
@@ -12,6 +12,12 @@ gradio = "^4.19.2"
|
|
12 |
pandas = "^2.2.1"
|
13 |
matplotlib = "^3.8.3"
|
14 |
termcolor = "^2.4.0"
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
|
17 |
[build-system]
|
|
|
12 |
pandas = "^2.2.1"
|
13 |
matplotlib = "^3.8.3"
|
14 |
termcolor = "^2.4.0"
|
15 |
+
seaborn = "^0.13.2"
|
16 |
+
numpy = "^1.26.4"
|
17 |
+
lifelines = "^0.28.0"
|
18 |
+
plotly = "^5.19.0"
|
19 |
+
gradio-calendar = "^0.0.4"
|
20 |
+
scipy = "^1.12.0"
|
21 |
|
22 |
|
23 |
[build-system]
|