Ethscriptions commited on
Commit
90c1aa2
·
verified ·
1 Parent(s): bc2e078

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -67
app.py CHANGED
@@ -7,12 +7,11 @@ import base64
7
  import os
8
  from datetime import datetime, timedelta
9
  import math
10
- from matplotlib.patches import FancyBboxPatch
11
  from pypinyin import lazy_pinyin, Style
12
  from matplotlib.backends.backend_pdf import PdfPages
13
 
14
  def get_font(size=14):
15
- """Fetches the font properties for the SimHei font."""
16
  font_path = "simHei.ttc"
17
  if not os.path.exists(font_path):
18
  font_path = "SimHei.ttf"
@@ -33,12 +32,14 @@ def get_pinyin_abbr(text):
33
  return ''.join(pinyin_list).upper()
34
 
35
  def process_schedule(file):
36
- """Processes the uploaded schedule file to extract and format movie showtimes."""
37
  try:
 
38
  date_df = pd.read_excel(file, header=None, skiprows=7, nrows=1, usecols=[3])
39
  date_str = pd.to_datetime(date_df.iloc[0, 0]).strftime('%Y-%m-%d')
40
  base_date = pd.to_datetime(date_str).date()
41
  except:
 
42
  date_str = datetime.today().strftime('%Y-%m-%d')
43
  base_date = datetime.today().date()
44
 
@@ -48,15 +49,19 @@ def process_schedule(file):
48
  df['Hall'] = df['Hall'].ffill()
49
  df.dropna(subset=['StartTime', 'EndTime', 'Movie'], inplace=True)
50
  df['Hall'] = df['Hall'].astype(str).str.extract(r'(\d+号)')
 
 
51
  df['StartTime_dt'] = pd.to_datetime(df['StartTime'], format='%H:%M', errors='coerce').apply(
52
  lambda t: t.replace(year=base_date.year, month=base_date.month, day=base_date.day) if pd.notnull(t) else t
53
  )
54
  df['EndTime_dt'] = pd.to_datetime(df['EndTime'], format='%H:%M', errors='coerce').apply(
55
  lambda t: t.replace(year=base_date.year, month=base_date.month, day=base_date.day) if pd.notnull(t) else t
56
  )
 
57
  df.loc[df['EndTime_dt'] < df['StartTime_dt'], 'EndTime_dt'] += timedelta(days=1)
58
  df = df.sort_values(['Hall', 'StartTime_dt'])
59
 
 
60
  merged_rows = []
61
  for hall, group in df.groupby('Hall'):
62
  group = group.sort_values('StartTime_dt')
@@ -75,7 +80,7 @@ def process_schedule(file):
75
 
76
  merged_df = pd.DataFrame(merged_rows)
77
 
78
- # Adjust start time 10 minutes earlier, end time 5 minutes earlier
79
  merged_df['StartTime_dt'] = merged_df['StartTime_dt'] - timedelta(minutes=10)
80
  merged_df['EndTime_dt'] = merged_df['EndTime_dt'] - timedelta(minutes=5)
81
 
@@ -83,31 +88,31 @@ def process_schedule(file):
83
  merged_df['EndTime_str'] = merged_df['EndTime_dt'].dt.strftime('%H:%M')
84
 
85
  return merged_df[['Hall', 'Movie', 'StartTime_str', 'EndTime_str']], date_str
86
- except:
 
87
  return None, date_str
88
 
89
  def create_print_layout(data, date_str):
90
- """Creates the PNG and PDF print layouts for the schedule."""
91
  if data is None or data.empty:
92
  return None
93
 
94
- # Create PNG image - A4 size, maximized content
95
  png_fig = plt.figure(figsize=(8.27, 11.69), dpi=300)
96
  png_ax = png_fig.add_subplot(111)
97
  png_ax.set_axis_off()
98
  png_fig.subplots_adjust(left=0.02, right=0.98, top=0.98, bottom=0.02)
99
 
100
- # Create PDF image - A4 size
101
  pdf_fig = plt.figure(figsize=(8.27, 11.69), dpi=300)
102
  pdf_ax = pdf_fig.add_subplot(111)
103
  pdf_ax.set_axis_off()
104
  pdf_fig.subplots_adjust(left=0.02, right=0.98, top=0.98, bottom=0.02)
105
 
106
- # Common function to process both figures
107
- def process_figure(fig, ax, is_pdf=False):
108
- # Pre-load fonts
109
  date_font = get_font(12)
110
- font_size_multiplier = 1.2
 
111
  movie_font_size = 14 * font_size_multiplier
112
  hall_font_size = movie_font_size * 0.8
113
  hall_font = get_font(hall_font_size)
@@ -118,69 +123,66 @@ def create_print_layout(data, date_str):
118
 
119
  halls = sorted(data['Hall'].unique(), key=lambda h: int(h.replace('号','')) if h else 0)
120
 
121
- total_lines = sum(len(data[data['Hall'] == hall]) for hall in halls) + (len(halls) - 1)
122
- available_height = 0.98 - 0.02
123
- line_spacing = available_height / total_lines if total_lines > 0 else 0.04
124
- y_position = 0.98
 
 
 
 
 
125
 
126
- for hall in halls:
 
127
  hall_data = data[data['Hall'] == hall]
128
- y_block_top = y_position
129
  hall_num = hall.replace("号", "")
130
- hall_text_template = f"${hall_num}^{{\\#}}$"
131
- movie_count = 1
132
 
133
- for _, row in hall_data.iterrows():
 
 
 
 
 
 
 
134
  pinyin_abbr = get_pinyin_abbr(row['Movie'])
135
- hall_text = hall_text_template if movie_count == 1 else ""
136
-
137
- # Draw a line of dots underneath the text
138
- dots = '·' * 300 # A large number of dots
139
- ax.text(0.03, y_position, dots,
140
- fontsize=movie_font_size, color='gray',
141
- ha='left', va='top', fontproperties=movie_font,
142
- transform=ax.transAxes, zorder=1.5, clip_on=True)
143
-
144
- # Draw Hall, Movie, and Time text with a white background to create the leader effect
145
- ax.text(0.03, y_position, hall_text,
146
- fontsize=hall_font_size, fontweight='bold',
147
- ha='left', va='top', fontproperties=hall_font,
148
- transform=ax.transAxes, zorder=2,
149
- bbox=dict(boxstyle="square,pad=0.1", fc="white", ec="none"))
150
 
151
- ax.text(0.20, y_position, f"{movie_count}. {pinyin_abbr} {row['Movie']}",
 
152
  fontsize=movie_font_size, ha='left', va='top', fontproperties=movie_font,
153
  transform=ax.transAxes, zorder=2, clip_on=True,
154
- bbox=dict(boxstyle="square,pad=0.1", fc="white", ec="none"))
155
 
 
156
  ax.text(0.95, y_position, f"{row['StartTime_str']} - {row['EndTime_str']}",
157
  fontsize=movie_font_size, ha='right', va='top', fontproperties=movie_font,
158
- transform=ax.transAxes, zorder=2,
159
- bbox=dict(boxstyle="square,pad=0.1", fc="white", ec="none"))
160
 
161
- y_position -= line_spacing
162
- movie_count += 1
163
 
164
- y_block_bottom = y_position
165
- y_position -= line_spacing
166
- rect = FancyBboxPatch((0.03, y_block_bottom), 0.94, y_block_top - y_block_bottom,
167
- boxstyle="round,pad=0.005,rounding_size=0.005",
168
- edgecolor='gray', facecolor='white',
169
- linewidth=0.8, zorder=1, transform=ax.transAxes)
170
- ax.add_patch(rect)
171
-
172
- # Process the figures
 
173
  process_figure(png_fig, png_ax)
174
- process_figure(pdf_fig, pdf_ax, is_pdf=True)
175
 
176
- # Save PNG image
177
  png_buffer = io.BytesIO()
178
  png_fig.savefig(png_buffer, format='png', bbox_inches='tight', pad_inches=0.05)
179
  png_buffer.seek(0)
180
  image_base64 = base64.b64encode(png_buffer.getvalue()).decode()
181
  plt.close(png_fig)
182
 
183
- # Save PDF file
184
  pdf_buffer = io.BytesIO()
185
  with PdfPages(pdf_buffer) as pdf:
186
  pdf.savefig(pdf_fig, bbox_inches='tight', pad_inches=0.05)
@@ -194,26 +196,24 @@ def create_print_layout(data, date_str):
194
  }
195
 
196
  def display_pdf(base64_pdf):
197
- """Generates the HTML to display a base64 encoded PDF in Streamlit."""
198
- pdf_display = f"""
199
- <iframe src="{base64_pdf}" width="100%" height="800" type="application/pdf"></iframe>
200
- """
201
  return pdf_display
202
 
203
- # --- Streamlit App ---
204
- st.set_page_config(page_title="LED 屏幕时间表打印", layout="wide")
205
- st.title("LED 屏幕时间表打印")
206
 
207
- uploaded_file = st.file_uploader("选择打开【放映时间核对表.xls】文件", accept_multiple_files=False, type=["xls"])
208
 
209
  if uploaded_file:
210
- with st.spinner("文件正在处理中,请稍候..."):
211
  schedule, date_str = process_schedule(uploaded_file)
212
- if schedule is not None:
213
  output = create_print_layout(schedule, date_str)
214
 
215
- # Create tabs for PDF and PNG views
216
- tab1, tab2 = st.tabs(["PDF 预览", "PNG 预览"])
217
 
218
  with tab1:
219
  st.markdown(display_pdf(output['pdf']), unsafe_allow_html=True)
@@ -221,4 +221,5 @@ if uploaded_file:
221
  with tab2:
222
  st.image(output['png'], use_container_width=True)
223
  else:
224
- st.error("无法处理文件,请检查文件格式或内容是否正确。")
 
 
7
  import os
8
  from datetime import datetime, timedelta
9
  import math
 
10
  from pypinyin import lazy_pinyin, Style
11
  from matplotlib.backends.backend_pdf import PdfPages
12
 
13
  def get_font(size=14):
14
+ """Finds an available SimHei font for Chinese character support."""
15
  font_path = "simHei.ttc"
16
  if not os.path.exists(font_path):
17
  font_path = "SimHei.ttf"
 
32
  return ''.join(pinyin_list).upper()
33
 
34
  def process_schedule(file):
35
+ """Processes the uploaded Excel file to extract and clean movie schedule data."""
36
  try:
37
+ # Try to read the date from the specified cell
38
  date_df = pd.read_excel(file, header=None, skiprows=7, nrows=1, usecols=[3])
39
  date_str = pd.to_datetime(date_df.iloc[0, 0]).strftime('%Y-%m-%d')
40
  base_date = pd.to_datetime(date_str).date()
41
  except:
42
+ # Fallback to the current date if reading fails
43
  date_str = datetime.today().strftime('%Y-%m-%d')
44
  base_date = datetime.today().date()
45
 
 
49
  df['Hall'] = df['Hall'].ffill()
50
  df.dropna(subset=['StartTime', 'EndTime', 'Movie'], inplace=True)
51
  df['Hall'] = df['Hall'].astype(str).str.extract(r'(\d+号)')
52
+
53
+ # Convert times to datetime objects
54
  df['StartTime_dt'] = pd.to_datetime(df['StartTime'], format='%H:%M', errors='coerce').apply(
55
  lambda t: t.replace(year=base_date.year, month=base_date.month, day=base_date.day) if pd.notnull(t) else t
56
  )
57
  df['EndTime_dt'] = pd.to_datetime(df['EndTime'], format='%H:%M', errors='coerce').apply(
58
  lambda t: t.replace(year=base_date.year, month=base_date.month, day=base_date.day) if pd.notnull(t) else t
59
  )
60
+ # Handle overnight screenings
61
  df.loc[df['EndTime_dt'] < df['StartTime_dt'], 'EndTime_dt'] += timedelta(days=1)
62
  df = df.sort_values(['Hall', 'StartTime_dt'])
63
 
64
+ # Merge consecutive screenings of the same movie
65
  merged_rows = []
66
  for hall, group in df.groupby('Hall'):
67
  group = group.sort_values('StartTime_dt')
 
80
 
81
  merged_df = pd.DataFrame(merged_rows)
82
 
83
+ # Adjust start and end times
84
  merged_df['StartTime_dt'] = merged_df['StartTime_dt'] - timedelta(minutes=10)
85
  merged_df['EndTime_dt'] = merged_df['EndTime_dt'] - timedelta(minutes=5)
86
 
 
88
  merged_df['EndTime_str'] = merged_df['EndTime_dt'].dt.strftime('%H:%M')
89
 
90
  return merged_df[['Hall', 'Movie', 'StartTime_str', 'EndTime_str']], date_str
91
+ except Exception as e:
92
+ st.error(f"An error occurred during data processing: {e}")
93
  return None, date_str
94
 
95
  def create_print_layout(data, date_str):
96
+ """Creates PNG and PDF layouts of the schedule, replacing boxes with separator lines."""
97
  if data is None or data.empty:
98
  return None
99
 
100
+ # --- Create figures for PNG and PDF ---
101
  png_fig = plt.figure(figsize=(8.27, 11.69), dpi=300)
102
  png_ax = png_fig.add_subplot(111)
103
  png_ax.set_axis_off()
104
  png_fig.subplots_adjust(left=0.02, right=0.98, top=0.98, bottom=0.02)
105
 
 
106
  pdf_fig = plt.figure(figsize=(8.27, 11.69), dpi=300)
107
  pdf_ax = pdf_fig.add_subplot(111)
108
  pdf_ax.set_axis_off()
109
  pdf_fig.subplots_adjust(left=0.02, right=0.98, top=0.98, bottom=0.02)
110
 
111
+ def process_figure(fig, ax):
112
+ # --- Font and Layout Setup ---
 
113
  date_font = get_font(12)
114
+ # Increased font size to better fill the page
115
+ font_size_multiplier = 1.3
116
  movie_font_size = 14 * font_size_multiplier
117
  hall_font_size = movie_font_size * 0.8
118
  hall_font = get_font(hall_font_size)
 
123
 
124
  halls = sorted(data['Hall'].unique(), key=lambda h: int(h.replace('号','')) if h else 0)
125
 
126
+ # --- Dynamic Layout Calculation ---
127
+ num_movie_lines = len(data)
128
+ num_gaps = len(halls) - 1
129
+ total_lines = num_movie_lines + num_gaps if num_gaps > 0 else num_movie_lines
130
+
131
+ available_height = 0.95 # Use 95% of vertical space
132
+ y_start = 0.98
133
+ line_height = available_height / total_lines if total_lines > 0 else 0.04
134
+ y_position = y_start
135
 
136
+ # --- Drawing Loop ---
137
+ for i, hall in enumerate(halls):
138
  hall_data = data[data['Hall'] == hall]
 
139
  hall_num = hall.replace("号", "")
140
+ hall_text = f"${hall_num}^{{\\#}}$"
 
141
 
142
+ for movie_idx, (_, row) in enumerate(hall_data.iterrows()):
143
+ # Print hall number (only for the first movie in the block)
144
+ if movie_idx == 0:
145
+ ax.text(0.03, y_position, hall_text,
146
+ fontsize=hall_font_size, fontweight='bold',
147
+ ha='left', va='top', fontproperties=hall_font,
148
+ transform=ax.transAxes, zorder=2)
149
+
150
  pinyin_abbr = get_pinyin_abbr(row['Movie'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
+ # Print movie name
153
+ ax.text(0.20, y_position, f"{movie_idx + 1}. {pinyin_abbr} {row['Movie']}",
154
  fontsize=movie_font_size, ha='left', va='top', fontproperties=movie_font,
155
  transform=ax.transAxes, zorder=2, clip_on=True,
156
+ bbox=dict(boxstyle="square,pad=0.0", fc="none", ec="none", alpha=0))
157
 
158
+ # Print time information
159
  ax.text(0.95, y_position, f"{row['StartTime_str']} - {row['EndTime_str']}",
160
  fontsize=movie_font_size, ha='right', va='top', fontproperties=movie_font,
161
+ transform=ax.transAxes, zorder=2)
 
162
 
163
+ y_position -= line_height
 
164
 
165
+ # After a hall's movies, add a black separator line
166
+ if i < len(halls) - 1:
167
+ # The line is drawn in the middle of the allocated gap space
168
+ line_y = y_position + (line_height / 2)
169
+ ax.plot([0.03, 0.97], [line_y, line_y], color='black', linewidth=1, transform=ax.transAxes, zorder=1)
170
+
171
+ # Move position down to account for the gap
172
+ y_position -= line_height
173
+
174
+ # --- Process and Save Figures ---
175
  process_figure(png_fig, png_ax)
176
+ process_figure(pdf_fig, pdf_ax)
177
 
178
+ # Save PNG to buffer
179
  png_buffer = io.BytesIO()
180
  png_fig.savefig(png_buffer, format='png', bbox_inches='tight', pad_inches=0.05)
181
  png_buffer.seek(0)
182
  image_base64 = base64.b64encode(png_buffer.getvalue()).decode()
183
  plt.close(png_fig)
184
 
185
+ # Save PDF to buffer
186
  pdf_buffer = io.BytesIO()
187
  with PdfPages(pdf_buffer) as pdf:
188
  pdf.savefig(pdf_fig, bbox_inches='tight', pad_inches=0.05)
 
196
  }
197
 
198
  def display_pdf(base64_pdf):
199
+ """Embeds the PDF in an iframe for display in Streamlit."""
200
+ pdf_display = f'<iframe src="{base64_pdf}" width="100%" height="800" type="application/pdf"></iframe>'
 
 
201
  return pdf_display
202
 
203
+ # --- Streamlit App UI ---
204
+ st.set_page_config(page_title="LED Screen Schedule Print", layout="wide")
205
+ st.title("LED Screen Schedule Print")
206
 
207
+ uploaded_file = st.file_uploader("Select 'Screening Time Checklist.xls' file", type=["xls"])
208
 
209
  if uploaded_file:
210
+ with st.spinner("Processing file, please wait..."):
211
  schedule, date_str = process_schedule(uploaded_file)
212
+ if schedule is not None and not schedule.empty:
213
  output = create_print_layout(schedule, date_str)
214
 
215
+ # Create tabs for PDF and PNG previews
216
+ tab1, tab2 = st.tabs(["PDF Preview", "PNG Preview"])
217
 
218
  with tab1:
219
  st.markdown(display_pdf(output['pdf']), unsafe_allow_html=True)
 
221
  with tab2:
222
  st.image(output['png'], use_container_width=True)
223
  else:
224
+ st.error("Could not process the file. Please check if the file format or content is correct.")
225
+