Spaces:
Running
Running
File size: 10,280 Bytes
cd93036 34a8bca a47f2f1 34a8bca cd93036 a47f2f1 34a8bca a47f2f1 34a8bca a47f2f1 34a8bca a47f2f1 34a8bca a47f2f1 34a8bca a47f2f1 | 1 2 3 4 5 6 7 8 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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | # (c) Timeless Wind Publishing // Proprietary Information
import streamlit as st
import tempfile
from pathlib import Path
import hashlib
import pandas as pd
import io
from accounting_program import generate_report
from openpyxl.styles import Font, PatternFill
import os
# Password protection
def check_password():
"""Returns `True` if the user has entered the correct password."""
def password_entered():
"""Checks whether a password entered by the user is correct."""
# Hash the password for security
entered_password = st.session_state["password"]
# Change this to your desired password (hashed)
# To generate hash: hashlib.sha256("your_password".encode()).hexdigest()
correct_password_hash = os.environ.get("PASSWORD_HASH")
if hashlib.sha256(entered_password.encode()).hexdigest() == correct_password_hash:
st.session_state["password_correct"] = True
del st.session_state["password"] # Don't store password
else:
st.session_state["password_correct"] = False
# First run or password not correct
if "password_correct" not in st.session_state:
st.text_input(
"π Enter Password",
type="password",
on_change=password_entered,
key="password"
)
st.info("Please enter the password to access the application.")
return False
elif not st.session_state["password_correct"]:
st.text_input(
"π Enter Password",
type="password",
on_change=password_entered,
key="password"
)
st.error("π Password incorrect")
return False
else:
return True
def create_download_dataframe(final_royalties, include_total=True):
"""Create a formatted dataframe for download"""
grouped = final_royalties.groupby(['Author', 'Title'], as_index=False)['Adjusted_Earnings'].sum()
# Create download dataframe
download_df = grouped.copy()
download_df.columns = ['Author', 'Title', 'Earnings']
# Add a total row only if requested (for CSV)
if include_total:
total_earnings = final_royalties["Adjusted_Earnings"].sum()
total_row = pd.DataFrame({
'Author': ['TOTAL'],
'Title': [''],
'Earnings': [total_earnings]
})
download_df = pd.concat([download_df, total_row], ignore_index=True)
return download_df
def display_report(royalties_path, payments_path):
"""Display the accounting report in Streamlit format"""
final_royalties = generate_report(royalties_path, payments_path)
# Store in session state for download
st.session_state['report_data'] = final_royalties
grouped = final_royalties.groupby(['Author', 'Title'], as_index=False)['Adjusted_Earnings'].sum()
# Display total earnings
total_earnings = final_royalties["Adjusted_Earnings"].sum()
st.subheader("π Report Summary")
st.metric("Total Earnings", f"${total_earnings:,.2f}")
st.divider()
# Display earnings by author
st.subheader("π Earnings by Author")
for author, group in grouped.groupby('Author'):
author_total = group['Adjusted_Earnings'].sum()
with st.expander(f"**{author}** - ${author_total:,.2f}", expanded=True):
# Create a dataframe for this author's books
books_data = []
for _, row in group.iterrows():
books_data.append({
'Title': row['Title'],
'Earnings': f"${row['Adjusted_Earnings']:,.2f}"
})
# Display as a clean table
st.dataframe(
books_data,
use_container_width=True,
hide_index=True
)
st.caption(f"Total for {author}: ${author_total:,.2f}")
# Download section
st.divider()
st.subheader("π₯ Download Report")
col1, col2 = st.columns(2)
with col1:
# Excel download with pivot table
excel_buffer = io.BytesIO()
# Create dataframe without total row for Excel
download_df_excel = create_download_dataframe(final_royalties, include_total=False)
# Create a pivot table summary using pandas
pivot_df = download_df_excel.copy()
# Create author subtotals
author_totals = pivot_df.groupby('Author')['Earnings'].sum().reset_index()
author_totals['Title'] = 'SUBTOTAL'
# Sort and combine data with subtotals
pivot_display = []
for author in pivot_df['Author'].unique():
# Add books for this author
author_books = pivot_df[pivot_df['Author'] == author].copy()
for _, row in author_books.iterrows():
pivot_display.append({
'Author': row['Author'],
'Title': row['Title'],
'Earnings': row['Earnings']
})
# Add subtotal row
author_total = author_totals[author_totals['Author'] == author]['Earnings'].values[0]
pivot_display.append({
'Author': author,
'Title': ' β Subtotal',
'Earnings': author_total
})
# Add grand total
grand_total = pivot_df['Earnings'].sum()
pivot_display.append({
'Author': 'GRAND TOTAL',
'Title': '',
'Earnings': grand_total
})
pivot_summary = pd.DataFrame(pivot_display)
with pd.ExcelWriter(excel_buffer, engine='openpyxl') as writer:
# Write main data sheet
download_df_excel.to_excel(writer, index=False, sheet_name='Earnings Data')
# Write pivot table summary
pivot_summary.to_excel(writer, index=False, sheet_name='Pivot Summary')
# Format the pivot summary sheet
workbook = writer.book
pivot_sheet = workbook['Pivot Summary']
# Bold the header row
for cell in pivot_sheet[1]:
cell.font = cell.font.copy(bold=True)
# Bold subtotal and grand total rows
for row in pivot_sheet.iter_rows(min_row=2):
if row[1].value and ('Subtotal' in str(row[1].value) or row[0].value == 'GRAND TOTAL'):
for cell in row:
cell.font = Font(bold=True)
if row[0].value == 'GRAND TOTAL':
cell.fill = PatternFill(start_color='E0E0E0', end_color='E0E0E0', fill_type='solid')
excel_buffer.seek(0)
st.download_button(
label="π Download as Excel",
data=excel_buffer,
file_name="twp_earnings_report.xlsx",
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
use_container_width=True
)
with col2:
# CSV download with total row
download_df_csv = create_download_dataframe(final_royalties, include_total=True)
csv_buffer = io.StringIO()
download_df_csv.to_csv(csv_buffer, index=False)
st.download_button(
label="π Download as CSV",
data=csv_buffer.getvalue(),
file_name="twp_earnings_report.csv",
mime="text/csv",
use_container_width=True
)
def main():
# Check password first
if not check_password():
st.stop() # Don't continue if password is wrong
st.title("TWP Accounting Report Generator")
st.write("Upload your royalties and payouts files to generate a report")
# Create two columns for file uploaders
col1, col2 = st.columns(2)
with col1:
royalties_file = st.file_uploader(
"Upload Royalties File",
type=['csv', 'xlsx', 'xls'],
help="Select your royalties data file"
)
with col2:
payouts_file = st.file_uploader(
"Upload Payouts File",
type=['csv', 'xlsx', 'xls'],
help="Select your payouts data file"
)
# Process button
if st.button("Generate Report", type="primary", disabled=not (royalties_file and payouts_file)):
if royalties_file and payouts_file:
with st.spinner("Processing files..."):
# Create temporary files to save uploads
with tempfile.TemporaryDirectory() as tmp_dir:
# Save uploaded files to temp directory
royalties_path = Path(tmp_dir) / royalties_file.name
payouts_path = Path(tmp_dir) / payouts_file.name
with open(royalties_path, 'wb') as f:
f.write(royalties_file.getbuffer())
with open(payouts_path, 'wb') as f:
f.write(payouts_file.getbuffer())
# Display processing info
st.info(f"Processing: {royalties_file.name} and {payouts_file.name}")
# Generate and display the report
try:
display_report(str(royalties_path), str(payouts_path))
st.success("Report generated successfully!")
except Exception as e:
st.error(f"Error generating report: {str(e)}")
st.exception(e)
# Instructions
with st.expander("βΉοΈ Instructions"):
st.markdown("""
1. Upload your **Royalties File** using the left file uploader
2. Upload your **Payouts File** using the right file uploader
3. Click **Generate Report** to process the files
4. View the results below
5. Download the report as Excel or CSV using the download buttons
**Supported file formats:** CSV, Excel (.xlsx, .xls)
""")
if __name__ == "__main__":
main()
|