dvsaudit commited on
Commit
ad023fd
·
verified ·
1 Parent(s): 499d3f4

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +30 -0
  2. app.py +240 -0
  3. docker-compose.yml +13 -0
  4. requirements.txt +7 -0
Dockerfile ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ # Set environment variables
4
+ ENV PYTHONDONTWRITEBYTECODE=1
5
+ ENV PYTHONUNBUFFERED=1
6
+
7
+ # Create a non-root user (Standard HF)
8
+ RUN useradd -m -u 1000 user
9
+ WORKDIR /home/user/app
10
+
11
+ # Install system dependencies
12
+ RUN apt-get update && apt-get install -y \
13
+ curl \
14
+ && rm -rf /var/lib/apt/lists/*
15
+
16
+ # Install python dependencies globally as root
17
+ COPY requirements.txt .
18
+ RUN pip install --no-cache-dir -r requirements.txt
19
+
20
+ # Copy application and set ownership
21
+ COPY --chown=user:user . .
22
+
23
+ # Expose port (HF requirement)
24
+ EXPOSE 7860
25
+
26
+ # Switch to user
27
+ USER user
28
+
29
+ # Run application with WebSocket & Custom Domain fixes
30
+ CMD ["python", "-m", "streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0", "--browser.gatherUsageStats=false", "--server.enableCORS=false", "--server.enableXsrfProtection=false"]
app.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import io
5
+
6
+ # Page Config
7
+ st.set_page_config(page_title="All Anomaly Analysis", layout="wide")
8
+
9
+ st.title('Aplikasi Analisa THC Pinjaman dan Simpanan')
10
+ st.markdown("""
11
+ ## File yang dibutuhkan
12
+ 1. **Anomali Simpanan.xlsx**
13
+ 2. **Anomali Pinjaman.xlsx**
14
+ 3. **DbSimpanan.xlsx**
15
+ - Hapus bagian header terlebih dahulu
16
+ - Nama File harus DbSimpanan.xlsx dan sheet atau lembar nya IA_SimpananDB
17
+ """)
18
+
19
+ def format_no(no):
20
+ try:
21
+ if pd.notna(no):
22
+ return f'{int(no):02d}.'
23
+ else:
24
+ return ''
25
+ except (ValueError, TypeError):
26
+ return str(no)
27
+
28
+ def format_center(center):
29
+ try:
30
+ if pd.notna(center):
31
+ return f'{int(center):03d}'
32
+ else:
33
+ return ''
34
+ except (ValueError, TypeError):
35
+ return str(center)
36
+
37
+ def format_kelompok(kelompok):
38
+ try:
39
+ if pd.notna(kelompok):
40
+ return f'{int(kelompok):02d}'
41
+ else:
42
+ return ''
43
+ except (ValueError, TypeError):
44
+ return str(kelompok)
45
+
46
+ def load_data(uploaded_files):
47
+ dfs = {}
48
+ for file in uploaded_files:
49
+ try:
50
+ excel_file = pd.ExcelFile(file, engine='openpyxl')
51
+
52
+ for sheet_name in excel_file.sheet_names:
53
+ df = pd.read_excel(excel_file, sheet_name=sheet_name)
54
+ key = f"{file.name}_{sheet_name}"
55
+ dfs[key] = df
56
+
57
+ st.success(f"File {file.name} berhasil diunggah.")
58
+ except Exception as e:
59
+ st.error(f"Terjadi kesalahan saat memproses file {file.name}: {str(e)}")
60
+ return dfs
61
+
62
+ def process_data(dfs):
63
+ try:
64
+ # Pinjaman
65
+ df_pu = dfs['Anomali Pinjaman.xlsx_Anomali PU'][['ID','CEK KRITERIA']].rename(columns={'CEK KRITERIA':'PU'})
66
+ df_pu['PU'] = df_pu['PU'].replace({True: 0, False: 1})
67
+
68
+ df_pmb = dfs['Anomali Pinjaman.xlsx_Anomali PMB'][['ID','CEK KRITERIA']].rename(columns={'CEK KRITERIA':'PMB'})
69
+ df_pmb['PMB'] = df_pmb['PMB'].replace({True: 0, False: 1})
70
+
71
+ df_psa = dfs['Anomali Pinjaman.xlsx_Anomali PSA'][['ID','CEK KRITERIA','Sukarela Sesuai','Wajib Sesuai','Pensiun Sesuai']]
72
+ df_psa[['CEK KRITERIA', 'Sukarela Sesuai', 'Wajib Sesuai', 'Pensiun Sesuai']] = df_psa[['CEK KRITERIA', 'Sukarela Sesuai', 'Wajib Sesuai', 'Pensiun Sesuai']].replace({True: 0, False: 1})
73
+ df_psa['PSA'] = df_psa[['CEK KRITERIA', 'Sukarela Sesuai', 'Wajib Sesuai', 'Pensiun Sesuai']].sum(axis=1)
74
+ df_psa = df_psa[['ID','PSA']]
75
+
76
+ df_prr = dfs['Anomali Pinjaman.xlsx_Anomali PRR'][['ID','CEK KRITERIA','Sukarela Sesuai','Wajib Sesuai','Pensiun Sesuai']]
77
+ df_prr[['CEK KRITERIA', 'Sukarela Sesuai', 'Wajib Sesuai', 'Pensiun Sesuai']] = df_prr[['CEK KRITERIA', 'Sukarela Sesuai', 'Wajib Sesuai', 'Pensiun Sesuai']].replace({True: 0, False: 1})
78
+ df_prr['PRR'] = df_prr[['CEK KRITERIA', 'Sukarela Sesuai', 'Wajib Sesuai', 'Pensiun Sesuai']].sum(axis=1)
79
+ df_prr = df_prr[['ID','PRR']]
80
+
81
+ df_ptn = dfs['Anomali Pinjaman.xlsx_Anomali PTN'][['ID','SEMUA KRITERIA TERPENUHI']].rename(columns={'SEMUA KRITERIA TERPENUHI':'PTN'})
82
+ df_ptn['PTN'] = df_ptn['PTN'].replace({True: 0, False: 1})
83
+
84
+ df_arta = dfs['Anomali Pinjaman.xlsx_Anomali ARTA'][['ID','CEK KRITERIA']].rename(columns={'CEK KRITERIA':'ARTA'})
85
+ df_arta['ARTA'] = df_arta['ARTA'].replace({True: 0, False: 1})
86
+
87
+ df_dtp = dfs['Anomali Pinjaman.xlsx_Anomali DTP'][['ID','CEK KRITERIA']].rename(columns={'CEK KRITERIA':'DTP'})
88
+ df_dtp['DTP'] = df_dtp['DTP'].replace({True: 0, False: 1})
89
+
90
+ # Simpanan
91
+ df_sukarela = dfs['Anomali Simpanan.xlsx_Sukarela'][['ID','Transaksi > 0 & ≠ Modus Sukarela']].rename(columns={'Transaksi > 0 & ≠ Modus Sukarela':'SUKARELA'})
92
+ df_sihara = dfs['Anomali Simpanan.xlsx_Sihara'][['ID','TRANSAKSI TIDAK SESUAI']].rename(columns={'TRANSAKSI TIDAK SESUAI':'HARI RAYA'})
93
+ df_pensiun = dfs['Anomali Simpanan.xlsx_Pensiun'][['ID','Anomali']].rename(columns={'Anomali':'PENSIUN'})
94
+
95
+ # Merge all data
96
+ df_selected_all = dfs['DbSimpanan.xlsx_IA_SimpananDB'][['Client ID','Client Name','Center ID','Group ID','Meeting Day','Officer Name']].rename(columns={
97
+ 'Client ID':'ID', 'Client Name': 'NAMA', 'Center ID': 'CENTER', 'Group ID': 'KELOMPOK', 'Meeting Day': 'HARI', 'Officer Name': 'STAF'
98
+ })
99
+
100
+ for df in [df_sukarela, df_pensiun, df_sihara, df_pu, df_pmb, df_psa, df_prr, df_ptn, df_arta, df_dtp]:
101
+ df_selected_all = df_selected_all.merge(df, on='ID', how='left')
102
+
103
+ df_selected_all = df_selected_all.fillna(0)
104
+
105
+ anomali_columns = ['SUKARELA', 'PENSIUN', 'HARI RAYA', 'PU', 'PMB', 'PSA', 'PRR', 'PTN', 'ARTA', 'DTP']
106
+ df_selected_all['TOTAL ANOMALI'] = df_selected_all[anomali_columns].sum(axis=1)
107
+
108
+ df_selected_all = df_selected_all[['ID', 'NAMA', 'CENTER', 'KELOMPOK', 'HARI', 'STAF'] + anomali_columns + ['TOTAL ANOMALI']]
109
+ df_selected_all = df_selected_all.drop_duplicates(subset=['ID', 'NAMA'])
110
+
111
+ # Pisahkan anomali simpanan dan pinjaman
112
+ anomali_simpanan = ['SUKARELA', 'PENSIUN', 'HARI RAYA']
113
+ anomali_pinjaman = ['PU', 'PMB', 'PSA', 'PRR', 'PTN', 'ARTA', 'DTP']
114
+
115
+ df_selected_all['Total Anomali Simpanan'] = df_selected_all[anomali_simpanan].sum(axis=1)
116
+ df_selected_all['Total Anomali Pinjaman'] = df_selected_all[anomali_pinjaman].sum(axis=1)
117
+
118
+ # Buat dictionary untuk memetakan hari bahasa Indonesia ke bahasa Inggris
119
+ hari_map = {
120
+ 'SENIN': 'Monday',
121
+ 'SELASA': 'Tuesday',
122
+ 'RABU': 'Wednesday',
123
+ 'KAMIS': 'Thursday',
124
+ 'JUMAT': 'Friday',
125
+ 'SABTU': 'Saturday',
126
+ 'MINGGU': 'Sunday'
127
+ }
128
+
129
+ # Konversi kolom HARI ke bahasa Inggris
130
+ df_selected_all['HARI_EN'] = df_selected_all['HARI'].map(hari_map)
131
+
132
+ # Buat pivot table untuk setiap hari
133
+ pivot_dfs = []
134
+ for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']:
135
+ sub_df = df_selected_all[df_selected_all['HARI_EN'] == day]
136
+ if not sub_df.empty:
137
+ pivot = sub_df.pivot_table(
138
+ values=['Total Anomali Simpanan', 'Total Anomali Pinjaman'],
139
+ index=['STAF', 'CENTER'],
140
+ aggfunc='sum'
141
+ ).reset_index()
142
+
143
+ pivot.columns = [f'{day}_{col}' if col not in ['STAF', 'CENTER'] else col for col in pivot.columns]
144
+ pivot_dfs.append(pivot)
145
+
146
+ # Gabungkan semua pivot table
147
+ if pivot_dfs:
148
+ result = pivot_dfs[0]
149
+ for df in pivot_dfs[1:]:
150
+ result = result.merge(df, on=['STAF', 'CENTER'], how='outer')
151
+
152
+ # Urutkan kolom
153
+ column_order = ['STAF', 'CENTER']
154
+ day_map_rev = {'Monday': 'SENIN', 'Tuesday': 'SELASA', 'Wednesday': 'RABU', 'Thursday': 'KAMIS', 'Friday': 'JUMAT', 'Saturday': 'SABTU', 'Sunday': 'MINGGU'}
155
+
156
+ for day_en, day_id in day_map_rev.items():
157
+ s_col = f'{day_en}_Total Anomali Simpanan'
158
+ p_col = f'{day_en}_Total Anomali Pinjaman'
159
+ if s_col in result.columns: column_order.append(s_col)
160
+ if p_col in result.columns: column_order.append(p_col)
161
+
162
+ result = result.reindex(columns=column_order)
163
+
164
+ # Ganti nama kolom kembali ke bahasa Indonesia
165
+ new_columns = []
166
+ for col in result.columns:
167
+ temp_col = col
168
+ for day_en, day_id in day_map_rev.items():
169
+ if day_en in temp_col:
170
+ temp_col = temp_col.replace(day_en, day_id)
171
+ new_columns.append(temp_col.replace('_', ' '))
172
+
173
+ result.columns = new_columns
174
+ result = result.fillna(0)
175
+ else:
176
+ result = pd.DataFrame(columns=['STAF', 'CENTER'])
177
+
178
+ return df_selected_all, result
179
+
180
+ except Exception as e:
181
+ st.error(f"Terjadi kesalahan saat memproses data: {str(e)}")
182
+ return None, None
183
+
184
+ def main():
185
+ uploaded_files = st.file_uploader("Unggah file Excel", accept_multiple_files=True, type=["xlsx"])
186
+
187
+ if uploaded_files:
188
+ dfs = load_data(uploaded_files)
189
+
190
+ required_files = [
191
+ 'Anomali Pinjaman.xlsx_Anomali PU',
192
+ 'Anomali Pinjaman.xlsx_Anomali PMB',
193
+ 'Anomali Pinjaman.xlsx_Anomali DTP',
194
+ 'Anomali Pinjaman.xlsx_Anomali PSA',
195
+ 'Anomali Pinjaman.xlsx_Anomali ARTA',
196
+ 'Anomali Pinjaman.xlsx_Anomali PRR',
197
+ 'Anomali Pinjaman.xlsx_Anomali PTN',
198
+ 'Anomali Simpanan.xlsx_Sihara',
199
+ 'Anomali Simpanan.xlsx_Pensiun',
200
+ 'Anomali Simpanan.xlsx_Sukarela',
201
+ 'DbSimpanan.xlsx_IA_SimpananDB'
202
+ ]
203
+
204
+ missing_files = [file for file in required_files if file not in dfs]
205
+
206
+ if not missing_files:
207
+ with st.spinner("Sedang memproses data..."):
208
+ df_selected_all, result = process_data(dfs)
209
+
210
+ if df_selected_all is not None and result is not None:
211
+ st.success("Proses selesai!")
212
+
213
+ tabs = st.tabs(["Data Anomali", "Pivot Analysis"])
214
+ with tabs[0]:
215
+ st.write("Data Anomali Gabungan:")
216
+ st.dataframe(df_selected_all)
217
+
218
+ with tabs[1]:
219
+ st.write("Analisis Pivot per Hari:")
220
+ st.dataframe(result)
221
+
222
+ st.divider()
223
+ # Downloads
224
+ col1, col2 = st.columns(2)
225
+ with col1:
226
+ buffer1 = io.BytesIO()
227
+ with pd.ExcelWriter(buffer1, engine='xlsxwriter') as writer:
228
+ df_selected_all.to_excel(writer, index=False, sheet_name='All Anomaly')
229
+ st.download_button("Unduh Data Anomali.xlsx", buffer1.getvalue(), "Data_Anomali.xlsx")
230
+
231
+ with col2:
232
+ buffer2 = io.BytesIO()
233
+ with pd.ExcelWriter(buffer2, engine='xlsxwriter') as writer:
234
+ result.to_excel(writer, index=False, sheet_name='Pivot')
235
+ st.download_button("Unduh Data Anomali Pivot.xlsx", buffer2.getvalue(), "Data_Anomali_Pivot.xlsx")
236
+ else:
237
+ st.warning(f"File/Sheet berikut belum lengkap: {', '.join(missing_files)}")
238
+
239
+ if __name__ == "__main__":
240
+ main()
docker-compose.yml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ all-anomaly:
3
+ build: .
4
+ container_name: streamlit-all-anomaly
5
+ ports:
6
+ - "7863:7860"
7
+ restart: always
8
+ deploy:
9
+ resources:
10
+ limits:
11
+ memory: 2G
12
+ environment:
13
+ - STREAMLIT_SERVER_MAX_UPLOAD_SIZE=500
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ pandas>=2.1.0
3
+ numpy
4
+ matplotlib
5
+ openpyxl
6
+ xlsxwriter
7
+ pyarrow>=12.0.0