Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,163 +1,157 @@
|
|
| 1 |
import gradio as gr
|
|
|
|
| 2 |
import openpyxl
|
| 3 |
-
from openpyxl import
|
| 4 |
-
|
| 5 |
import calendar
|
| 6 |
-
|
| 7 |
|
| 8 |
-
def
|
| 9 |
"""
|
| 10 |
-
μ
λ‘λλ μμ
νμΌ
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
"""
|
| 14 |
-
#
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
#
|
| 18 |
-
|
| 19 |
-
|
|
|
|
| 20 |
|
| 21 |
-
#
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
# "μλ³Έλ°μ΄ν°" μνΈμ Bμ΄(리뷰 λ μ§)μ κΈ°μ€μΌλ‘ λ°μ΄ν°λ₯Ό μ½μ (첫λ²μ§Έ νμ headerλ‘ κ°μ )
|
| 39 |
-
review_dates = []
|
| 40 |
-
for row in source_ws.iter_rows(min_row=2, min_col=2, max_col=2, values_only=True):
|
| 41 |
-
cell_value = row[0]
|
| 42 |
-
if cell_value is None:
|
| 43 |
-
continue
|
| 44 |
-
# μ
μ κ°μ΄ datetime κ°μ²΄μΈμ§ νμΈ, μλλ©΄ λ¬Έμμ΄λ‘ κ°μ νμ¬ νμ±
|
| 45 |
-
if isinstance(cell_value, datetime):
|
| 46 |
-
review_date = cell_value
|
| 47 |
-
else:
|
| 48 |
-
try:
|
| 49 |
-
# μμ: "25.02.25" νμ
|
| 50 |
-
review_date = datetime.strptime(str(cell_value).strip(), "%y.%m.%d")
|
| 51 |
-
except Exception as e:
|
| 52 |
-
# λ체 νμ (μ: "2025-02-25")λ‘ μλ
|
| 53 |
-
try:
|
| 54 |
-
review_date = datetime.strptime(str(cell_value).strip(), "%Y-%m-%d")
|
| 55 |
-
except Exception as e:
|
| 56 |
-
continue
|
| 57 |
-
review_dates.append(review_date)
|
| 58 |
-
|
| 59 |
-
# νμ¬ λ μ§ κΈ°μ€μΌλ‘ μ΅κ·Ό 3λ
(current_year-2, current_year-1, current_year) κ³μ°
|
| 60 |
-
today = datetime.today()
|
| 61 |
-
current_year = today.year
|
| 62 |
-
recent_years = [current_year - 2, current_year - 1, current_year]
|
| 63 |
|
| 64 |
-
#
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
for m in range(1, 13):
|
| 68 |
-
count = sum(1 for d in review_dates if d.year == yr and d.month == m)
|
| 69 |
-
monthly_counts.append(count)
|
| 70 |
|
| 71 |
-
#
|
| 72 |
-
|
| 73 |
-
dashboard_ws = main_wb["λμ보λλ°μ΄ν°"]
|
| 74 |
-
else:
|
| 75 |
-
dashboard_ws = main_wb.create_sheet("λμ보λλ°μ΄ν°")
|
| 76 |
|
| 77 |
-
# λ―Έμ
2: μλ³ λ¦¬λ·° 건μ
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
|
|
|
|
|
|
| 82 |
|
| 83 |
-
#
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
-
# λ―Έμ
4
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
for
|
| 102 |
-
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
-
# λ―Έμ
5
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
|
| 121 |
-
#
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
# λ³κ²½λ μν¬λΆμ BytesIOμ μ μ₯ ν bytes λ°μ΄ν°λ₯Ό λ°ν
|
| 125 |
-
output = io.BytesIO()
|
| 126 |
-
main_wb.save(output)
|
| 127 |
output.seek(0)
|
| 128 |
-
|
|
|
|
|
|
|
| 129 |
|
| 130 |
def get_year_options():
|
| 131 |
"""
|
| 132 |
-
|
|
|
|
| 133 |
"""
|
| 134 |
-
|
| 135 |
-
|
|
|
|
| 136 |
return options
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
with gr.Blocks() as demo:
|
| 139 |
-
gr.Markdown("## 리뷰λΆμ
|
| 140 |
with gr.Row():
|
| 141 |
-
file_input = gr.File(label="μλ³Έ μμ
νμΌ μ
λ‘λ", file_types=[".xlsx"])
|
| 142 |
year_dropdown = gr.Dropdown(label="λΆμλ
λ μ ν", choices=get_year_options(), value=get_year_options()[0])
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
def process(file_obj, selected_year):
|
| 147 |
-
if file_obj is None:
|
| 148 |
-
return None
|
| 149 |
-
# Gradio File μ»΄ν¬λνΈλ‘ μ
λ‘λλ νμΌμ file_obj['name'] κ²½λ‘λ₯Ό κ°μ§.
|
| 150 |
-
with open(file_obj.name, "rb") as f:
|
| 151 |
-
file_bytes = f.read()
|
| 152 |
-
# BytesIO κ°μ²΄λ‘ λ³ννμ¬ λΆμ ν¨μ νΈμΆ
|
| 153 |
-
result_bytes = analyze_excel(io.BytesIO(file_bytes), selected_year)
|
| 154 |
-
# λΆμ κ²°κ³Όλ₯Ό μμ νμΌ("λΆμκ²°κ³Ό.xlsx")λ‘ μ μ₯νμ¬ λ€μ΄λ‘λ κ°λ₯νλλ‘ ν¨
|
| 155 |
-
output_path = "λΆμκ²°κ³Ό.xlsx"
|
| 156 |
-
with open(output_path, "wb") as out_f:
|
| 157 |
-
out_f.write(result_bytes)
|
| 158 |
-
return output_path
|
| 159 |
-
|
| 160 |
-
analyze_button.click(fn=process, inputs=[file_input, year_dropdown], outputs=download_output)
|
| 161 |
|
| 162 |
if __name__ == "__main__":
|
| 163 |
demo.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import pandas as pd
|
| 3 |
import openpyxl
|
| 4 |
+
from openpyxl.utils import get_column_letter
|
| 5 |
+
import datetime
|
| 6 |
import calendar
|
| 7 |
+
from io import BytesIO
|
| 8 |
|
| 9 |
+
def process_file(uploaded_file, selected_year):
|
| 10 |
"""
|
| 11 |
+
1. μ
λ‘λλ μλ³Έ μμ
νμΌμ μ½μ΄ DataFrameμΌλ‘ λ³νν©λλ€.
|
| 12 |
+
2. λμΌν μμΉμ μλ ν
νλ¦Ώ νμΌ "리뷰λΆμ.ver1.0"μ λΆλ¬μ΅λλ€.
|
| 13 |
+
3. ν
νλ¦Ώ λ΄ "μλ³Έλ°μ΄ν°" μνΈμ μ
λ‘λλ λ°μ΄ν°λ₯Ό κ·Έλλ‘ κΈ°λ‘ν©λλ€.
|
| 14 |
+
4. μ
λ‘λ λ°μ΄ν°μ 리뷰 λ μ§(Bμ΄)λ₯Ό κΈ°λ°μΌλ‘ μ¬λ¬ λ―Έμ
μ ν΄λΉνλ μ§κ³(μ΅κ·Ό 3λ
μλ³/λ
λλ³, μ νλ
λ μλ³/μμΌλ³)λ₯Ό μννμ¬
|
| 15 |
+
"λμ보λλ°μ΄ν°" μνΈμ μ§μ μ
μ κ²°κ³Όκ°μ κΈ°λ‘ν©λλ€.
|
| 16 |
+
- λ―Έμ
2: μ΅κ·Ό 3λ
μ κ° λ
λ(μ: 23,24,25)μ κ° μλ³ λ¦¬λ·° 건μλ₯Ό C5 ~ C40 μ
μ κΈ°λ‘ν©λλ€.
|
| 17 |
+
- λ―Έμ
3: μ΅κ·Ό 3λ
μ μ°λλ³ λ¦¬λ·° 건μλ₯Ό F5(νμ¬λ
λ), F6(μ λ
λ), F7(μ μ λ
λ)μ κΈ°λ‘νκ³ , νμ¬λ
λ(λμ리)λ₯Ό E2μ
μ μ
λ ₯ν©λλ€.
|
| 18 |
+
- λ―Έμ
4: λλ‘λ€μ΄μΌλ‘ μ νλ λ
λ(μ΅κ·Ό 5λ
μ€ νλ)μ μλ³ λ¦¬λ·° 건μλ₯Ό I5 ~ I16 μ
μ κΈ°λ‘ν©λλ€.
|
| 19 |
+
- λ―Έμ
5: μ νλ λ
λμ 1μ1μΌλΆν° 12μ31μΌκΉμ§(365μΌ κΈ°μ€) μΌλ³ 리뷰 건μλ₯Ό L5 ~ L369 μ
μ κΈ°λ‘νκ³ , μ νλ
λ(λμ리)λ₯Ό K2μ
μ μ
λ ₯ν©λλ€.
|
| 20 |
+
5. μ΅μ’
μμ λ μν¬λΆμ BytesIO κ°μ²΄λ‘ μ μ₯νμ¬ λ°νν©λλ€.
|
| 21 |
"""
|
| 22 |
+
# μ
λ‘λλ μμ
νμΌμ pandas DataFrameμΌλ‘ μ½κΈ° (첫λ²μ§Έ μνΈλ₯Ό μ¬μ©)
|
| 23 |
+
df = pd.read_excel(uploaded_file, engine="openpyxl")
|
| 24 |
+
# 리뷰 λ μ§λ μ
λ‘λ νμΌμ λλ²μ§Έ μ΄(Bμ΄)λ‘ κ°μ νκ³ , λ μ§ νμμΌλ‘ λ³ν
|
| 25 |
+
review_date_col = df.columns[1]
|
| 26 |
+
df[review_date_col] = pd.to_datetime(df[review_date_col], errors='coerce')
|
| 27 |
|
| 28 |
+
# ν
νλ¦Ώ νμΌ "리뷰λΆμ.ver1.0" λΆλ¬μ€κΈ° (λμΌ κ²½λ‘μ μμΉ)
|
| 29 |
+
wb = openpyxl.load_workbook("리뷰λΆμ.ver1.0")
|
| 30 |
+
ws_origin = wb["μλ³Έλ°μ΄ν°"]
|
| 31 |
+
ws_dashboard = wb["λμ보λλ°μ΄ν°"]
|
| 32 |
|
| 33 |
+
# [λ―Έμ
1] μ
λ‘λλ μλ£λ₯Ό "μλ³Έλ°μ΄ν°" μνΈμ λμΌνκ² μ μ₯
|
| 34 |
+
# κΈ°μ‘΄ λ°μ΄ν° μμ (μ
λ΄μ© λͺ¨λ μ΄κΈ°ν)
|
| 35 |
+
for row in ws_origin.iter_rows(min_row=1, max_row=ws_origin.max_row, min_col=1, max_col=ws_origin.max_column):
|
| 36 |
+
for cell in row:
|
| 37 |
+
cell.value = None
|
| 38 |
+
# DataFrameμ ν€λ λ° λ°μ΄ν°λ₯Ό μμλλ‘ κΈ°λ‘ (μμ
μ A1μ
λΆν° κΈ°λ‘)
|
| 39 |
+
for col_idx, col_name in enumerate(df.columns, start=1):
|
| 40 |
+
ws_origin.cell(row=1, column=col_idx, value=col_name)
|
| 41 |
+
for row_idx, row in df.iterrows():
|
| 42 |
+
for col_idx, value in enumerate(row, start=1):
|
| 43 |
+
ws_origin.cell(row=row_idx+2, column=col_idx, value=value)
|
| 44 |
+
|
| 45 |
+
# μ§κ³ μ²λ¦¬λ₯Ό μν΄ λ¦¬λ·° λ μ§κ° μλ νλ§ νν°λ§
|
| 46 |
+
df_dates = df.dropna(subset=[review_date_col]).copy()
|
| 47 |
+
df_dates[review_date_col] = pd.to_datetime(df_dates[review_date_col], errors='coerce')
|
| 48 |
+
df_dates = df_dates.dropna(subset=[review_date_col])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
+
# νμ¬λ
λ μ 보 (μ: 2025λ
β λμ리 25)
|
| 51 |
+
current_full_year = datetime.datetime.now().year
|
| 52 |
+
current_two_digit = int(str(current_full_year)[-2:])
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
+
# [λ―Έμ
2,3] μ΅κ·Ό 3λ
: (νμ¬λ
λ, μ λ
λ, μ μ λ
λ)
|
| 55 |
+
recent_years_full = [current_full_year - i for i in range(2, -1, -1)] # μ: [2023, 2024, 2025]
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
+
# λ―Έμ
2: μ΅κ·Ό 3λ
μ μλ³ λ¦¬λ·° 건μ μ§κ³ (κ° λ
λλ³ 1μ~12μ)
|
| 58 |
+
df_recent = df_dates[df_dates[review_date_col].dt.year >= (current_full_year - 2)]
|
| 59 |
+
month_counts = {}
|
| 60 |
+
for year in recent_years_full:
|
| 61 |
+
for month in range(1, 13):
|
| 62 |
+
count = df_recent[(df_recent[review_date_col].dt.year == year) & (df_recent[review_date_col].dt.month == month)].shape[0]
|
| 63 |
+
month_counts[(year, month)] = count
|
| 64 |
|
| 65 |
+
# "λμ보λλ°μ΄ν°" μνΈμ C5μ
λΆν° C40μ
κΉμ§ μμλλ‘ κΈ°λ‘ (첫 12μ
: μ μ λ
λ, κ·Έ λ€μ: μ λ
λ, λ§μ§λ§ 12μ
: νμ¬λ
λ)
|
| 66 |
+
start_row = 5
|
| 67 |
+
col_month = 3 # Cμ΄
|
| 68 |
+
for year in recent_years_full:
|
| 69 |
+
for month in range(1, 13):
|
| 70 |
+
ws_dashboard.cell(row=start_row, column=col_month, value=month_counts.get((year, month), 0))
|
| 71 |
+
start_row += 1
|
| 72 |
+
|
| 73 |
+
# λ―Έμ
3: μ΅κ·Ό 3λ
μ μ°λλ³ λ¦¬λ·° 건μ μ§κ³ λ° κΈ°λ‘
|
| 74 |
+
year_counts = {}
|
| 75 |
+
for year in recent_years_full:
|
| 76 |
+
count = df_recent[df_recent[review_date_col].dt.year == year].shape[0]
|
| 77 |
+
year_counts[year] = count
|
| 78 |
+
# "λμ보λλ°μ΄ν°" μνΈμ F5μ
μ νμ¬λ
λ, F6μ
μ μ λ
λ, F7μ
μ μ μ λ
λ 건μ μ
λ ₯
|
| 79 |
+
ws_dashboard.cell(row=5, column=6, value=year_counts.get(current_full_year, 0)) # F5: νμ¬λ
λ
|
| 80 |
+
ws_dashboard.cell(row=6, column=6, value=year_counts.get(current_full_year - 1, 0)) # F6: μ λ
λ
|
| 81 |
+
ws_dashboard.cell(row=7, column=6, value=year_counts.get(current_full_year - 2, 0)) # F7: μ μ λ
λ
|
| 82 |
+
# νμ¬λ
λ(λμ리)λ₯Ό E2μ
μ μ
λ ₯
|
| 83 |
+
ws_dashboard.cell(row=2, column=5, value=str(current_two_digit))
|
| 84 |
|
| 85 |
+
# [λ―Έμ
4] μ νλ λ
λμ μλ³ λ¦¬λ·° 건μ μ§κ³
|
| 86 |
+
# λλ‘λ€μ΄μΌλ‘ μ νλ κ°μ λμ리 λ¬Έμμ΄ (μ: "25") β full year κ³μ°
|
| 87 |
+
selected_year_int = int(selected_year)
|
| 88 |
+
full_selected_year = current_full_year - ((current_two_digit) - selected_year_int)
|
| 89 |
+
df_selected = df_dates[df_dates[review_date_col].dt.year == full_selected_year]
|
| 90 |
+
month_counts_selected = {}
|
| 91 |
+
for month in range(1, 13):
|
| 92 |
+
count = df_selected[df_selected[review_date_col].dt.month == month].shape[0]
|
| 93 |
+
month_counts_selected[month] = count
|
| 94 |
+
# "λμ보λλ°μ΄ν°" μνΈμ I5μ
λΆν° I16μ
κΉμ§ κΈ°λ‘ (1μ ~ 12μ)
|
| 95 |
+
start_row = 5
|
| 96 |
+
col_selected_month = 9 # Iμ΄
|
| 97 |
+
for month in range(1, 13):
|
| 98 |
+
ws_dashboard.cell(row=start_row, column=col_selected_month, value=month_counts_selected.get(month, 0))
|
| 99 |
+
start_row += 1
|
| 100 |
|
| 101 |
+
# [λ―Έμ
5] μ νλ λ
λμ μμΌλ³ 리뷰 건μ μ§κ³ (1μ 1μΌ ~ 12μ 31μΌ: μ΄ 365μΌ)
|
| 102 |
+
day_counts_selected = []
|
| 103 |
+
for month in range(1, 13):
|
| 104 |
+
num_days = calendar.monthrange(full_selected_year, month)[1]
|
| 105 |
+
for day in range(1, num_days + 1):
|
| 106 |
+
count = df_selected[(df_selected[review_date_col].dt.month == month) & (df_selected[review_date_col].dt.day == day)].shape[0]
|
| 107 |
+
day_counts_selected.append(count)
|
| 108 |
+
# "λμ보λλ°μ΄ν°" μνΈμ L5μ
λΆν° L369μ
κΉμ§ κΈ°λ‘
|
| 109 |
+
start_row = 5
|
| 110 |
+
col_selected_day = 12 # Lμ΄
|
| 111 |
+
for count in day_counts_selected:
|
| 112 |
+
ws_dashboard.cell(row=start_row, column=col_selected_day, value=count)
|
| 113 |
+
start_row += 1
|
| 114 |
+
# μ νλ λ
λμ λμ리 κ°μ K2μ
μ μ
λ ₯
|
| 115 |
+
ws_dashboard.cell(row=2, column=11, value=str(selected_year_int))
|
| 116 |
|
| 117 |
+
# μ΅μ’
μμ λ μν¬λΆμ BytesIO κ°μ²΄μ μ μ₯νμ¬ λ°ν
|
| 118 |
+
output = BytesIO()
|
| 119 |
+
wb.save(output)
|
|
|
|
|
|
|
|
|
|
| 120 |
output.seek(0)
|
| 121 |
+
# λ°ν νμΌμ νμΌλͺ
μ μ§μ (λ€μ΄λ‘λ μ μ¬μ©)
|
| 122 |
+
output.name = "리뷰λΆμ_λ³΄κ³ μ.xlsx"
|
| 123 |
+
return output
|
| 124 |
|
| 125 |
def get_year_options():
|
| 126 |
"""
|
| 127 |
+
λλ‘λ€μ΄μ μ ν νλͺ©μ μ΅κ·Ό 5λ
(νμ¬λ
λλΆν° μ 4λ
)μΌλ‘ μμ±ν©λλ€.
|
| 128 |
+
μλ₯Ό λ€μ΄, νμ¬λ
λ 2025λ
μ΄λ©΄ μ΅μ
μ "25", "24", "23", "22", "21"μ΄ λ©λλ€.
|
| 129 |
"""
|
| 130 |
+
current_full_year = datetime.datetime.now().year
|
| 131 |
+
current_two_digit = int(str(current_full_year)[-2:])
|
| 132 |
+
options = [str(current_two_digit - i) for i in range(0, 5)]
|
| 133 |
return options
|
| 134 |
|
| 135 |
+
def generate_report(file, selected_year):
|
| 136 |
+
# νμΌ μ
λ‘λκ° μμΌλ©΄ None λ°ν
|
| 137 |
+
if file is None:
|
| 138 |
+
return None
|
| 139 |
+
# gr.File μ
λ‘λ μ fileμ dict ννλ‘ {"name": ..., "data": ...}λ‘ μ λ¬λ¨
|
| 140 |
+
if isinstance(file, dict):
|
| 141 |
+
file_obj = BytesIO(file["data"])
|
| 142 |
+
else:
|
| 143 |
+
file_obj = file
|
| 144 |
+
output_bytes = process_file(file_obj, selected_year)
|
| 145 |
+
return output_bytes
|
| 146 |
+
|
| 147 |
with gr.Blocks() as demo:
|
| 148 |
+
gr.Markdown("## 리뷰λΆμ λ³΄κ³ μ μμ± μ€νμ΄μ€")
|
| 149 |
with gr.Row():
|
| 150 |
+
file_input = gr.File(label="μλ³Έ μμ
νμΌ μ
λ‘λ", file_types=[".xlsx", ".xls"])
|
| 151 |
year_dropdown = gr.Dropdown(label="λΆμλ
λ μ ν", choices=get_year_options(), value=get_year_options()[0])
|
| 152 |
+
output_file = gr.File(label="μμ±λ λ³΄κ³ μ λ€μ΄λ‘λ")
|
| 153 |
+
generate_button = gr.Button("λ³΄κ³ μ μμ±")
|
| 154 |
+
generate_button.click(fn=generate_report, inputs=[file_input, year_dropdown], outputs=output_file)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
if __name__ == "__main__":
|
| 157 |
demo.launch()
|