Spaces:
Running
Running
| import json | |
| import os | |
| import re | |
| # ========================================== | |
| # CONFIGURATION | |
| # ========================================== | |
| # Point this to your new flat JSON generated by split_syllabus.py | |
| INPUT_FILE = 'syllabus_flat.json' | |
| OUTPUT_DIR = 'syllabus_out' # Rename to 'syllabus' before pushing to GitHub | |
| # Load JSON Data | |
| with open(INPUT_FILE, 'r', encoding='utf-8') as f: | |
| flat_data = json.load(f) | |
| os.makedirs(OUTPUT_DIR, exist_ok=True) | |
| # ========================================== | |
| # RESTRUCTURE FLAT JSON TO GROUPED DICTIONARY | |
| # ========================================== | |
| # Converts flat array into: { "Branch": { "S1": [subjects], "S2": [subjects] } } | |
| data = {} | |
| for item in flat_data: | |
| branch = item['branch'] | |
| sem = item['semester'] | |
| if branch not in data: | |
| data[branch] = {} | |
| if sem not in data[branch]: | |
| data[branch][sem] = [] | |
| data[branch][sem].append(item) | |
| # ========================================== | |
| # HELPER FUNCTIONS & SORTING | |
| # ========================================== | |
| def slugify(text): | |
| text = text.lower() | |
| text = re.sub(r'[^a-z0-9]+', '-', text) | |
| return text.strip('-') | |
| def clean_name(name): | |
| return re.sub(r'\s*-\s*\(.*?\)', '', name).strip() | |
| def get_branch_acronym(branch_name): | |
| b = branch_name.upper() | |
| if 'COMPUTER' in b: return 'CSE' | |
| if 'MECHANICAL' in b: return 'ME' | |
| if 'CIVIL' in b: return 'CE' | |
| if 'ELECTRICAL AND ELECTRONICS' in b: return 'EEE' | |
| if 'ELECTRONICS AND COMMUNICATION' in b: return 'ECE' | |
| if 'INFORMATION TECHNOLOGY' in b: return 'IT' | |
| return "".join([word[0] for word in branch_name.split() if word.isalpha()]) | |
| def get_branch_priority(branch_name): | |
| b = branch_name.upper() | |
| if 'COMPUTER' in b: return 1 # CSE First | |
| if 'CIVIL' in b: return 2 # Civil Second | |
| if 'MECHANICAL' in b: return 3 | |
| if 'ELECTRICAL AND ELECTRONICS' in b: return 4 | |
| if 'ELECTRONICS AND COMMUNICATION' in b: return 5 | |
| return 99 | |
| def get_branch_theme(branch_name): | |
| b = branch_name.upper() | |
| if 'COMPUTER' in b: return ('indigo', 'fa-laptop-code') | |
| if 'CIVIL' in b: return ('emerald', 'fa-building') | |
| if 'MECHANICAL' in b: return ('orange', 'fa-cogs') | |
| if 'ELECTRICAL AND ELECTRONICS' in b: return ('amber', 'fa-bolt') | |
| if 'ELECTRONICS AND COMMUNICATION' in b: return ('purple', 'fa-microchip') | |
| return ('blue', 'fa-graduation-cap') | |
| # ========================================== | |
| # HTML TEMPLATES | |
| # ========================================== | |
| def get_header(title, desc): | |
| return f"""<!DOCTYPE html> | |
| <html lang="en" class="scroll-smooth"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>{title}</title> | |
| <meta name="description" content="{desc}"> | |
| <meta name="robots" content="index, follow"> | |
| <script src="/tailwind-local.js"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> | |
| <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@700;900&family=Plus+Jakarta+Sans:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"> | |
| <style> | |
| body {{ font-family: 'Plus Jakarta Sans', sans-serif; }} | |
| .font-sporty {{ font-family: 'Orbitron', sans-serif; letter-spacing: 0.05em; }} | |
| .bg-mesh {{ | |
| background-color: #f8fafc; | |
| background-image: radial-gradient(at 40% 20%, hsla(228,100%,74%,0.15) 0px, transparent 50%), | |
| radial-gradient(at 80% 0%, hsla(189,100%,56%,0.15) 0px, transparent 50%), | |
| radial-gradient(at 0% 50%, hsla(228,100%,74%,0.15) 0px, transparent 50%); | |
| }} | |
| .no-scrollbar::-webkit-scrollbar {{ display: none; }} | |
| .no-scrollbar {{ -ms-overflow-style: none; scrollbar-width: none; }} | |
| /* Modern UI Hover Transitions */ | |
| .hub-card {{ transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1); }} | |
| .hub-card:hover {{ transform: translateY(-6px); }} | |
| @keyframes fadeUpEntry {{ 0% {{ opacity: 0; transform: translateY(20px); }} 100% {{ opacity: 1; transform: translateY(0); }} }} | |
| .animate-fade-up {{ opacity: 0; animation: fadeUpEntry 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; }} | |
| .delay-100 {{ animation-delay: 100ms; }} | |
| .delay-200 {{ animation-delay: 200ms; }} | |
| </style> | |
| <link rel="icon" href="/favicon.png" type="image/png"> | |
| <meta property="og:site_name" content="Kerala Timetable"> | |
| <meta property="og:image" content="https://www.keralatimetable.in/social-preview.png"> | |
| <meta property="og:type" content="website"> | |
| </head> | |
| <body class="bg-mesh text-slate-800 antialiased flex flex-col min-h-screen relative overflow-x-hidden"> | |
| <div id="navigation-container" class="min-h-[73px] w-full"></div> | |
| <main class="flex-grow w-full pb-20"> | |
| """ | |
| def get_footer(): | |
| return f""" | |
| </main> | |
| <script src="/components.js?v=12"></script> | |
| <script>if(typeof loadNavigation === 'function') loadNavigation('', '', false);</script> | |
| </body> | |
| </html> | |
| """ | |
| # ========================================== | |
| # 1. GENERATE MAIN HUB (index.html) | |
| # ========================================== | |
| sorted_branches = sorted(data.keys(), key=lambda x: (get_branch_priority(x), x)) | |
| main_html = get_header("KTU B.Tech Syllabus 2024 Scheme | All Branches", "Download official KTU B.Tech syllabus PDFs for the 2024 scheme.") | |
| main_html += """ | |
| <div class="w-full max-w-6xl mx-auto px-4 pt-12 pb-8 text-center relative z-20"> | |
| <div class="animate-fade-up inline-flex items-center gap-2 px-4 py-2 rounded-full bg-slate-800 border border-slate-700 text-white text-xs font-bold uppercase tracking-wider mb-6 shadow-sm"> | |
| <i class="fas fa-layer-group text-cyan-400"></i> Official Curriculum | |
| </div> | |
| <h1 class="animate-fade-up delay-100 text-5xl md:text-6xl font-black text-slate-900 tracking-tight leading-tight mb-4"> | |
| B.Tech Syllabus <span class="text-transparent bg-clip-text bg-gradient-to-r from-indigo-500 to-emerald-500">2024</span> | |
| </h1> | |
| <p class="animate-fade-up delay-200 text-slate-500 font-medium md:text-lg max-w-2xl mx-auto mb-10"> | |
| Select your engineering branch below to access beautifully organized, semester-wise syllabus PDFs. | |
| </p> | |
| </div> | |
| <div class="w-full max-w-6xl mx-auto px-4 relative z-20 animate-fade-up delay-200"> | |
| <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4 md:gap-5"> | |
| """ | |
| for branch in sorted_branches: | |
| branch_slug = slugify(branch) | |
| branch_acronym = get_branch_acronym(branch) | |
| color, icon = get_branch_theme(branch) | |
| main_html += f""" | |
| <a href="{branch_slug}/index.html" class="hub-card relative bg-white/80 backdrop-blur-xl border border-slate-200 rounded-3xl p-5 flex flex-col items-center justify-center text-center group overflow-hidden hover:shadow-[0_15px_30px_-10px_rgba(0,0,0,0.1)] hover:border-{color}-300"> | |
| <div class="absolute -top-8 -right-8 w-24 h-24 bg-{color}-400/20 rounded-full blur-2xl group-hover:bg-{color}-500/30 transition-all duration-500"></div> | |
| <div class="absolute -bottom-8 -left-8 w-20 h-20 bg-slate-300/20 rounded-full blur-xl group-hover:bg-{color}-300/20 transition-all duration-500"></div> | |
| <div class="relative w-12 h-12 bg-{color}-50 text-{color}-600 rounded-2xl flex items-center justify-center text-xl mb-3 group-hover:scale-110 group-hover:bg-{color}-600 group-hover:text-white transition-all duration-300 shadow-sm border border-{color}-100 z-10"> | |
| <i class="fas {icon}"></i> | |
| </div> | |
| <span class="relative text-[9px] font-black bg-slate-800 text-white px-2 py-0.5 rounded mb-2 tracking-widest shadow-sm z-10">B.TECH</span> | |
| <h2 class="relative font-extrabold text-slate-800 text-base leading-snug group-hover:text-{color}-700 transition-colors z-10">{branch_acronym}</h2> | |
| <p class="relative text-[10px] text-slate-500 mt-1 font-semibold leading-tight line-clamp-2 z-10">{branch}</p> | |
| </a> | |
| """ | |
| main_html += "</div></div>" + get_footer() | |
| with open(os.path.join(OUTPUT_DIR, 'index.html'), 'w', encoding='utf-8') as f: | |
| f.write(main_html) | |
| # ========================================== | |
| # 2. GENERATE BRANCH & SEMESTER PAGES | |
| # ========================================== | |
| for branch in sorted_branches: | |
| all_sems = data[branch] | |
| branch_slug = slugify(branch) | |
| branch_dir = os.path.join(OUTPUT_DIR, branch_slug) | |
| os.makedirs(branch_dir, exist_ok=True) | |
| branch_acronym = get_branch_acronym(branch) | |
| color, icon = get_branch_theme(branch) | |
| def get_sem_num(sem_str): | |
| m = re.search(r'\d+', sem_str) | |
| return int(m.group()) if m else 99 | |
| sorted_sem_keys = sorted(all_sems.keys(), key=get_sem_num) | |
| branch_html = get_header(f"KTU B.Tech {branch_acronym} Syllabus 2024 Scheme", f"Download complete KTU B.Tech {branch_acronym} syllabus PDFs for the 2024 scheme. All semesters available.") | |
| branch_html += f""" | |
| <div class="w-full max-w-5xl mx-auto px-4 pt-10 pb-6 relative z-20 animate-fade-up"> | |
| <div class="flex items-center flex-wrap text-xs font-bold text-slate-400 uppercase tracking-wider mb-6 gap-2"> | |
| <a href="/syllabus/index.html" class="hover:text-{color}-600 transition-colors">Hub</a> | |
| <i class="fas fa-chevron-right text-[8px]"></i> | |
| <span class="text-slate-700 bg-slate-100 px-2 py-0.5 rounded">{branch_acronym}</span> | |
| </div> | |
| <div class="flex items-center gap-4 mb-4"> | |
| <div class="w-12 h-12 bg-{color}-100 text-{color}-600 rounded-xl flex items-center justify-center text-2xl shadow-sm border border-{color}-200"> | |
| <i class="fas {icon}"></i> | |
| </div> | |
| <div> | |
| <h1 class="text-3xl md:text-4xl font-black text-slate-900 leading-tight">KTU {branch_acronym} Syllabus</h1> | |
| <p class="text-slate-500 font-medium mt-1">B.Tech 2024 Scheme • {branch}</p> | |
| </div> | |
| </div> | |
| <div class="w-full h-px bg-slate-200 mt-8 mb-8"></div> | |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-5"> | |
| """ | |
| for semester in sorted_sem_keys: | |
| subjects_list = all_sems[semester] | |
| sem_slug = slugify(semester) | |
| short_sem = semester.replace('Semester ', 'S') | |
| sub_count = len(subjects_list) | |
| branch_html += f""" | |
| <a href="{sem_slug}.html" class="hub-card relative rounded-3xl p-6 overflow-hidden group border border-slate-200 bg-white/80 hover:border-transparent"> | |
| <div class="absolute inset-0 bg-gradient-to-br from-{color}-500 to-{color}-600 opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-0"></div> | |
| <div class="absolute top-2 right-4 text-7xl font-black text-slate-900 opacity-[0.15] group-hover:opacity-[0.25] group-hover:text-white transition-all duration-500 select-none z-0"> | |
| {short_sem} | |
| </div> | |
| <div class="relative z-10 flex flex-col h-full pt-8"> | |
| <h3 class="font-black text-4xl text-slate-800 group-hover:text-white transition-colors duration-300 mb-1">{short_sem}</h3> | |
| <p class="text-xs font-bold text-slate-400 group-hover:text-{color}-100 uppercase tracking-widest transition-colors duration-300"> | |
| {sub_count} Subjects | |
| </p> | |
| <div class="absolute bottom-0 right-0 w-8 h-8 bg-slate-100 group-hover:bg-white/20 rounded-full flex items-center justify-center text-slate-400 group-hover:text-white transition-all duration-500 -translate-x-4 opacity-0 group-hover:translate-x-0 group-hover:opacity-100"> | |
| <i class="fas fa-arrow-right text-xs"></i> | |
| </div> | |
| </div> | |
| </a> | |
| """ | |
| sem_page_title = f"KTU B.Tech {short_sem} {branch_acronym} Syllabus 2024 Scheme" | |
| sem_page_desc = f"Download individual subject syllabus PDFs for KTU B.Tech {short_sem} {branch_acronym} (2024 Scheme)." | |
| sem_html = get_header(sem_page_title, sem_page_desc) | |
| sem_html += f""" | |
| <div class="w-full max-w-6xl mx-auto px-4 pt-10 pb-6 relative z-20 animate-fade-up"> | |
| <div class="flex items-center flex-wrap text-xs font-bold text-slate-400 uppercase tracking-wider mb-8 gap-2"> | |
| <a href="/syllabus/index.html" class="hover:text-{color}-600 transition-colors">Hub</a> | |
| <i class="fas fa-chevron-right text-[8px]"></i> | |
| <a href="index.html" class="hover:text-{color}-600 transition-colors bg-slate-100 hover:bg-{color}-50 px-2 py-0.5 rounded transition-all">{branch_acronym}</a> | |
| <i class="fas fa-chevron-right text-[8px]"></i> | |
| <span class="text-slate-700 bg-slate-200 px-2 py-0.5 rounded">{short_sem}</span> | |
| </div> | |
| <div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8 bg-white/60 backdrop-blur-xl border border-white shadow-[0_10px_30px_rgba(0,0,0,0.03)] p-6 rounded-3xl"> | |
| <div class="flex items-center gap-4"> | |
| <div class="w-14 h-14 bg-gradient-to-br from-{color}-500 to-{color}-600 text-white rounded-2xl flex items-center justify-center text-2xl shadow-lg shadow-{color}-500/30"> | |
| <span class="font-sporty font-black">{short_sem}</span> | |
| </div> | |
| <div> | |
| <h1 class="text-2xl md:text-3xl font-black text-slate-900 leading-tight">{branch_acronym} Syllabus</h1> | |
| <p class="text-slate-500 font-medium text-sm mt-0.5">2024 Scheme • {sub_count} Subjects Available</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5"> | |
| """ | |
| for subject in subjects_list: | |
| # Re-mapped to the new JSON keys | |
| sub_name = clean_name(subject.get('subject_name', 'Unknown Subject')) | |
| sub_code = subject.get('course_code', 'KTU') | |
| # Make sure we map the path correctly assuming your site structure | |
| sub_link = "/" + subject.get('pdf_file', '#') | |
| if sub_link == '/#': | |
| btn_classes = "w-full bg-slate-100 text-slate-400 cursor-not-allowed font-bold text-sm px-4 py-2.5 rounded-xl flex items-center justify-center gap-2 border border-slate-200" | |
| btn_text = "Link Unavailable" | |
| target = "" | |
| else: | |
| btn_classes = f"w-full bg-slate-50 hover:bg-{color}-600 text-slate-600 hover:text-white border border-slate-200 hover:border-{color}-600 font-bold text-sm px-4 py-2.5 rounded-xl flex items-center justify-center gap-2 transition-all shadow-sm hover:shadow-lg hover:shadow-{color}-500/20" | |
| btn_text = "Download PDF" | |
| target = 'target="_blank"' | |
| sem_html += f""" | |
| <div class="hub-card bg-white/90 backdrop-blur-md rounded-2xl border border-slate-200 shadow-sm flex flex-col p-5 group relative overflow-hidden"> | |
| <div class="absolute -bottom-4 -right-2 text-7xl font-black text-slate-900 opacity-[0.02] pointer-events-none select-none z-0 transition-transform group-hover:scale-110 group-hover:-rotate-3"> | |
| {short_sem} | |
| </div> | |
| <div class="relative z-10 flex-grow flex flex-col"> | |
| <div class="flex items-center flex-wrap gap-2 mb-4"> | |
| <span class="px-2.5 py-1 rounded-md text-[10px] font-black uppercase tracking-wider bg-slate-800 text-white shadow-sm">B.TECH</span> | |
| <span class="px-2.5 py-1 rounded-md text-[10px] font-black uppercase tracking-wider bg-{color}-100 text-{color}-700 border border-{color}-200">{short_sem}</span> | |
| <span class="px-2.5 py-1 rounded-md text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-600 border border-slate-200 font-sporty">{sub_code}</span> | |
| </div> | |
| <h3 class="text-base font-extrabold text-slate-800 leading-tight mb-6 pr-2 flex-grow">{sub_name}</h3> | |
| <a href="{sub_link}" {target} class="{btn_classes}"> | |
| <i class="fas fa-file-pdf"></i> {btn_text} | |
| </a> | |
| </div> | |
| </div> | |
| """ | |
| sem_html += "</div></div>" + get_footer() | |
| with open(os.path.join(branch_dir, f"{sem_slug}.html"), 'w', encoding='utf-8') as f: | |
| f.write(sem_html) | |
| branch_html += "</div></div>" + get_footer() | |
| with open(os.path.join(branch_dir, 'index.html'), 'w', encoding='utf-8') as f: | |
| f.write(branch_html) | |
| print("✅ SUCCESS! The compact, Auto-Sorted UI Syllabus site is generated in the 'syllabus_out' folder.") | |