Make a separate schedule of M25 04B in a table based format
M25 04B schedule 2026 2027
~/
python -c "import pdfplumber; p='/tmp/workspace/attachments/5a71c4a9-a00f-4a69-a823-fefa568472c2/+2 курс 3 сем 2026-2027 222.pdf'; pdf=pdfplumber.open(p); print('PAGES',len(pdf.pages)); [print('---PAGE',i+1,'---\n',page.extract_text(x_tolerance=1,y_tolerance=3)[:10000]) for i,page in enumerate(pdf.pages)]"
mkdir -p "/tmp/workspace/m25-04b-schedule/assets" && python -c "import pdfplumber; p='/tmp/workspace/attachments/5a71c4a9-a00f-4a69-a823-fefa568472c2/+2 курс 3 сем 2026-2027 222.pdf'; pdf=pdfplumber.open(p); [print(i+1,page.width,page.height) for i,page in enumerate(pdf.pages[2:4],start=3)]; [page.to_image(resolution=180).save(f'/tmp/workspace/m25-04b-schedule/assets/page{i}.png',format='PNG') for i,page in enumerate(pdf.pages[2:4],start=3)]"
~/m25-04b-schedule
python - <<'PY' import pdfplumber p='/tmp/workspace/attachments/5a71c4a9-a00f-4a69-a823-fefa568472c2/+2 курс 3 сем 2026-2027 222.pdf' with pdfplumber.open(p) as pdf: for n in [2,3]: page=pdf.pages[n] print('\nPAGE',n+1) for w in page.extract_words(x_tolerance=1,y_tolerance=2, keep_blank_chars=False): if 145 < w['x0'] < 235: print(round(w['top'],1),round(w['x0'],1),w['text']) PY
~/
python - <<'PY' import pdfplumber p='/tmp/workspace/attachments/5a71c4a9-a00f-4a69-a823-fefa568472c2/+2 курс 3 сем 2026-2027 222.pdf' with pdfplumber.open(p) as pdf: for n in [2,3]: page=pdf.pages[n] print('\nPAGE',n+1) for w in page.extract_words(x_tolerance=1,y_tolerance=2): if 240 < w['x0'] < 320: print(f"{w['top']:6.1f} {w['x0']:6.1f} {w['text']}") PY
python - <<'PY' import pdfplumber p='/tmp/workspace/attachments/5a71c4a9-a00f-4a69-a823-fefa568472c2/+2 курс 3 сем 2026-2027 222.pdf' with pdfplumber.open(p) as pdf: for n,lo,hi in [(2,335,360),(3,330,360)]: page=pdf.pages[n] print('\nPAGE',n+1) for w in page.extract_words(x_tolerance=1,y_tolerance=2): if lo < w['top'] < hi: print(f"x={w['x0']:.1f}-{w['x1']:.1f}, y={w['top']:.1f}: {w['text']}") PY
python - <<'PY' import pdfplumber p='/tmp/workspace/attachments/5a71c4a9-a00f-4a69-a823-fefa568472c2/+2 курс 3 сем 2026-2027 222.pdf' with pdfplumber.open(p) as pdf: page=pdf.pages[3] for lo,hi in [(25,150),(150,275),(275,390)]: print('\n---',lo,hi) words=[w for w in page.extract_words(x_tolerance=1,y_tolerance=2) if lo<=w['top']<hi] for w in sorted(words,key=lambda z:(round(z['top'],1),z['x0'])): print(f"{w['top']:6.1f} x{w['x0']:6.1f} {w['text']}") PY
python - <<'PY' import pdfplumber p='/tmp/workspace/attachments/5a71c4a9-a00f-4a69-a823-fefa568472c2/+2 курс 3 сем 2026-2027 222.pdf' with pdfplumber.open(p) as pdf: for n in [2,3]: pg=pdf.pages[n] print('\nPAGE',n+1) for lo,hi in ([(115,245),(245,365)] if n==2 else [(25,150),(150,275),(275,390)]): print('\nrange',lo,hi) words=[w for w in pg.extract_words(x_tolerance=1,y_tolerance=2) if lo<w['top']<hi and 235<=w['x0']<=278] for w in words: print(f"{w['top']:.1f} {w['x0']:.1f} {w['text']}") PY
python - <<'PY' import pdfplumber p='/tmp/workspace/attachments/5a71c4a9-a00f-4a69-a823-fefa568472c2/+2 курс 3 сем 2026-2027 222.pdf' with pdfplumber.open(p) as pdf: for pi in [2,3]: page=pdf.pages[pi] print('\nPAGE',pi+1) vs=sorted(set(round(e['x0'],1) for e in page.lines if abs(e['x0']-e['x1'])<.2)) hs=sorted(set(round(e['top'],1) for e in page.lines if abs(e['top']-e['bottom'])<.2)) print('V',vs) print('H',hs[:100]) print('tables',len(page.find_tables())) for t in page.find_tables(): print('bbox',t.bbox,'rows',len(t.rows),'cols',len(t.columns)) PY
python - <<'PY' import pdfplumber p='/tmp/workspace/attachments/5a71c4a9-a00f-4a69-a823-fefa568472c2/+2 курс 3 сем 2026-2027 222.pdf' with pdfplumber.open(p) as pdf: for pi in [2,3]: pg=pdf.pages[pi] print('\nPAGE',pi+1) t=pg.find_tables()[0] d=t.extract() for i,row in enumerate(d): print(i,repr(row)) PY
python - <<'PY' from openpyxl import Workbook, load_workbook from openpyxl.styles import Font, PatternFill, Alignment, Border, Side from openpyxl.worksheet.dimensions import ColumnDimension from openpyxl.formatting.rule import FormulaRule from pathlib import Path outdir = Path('/tmp/workspace/m25-04b-schedule/output') outdir.mkdir(parents=True, exist_ok=True) out = outdir/'M25-04B_Separate_Schedule_2026-2027.xlsx' wb = Workbook() ws = wb.active ws.title = 'M25-04B Schedule' ws.sheet_view.showGridLines = False ws.freeze_panes = 'A6' # Palette navy='17365D'; blue='1F4E78'; lightblue='D9EAF7'; pale='F3F7FB'; white='FFFFFF'; gold='F4B183'; green='E2F0D9'; grey='D9E1F2' thin = Side(style='thin', color='9EADBA') med = Side(style='medium', color=navy) ws.merge_cells('A1:C1') ws['A1']='CASPIAN UNIVERSITY' ws['A1'].font=Font(name='Arial', size=15, bold=True, color=white) ws['A1'].fill=PatternFill('solid', fgColor=navy) ws['A1'].alignment=Alignment(horizontal='center', vertical='center') ws.row_dimensions[1].height=27 ws.merge_cells('A2:C2') ws['A2']='M25-04B - Separate Class Schedule' ws['A2'].font=Font(name='Arial', size=14, bold=True, color=navy) ws['A2'].alignment=Alignment(horizontal='center', vertical='center') ws.row_dimensions[2].height=25 ws.merge_cells('A3:C3') ws['A3']='2nd Course, 3rd Semester | 2026-2027 Academic Year | EP 6B10124 Medicine' ws['A3'].font=Font(name='Arial', size=10, italic=True, color='404040') ws['A3'].alignment=Alignment(horizontal='center') ws.merge_cells('A4:C4') ws['A4']='All timings and rooms reproduced from the supplied official schedule.' ws['A4'].font=Font(name='Arial', size=9, color='666666') ws['A4'].alignment=Alignment(horizontal='center') # Headers for cell, value in zip(ws[5], ['Day', 'Time', 'Class / room']): cell.value=value cell.font=Font(name='Arial', size=11, bold=True, color=white) cell.fill=PatternFill('solid', fgColor=blue) cell.alignment=Alignment(horizontal='center', vertical='center') cell.border=Border(left=med,right=med,top=med,bottom=med) ws.row_dimensions[5].height=24 rows = [ ('Monday','14:00-15:50','Microbiology | Room 514'), ('Monday','16:00-17:50','Pre-hospital medical care | Room 505'), ('Tuesday','10:00-11:50','Immunology | Room 520 | 05 Oct-12 Dec'), ('Wednesday','08:00-09:50','Lecture: General hygiene and epidemiology | Dostyk Ave. 85A, Room 305'), ('Wednesday','10:00-11:50','Histology-1 | Room 519'), ('Thursday','10:00-11:50','Anatomy-1 | Room 501'), ('Thursday','12:00-13:50','Foreign language (professional) | Room 503'), ('Friday','12:00-13:50','Biochemistry-1 | Room 513'), ('Saturday','10:00-11:50','Physiology-1 | Room 503'), ] start=6 for r, (day,time,cls) in enumerate(rows,start): ws.cell(r,1,day) ws.cell(r,2,time) ws.cell(r,3,cls) for c in range(1,4): cell=ws.cell(r,c) cell.font=Font(name='Arial', size=10, bold=(c==1)) cell.alignment=Alignment(vertical='center', horizontal='center' if c<3 else 'left', wrap_text=True) cell.border=Border(left=thin,right=thin,top=thin,bottom=thin) cell.fill=PatternFill('solid', fgColor=lightblue if r%2==0 else pale) ws.row_dimensions[r].height=32 if r != 9 else 42 # Merge day cells only where Monday and Wednesday span consecutive entries ws.merge_cells('A6:A7') ws['A6'].alignment=Alignment(horizontal='center',vertical='center') ws.merge_cells('A9:A10') ws['A9'].alignment=Alignment(horizontal='center',vertical='center') # Restore borders across merged region visually for r in [6,7,9,10]: ws.cell(r,1).border=Border(left=thin,right=thin,top=thin,bottom=thin) # Note note_row = start+len(rows)+2 ws.merge_cells(start_row=note_row,start_column=1,end_row=note_row,end_column=3) ws.cell(note_row,1).value='Immunology runs from 05 October to 12 December. No other classes are listed for M25-04B in the supplied timetable.' ws.cell(note_row,1).font=Font(name='Arial',size=9,italic=True,color='5B5B5B') ws.cell(note_row,1).fill=PatternFill('solid',fgColor=green) ws.cell(note_row,1).alignment=Alignment(wrap_text=True,vertical='center') ws.cell(note_row,1).border=Border(left=thin,right=thin,top=thin,bottom=thin) ws.row_dimensions[note_row].height=32 ws.column_dimensions['A'].width=16 ws.column_dimensions['B'].width=17 ws.column_dimensions['C'].width=76 ws.sheet_properties.pageSetUpPr.fitToPage=True ws.page_setup.orientation='landscape' ws.page_setup.fitToWidth=1 ws.page_setup.fitToHeight=1 ws.print_title_rows='1:5' ws.sheet_properties.outlinePr.summaryBelow=True # Footer headers ws.oddFooter.center.text='M25-04B Schedule | Page &P of &N' ws.oddFooter.center.size=8 wb.save(out) # Validation check=load_workbook(out, data_only=False) sh=check['M25-04B Schedule'] assert sh['A2'].value == 'M25-04B - Separate Class Schedule' assert sh['C8'].value == 'Immunology | Room 520 | 05 Oct-12 Dec' assert sh.max_row == note_row print(out) print('rows:', sh.max_row, 'merged:', list(sh.merged_cells.ranges)) PY ls -lh "/tmp/workspace/m25-04b-schedule/output/M25-04B_Separate_Schedule_2026-2027.xlsx"
| Day | Time | Class / Room |
|---|---|---|
| Monday | 14:00-15:50 | Microbiology, Room 514 |
| Monday | 16:00-17:50 | Pre-hospital medical care, Room 505 |
| Tuesday | 10:00-11:50 | Immunology, Room 520, from 05 October to 12 December |
| Wednesday | 08:00-09:50 | Lecture: General hygiene and epidemiology, Dostyk Ave. 85A, Room 305 |
| Wednesday | 10:00-11:50 | Histology-1, Room 519 |
| Thursday | 10:00-11:50 | Anatomy-1, Room 501 |
| Thursday | 12:00-13:50 | Foreign language (professional), Room 503 |
| Friday | 12:00-13:50 | Biochemistry-1, Room 513 |
| Saturday | 10:00-11:50 | Physiology-1, Room 503 |
M25 04B Separate Schedule 2026 2027
Spreadsheet · XLSX