Skip to content

Commit bc033d7

Browse files
committed
merge local stuff
1 parent 9ad1e70 commit bc033d7

29 files changed

+245
-1115
lines changed

content/issues/2025-11-01 (43-1)/generate_newspaper.py

Lines changed: 134 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ def load_events(self):
6363
return yaml.safe_load(f)
6464
return {'events': []}
6565

66+
def load_horoscope(self):
67+
"""Load horoscope data"""
68+
horoscope_path = self.base_dir / 'horoscope.yaml'
69+
if horoscope_path.exists():
70+
with open(horoscope_path, 'r', encoding='utf-8') as f:
71+
return yaml.safe_load(f)
72+
return {'horoscope': []}
73+
6674
def markdown_to_latex(self, markdown_text, is_article=False):
6775
"""Convert markdown to LaTeX
6876
@@ -76,20 +84,20 @@ def markdown_to_latex(self, markdown_text, is_article=False):
7684
text = markdown_text
7785

7886
# Convert <br/> and <br> tags to line breaks - do this early before escaping
79-
text = re.sub(r'<br\s*/?>', r'§§§LINEBREAK§§§', text, flags=re.IGNORECASE)
87+
text = re.sub(r'<br\s*/?>', r'§§§LINEBREAK§§§', text, flags=re.IGNORECASE)
8088

8189
# Headers - do first before escaping
82-
text = re.sub(r'^### (.+)$', r'§§§SUBSUB:\1§§§', text, flags=re.MULTILINE)
83-
text = re.sub(r'^## (.+)$', r'§§§SUB:\1§§§', text, flags=re.MULTILINE)
84-
text = re.sub(r'^# (.+)$', r'§§§SEC:\1§§§', text, flags=re.MULTILINE)
90+
text = re.sub(r'^### (.+)$', r'§§§SUBSUB:\1§§§', text, flags=re.MULTILINE)
91+
text = re.sub(r'^## (.+)$', r'§§§SUB:\1§§§', text, flags=re.MULTILINE)
92+
text = re.sub(r'^# (.+)$', r'§§§SEC:\1§§§', text, flags=re.MULTILINE)
8593

8694
# Bold and italic - use placeholders
8795
bold_pattern = r'\*\*(.+?)\*\*'
88-
text = re.sub(bold_pattern, r'§§§BOLD:\1§§§', text)
96+
text = re.sub(bold_pattern, r'§§§BOLD:\1§§§', text)
8997

9098
# Replace *text* with italic placeholder
9199
italic_pattern = r'\*(.+?)\*'
92-
text = re.sub(italic_pattern, r'§§§ITALIC:\1§§§', text)
100+
text = re.sub(italic_pattern, r'§§§ITALIC:\1§§§', text)
93101

94102
# Images - convert markdown image syntax to placeholders FIRST (before links)
95103
image_pattern = r'!\[([^\]]*)\]\(([^\)]+)\)'
@@ -100,12 +108,12 @@ def replace_image(match):
100108
if image_path.startswith('./'):
101109
image_path = image_path[2:]
102110
# Store the image path with a special marker to avoid escaping underscores later
103-
return f'§§§IMAGE:{image_path}§§§'
111+
return f'§§§IMAGE:{image_path}§§§'
104112
text = re.sub(image_pattern, replace_image, text)
105113

106114
# Links - convert markdown links to placeholders
107115
link_pattern = r'\[([^\]]+)\]\(([^\)]+)\)'
108-
text = re.sub(link_pattern, r'§§§LINK:\1§|§\2§§§', text)
116+
text = re.sub(link_pattern, r'§§§LINK:\1§|§\2§§§', text)
109117

110118
# Lists - convert bullet points to placeholders
111119
in_list = False
@@ -114,17 +122,17 @@ def replace_image(match):
114122
for line in lines:
115123
if re.match(r'^\s*[\*\-]\s+', line):
116124
if not in_list:
117-
new_lines.append('§§§BEGINLIST§§§')
125+
new_lines.append('§§§BEGINLIST§§§')
118126
in_list = True
119127
item = re.sub(r'^\s*[\*\-]\s+', '', line)
120-
new_lines.append(f'§§§ITEM:{item}§§§')
128+
new_lines.append(f'§§§ITEM:{item}§§§')
121129
else:
122130
if in_list:
123-
new_lines.append('§§§ENDLIST§§§')
131+
new_lines.append('§§§ENDLIST§§§')
124132
in_list = False
125133
new_lines.append(line)
126134
if in_list:
127-
new_lines.append('§§§ENDLIST§§§')
135+
new_lines.append('§§§ENDLIST§§§')
128136
text = '\n'.join(new_lines)
129137

130138
# Now escape special LaTeX characters
@@ -141,34 +149,34 @@ def replace_image(match):
141149

142150
# Don't escape special characters inside href URLs or image paths
143151
# First, temporarily protect href content
144-
href_pattern = r'§§§LINK:(.+?)§\|§(.+?)§§§'
152+
href_pattern = r'§§§LINK:(.+?)§\|§(.+?)§§§'
145153
hrefs = []
146154
def save_href(match):
147155
hrefs.append((match.group(1), match.group(2)))
148-
return f'§§§HREFPLACEHOLDER{len(hrefs)-1}§§§'
156+
return f'§§§HREFPLACEHOLDER{len(hrefs)-1}§§§'
149157
text = re.sub(href_pattern, save_href, text)
150158

151159
# Also protect image paths from escaping
152-
image_placeholder_pattern = r'§§§IMAGE:(.+?)§§§'
160+
image_placeholder_pattern = r'§§§IMAGE:(.+?)§§§'
153161
images = []
154162
def save_image(match):
155163
images.append(match.group(1))
156-
return f'§§§IMAGEPLACEHOLDER{len(images)-1}§§§'
164+
return f'§§§IMAGEPLACEHOLDER{len(images)-1}§§§'
157165
text = re.sub(image_placeholder_pattern, save_image, text)
158166

159167
for char, replacement in special_chars.items():
160168
text = text.replace(char, replacement)
161169

162170
# Now convert placeholders to actual LaTeX
163-
text = re.sub(r'§§§SUBSUB:(.+?)§§§', r'\\subsubsection*{\1}', text)
164-
text = re.sub(r'§§§SUB:(.+?)§§§', r'\\subsection*{\1}', text)
165-
text = re.sub(r'§§§SEC:(.+?)§§§', r'\\section*{\1}', text)
166-
text = re.sub(r'§§§BOLD:(.+?)§§§', r'\\textbf{\1}', text)
167-
text = re.sub(r'§§§ITALIC:(.+?)§§§', r'\\textit{\1}', text)
171+
text = re.sub(r'§§§SUBSUB:(.+?)§§§', r'\\subsubsection*{\1}', text)
172+
text = re.sub(r'§§§SUB:(.+?)§§§', r'\\subsection*{\1}', text)
173+
text = re.sub(r'§§§SEC:(.+?)§§§', r'\\section*{\1}', text)
174+
text = re.sub(r'§§§BOLD:(.+?)§§§', r'\\textbf{\1}', text)
175+
text = re.sub(r'§§§ITALIC:(.+?)§§§', r'\\textit{\1}', text)
168176

169177
# Convert line break placeholders to LaTeX line breaks
170178
# Using \vspace{0.5em} which adds vertical space and works reliably in all contexts
171-
text = text.replace('§§§LINEBREAK§§§', '\\vspace{0.5em}')
179+
text = text.replace('§§§LINEBREAK§§§', '\\vspace{0.5em}')
172180

173181
# Restore hrefs and handle # in URLs
174182
def restore_href(match):
@@ -192,19 +200,19 @@ def restore_href(match):
192200
f'\\end{{center}}')
193201
else:
194202
return f'\\href{{{url}}}{{{link_text}}}'
195-
text = re.sub(r'§§§HREFPLACEHOLDER(\d+)§§§', restore_href, text)
203+
text = re.sub(r'§§§HREFPLACEHOLDER(\d+)§§§', restore_href, text)
196204

197205
# Restore image placeholders
198206
def restore_image(match):
199207
idx = int(match.group(1))
200-
return f'§§§IMAGE:{images[idx]}§§§'
201-
text = re.sub(r'§§§IMAGEPLACEHOLDER(\d+)§§§', restore_image, text)
208+
return f'§§§IMAGE:{images[idx]}§§§'
209+
text = re.sub(r'§§§IMAGEPLACEHOLDER(\d+)§§§', restore_image, text)
202210

203211
# Convert image placeholders - note: captions are handled separately as italic text following the image
204-
text = re.sub(r'§§§IMAGE:(.+?)§§§', r'\\begin{center}\\includegraphics[width=0.9\\columnwidth]{./articles/images/\1}\\end{center}', text)
205-
text = text.replace('§§§BEGINLIST§§§', '\\begin{itemize}')
206-
text = text.replace('§§§ENDLIST§§§', '\\end{itemize}')
207-
text = re.sub(r'§§§ITEM:(.+?)§§§', r'\\item \1', text)
212+
text = re.sub(r'§§§IMAGE:(.+?)§§§', r'\\begin{center}\\includegraphics[width=0.9\\columnwidth]{./articles/images/\1}\\end{center}', text)
213+
text = text.replace('§§§BEGINLIST§§§', '\\begin{itemize}')
214+
text = text.replace('§§§ENDLIST§§§', '\\end{itemize}')
215+
text = re.sub(r'§§§ITEM:(.+?)§§§', r'\\item \1', text)
208216

209217
return text
210218

@@ -287,8 +295,16 @@ def generate_articles_tex(self):
287295
if not article:
288296
continue
289297

290-
# Add needspace to prevent orphaned headers (ensure at least 5 lines stay together)
291-
content.append('\\needspace{5\\baselineskip}')
298+
# Check if article starts with an image
299+
article_content = article.get('content', '')
300+
starts_with_image = article_content.strip().startswith('![')
301+
302+
# Use larger needspace if article starts with image
303+
# This reserves space for: header + image + at least one paragraph
304+
if starts_with_image:
305+
content.append('\\needspace{20\\baselineskip}')
306+
else:
307+
content.append('\\needspace{5\\baselineskip}')
292308

293309
# Add label for TOC reference
294310
content.append(f'\\label{{article:{article_name}}}')
@@ -308,7 +324,6 @@ def generate_articles_tex(self):
308324
content.append(f'\\headline{{\\textbf{{\\Large {title}}}}}')
309325

310326
# Article content
311-
article_content = article.get('content', '')
312327
latex_content = self.markdown_to_latex(article_content, is_article=True)
313328
content.append(latex_content)
314329

@@ -369,6 +384,80 @@ def generate_events_tex(self):
369384

370385
return '\n'.join(content)
371386

387+
def generate_horoscope_tex(self):
388+
"""Generate the horoscope section LaTeX file"""
389+
horoscope_data = self.load_horoscope()
390+
content = []
391+
392+
# Add label for TOC reference
393+
content.append('\\label{horoscope}')
394+
content.append('')
395+
396+
horoscope_list = horoscope_data.get('horoscope', [])
397+
if not horoscope_list:
398+
content.append('\\headline{\\textbf{\\LARGE Your Horoscope}}')
399+
content.append('\\vspace{0.05cm}')
400+
content.append('')
401+
content.append('\\noindent\\textit{No horoscope available this issue.}')
402+
else:
403+
# Extract author from the list (it's a separate dict item)
404+
author = None
405+
question = None
406+
responses = []
407+
408+
for item in horoscope_list:
409+
if 'author' in item:
410+
author = item['author']
411+
if 'question' in item:
412+
question = item['question']
413+
if 'response' in item:
414+
responses = item['response']
415+
416+
# Display title with byline if author exists
417+
if author:
418+
author_upper = author.upper()
419+
content.append(f'\\byline{{\\textbf{{\\LARGE Your Horoscope}}}}{{{author_upper}}}')
420+
else:
421+
content.append('\\headline{\\textbf{\\LARGE Your Horoscope}}')
422+
423+
content.append('\\vspace{0.05cm}')
424+
content.append('')
425+
426+
# Display the question
427+
if question:
428+
content.append(f'\\noindent\\textbf{{\\large {question}}}')
429+
content.append('')
430+
content.append('\\vspace{0.3cm}')
431+
content.append('')
432+
433+
# Start 2-column layout for zodiac signs
434+
content.append('\\begin{multicols}{2}')
435+
content.append('')
436+
437+
# Display each zodiac sign's response
438+
for i, response_item in enumerate(responses):
439+
for sign, text in response_item.items():
440+
# Zodiac sign in bold
441+
content.append(f'\\noindent\\textbf{{{sign}:}} {text}')
442+
content.append('')
443+
444+
# Add spacing between entries
445+
if i < len(responses) - 1:
446+
content.append('\\vspace{0.2cm}')
447+
content.append('')
448+
449+
# Force column break after the 6th zodiac sign (Virgo)
450+
if i == 5: # After Virgo (0-indexed, so 5 is the 6th)
451+
content.append('\\columnbreak')
452+
content.append('')
453+
454+
# End 2-column layout
455+
content.append('\\end{multicols}')
456+
457+
content.append('\\vspace{0.2cm}')
458+
459+
return '\n'.join(content)
460+
372461
def format_meeting_time(self, meeting_time):
373462
"""Format meeting time from JSON data"""
374463
if not meeting_time:
@@ -639,6 +728,9 @@ def generate_toc_tex(self):
639728
except:
640729
pass
641730

731+
content.append('\\noindent Your Horoscope \\dotfill \\pageref{horoscope}')
732+
content.append('')
733+
642734
content.append('\\noindent ACM @ UIUC Directory \\dotfill \\pageref{directory}')
643735
content.append('')
644736

@@ -663,6 +755,12 @@ def generate_all(self, output_dir):
663755
with open(output_path / 'events.tex', 'w', encoding='utf-8') as f:
664756
f.write(events_tex)
665757

758+
# Generate horoscope
759+
print("Generating horoscope...")
760+
horoscope_tex = self.generate_horoscope_tex()
761+
with open(output_path / 'horoscope.tex', 'w', encoding='utf-8') as f:
762+
f.write(horoscope_tex)
763+
666764
# Generate letter from the chair
667765
print("Generating letter from the chair...")
668766
letter_tex = self.generate_letter_tex()
@@ -681,10 +779,11 @@ def generate_all(self, output_dir):
681779
with open(output_path / 'directory.tex', 'w', encoding='utf-8') as f:
682780
f.write(directory_tex)
683781

684-
print(f"\n✓ All files generated in {output_path}/")
782+
print(f"\nAll files generated in {output_path}/")
685783
print("\nGenerated files:")
686784
print(" - toc.tex")
687785
print(" - events.tex")
786+
print(" - horoscope.tex")
688787
print(" - letter.tex")
689788
print(" - articles.tex")
690789
print(" - directory.tex")
@@ -710,9 +809,9 @@ def generate_all(self, output_dir):
710809
generator.generate_all(f'{args.base_dir}/content')
711810

712811
if args.print_mode:
713-
print("\n📄 Generated in PRINT mode (non-clickable black links)")
812+
print("\nGenerated in PRINT mode (non-clickable black links)")
714813
else:
715-
print("\n🌐 Generated in ONLINE mode (clickable blue links)")
814+
print("\nGenerated in ONLINE mode (clickable blue links)")
716815

717816
except Exception as e:
718817
print(f"Error: {e}")

content/issues/2025-11-01 (43-1)/texput.log

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.pdf
2+
*.fdb_latexmk
3+
*.fls
4+
*.synctex.gz
5+
./content/*

content/issues/2025-11-01 (43-1)/vol43is1/articles/signll.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
title: Why NLP Matters More than Ever
2-
authors: ['Vinay Rajagopalan']
2+
authors: ['Kush Bhardwaj']
33

44
You have probably heard of the term “natural language processing.” Maybe you think of it as a buzzword - isn’t it just ChatGPT, or a subset of artificial intelligence? However, it is a lot bigger than that. NLP is not just a subset of AI; it is the layer that makes AI usable by humans. It is the reason you are able to interact with the AI models you love and use. At its core, NLP is about teaching computers to interpret, structure, and generate language in a way that captures meaning and intention. It is not about flashy demos or the “state-of-the-art.” Rather, it is about modelling and making sense of the understandability and interactability itself. So if NLP is bigger than chatbots, what does it actually do, and why does it matter for computer scientists today?
55

content/issues/2025-11-01 (43-1)/vol43is1/articles/sigplan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ authors: ['Eyad Loutfi']
33

44
![sigplan](sigplan.png)
55

6-
One of the more interesting consequences of interactive theorem proving in recent years has been the realization of its potential both to enhance collaboration among professional mathematicians and bridge the gap of professional mathematics for software engineers and others from less traditional academic backgrounds.
6+
In recent years, interactive theorem proving has revealed unexpected potential, both enhancing collaboration among professional mathematicians and making the field accessible to software engineers and those from less traditional academic backgrounds.
77

88
Many of the benefits of mathematicians adopting such technologies are obvious - digitizing a library of theorems would open it up to search and other automotive tools, which could then be used to assist in the building of more complicated modern proofs. Should we reach the point where modern research level proofs are built with or at least checked by a theorem prover, ensuring correctness would no longer be a matter of faith in the author or in the wait for peer review.
99

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
status: active
22

33
blurb: |
4-
HackIllinois is UIUC's premier collegiate hackathon. With over 1000 attendees and 50 mentors in 2019, the hackathon has become one of the largest and most well-regarded in the nation.
4+
HackIllinois is UIUC's premier student-run hackathon. Each spring, nearly 1000 students come together for one weekend to learn and create innovative hacks such as websites, mobile apps, and many more!

content/issues/2025-11-01 (43-1)/vol43is1/blurb/sigplan.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ meeting_times:
99
blurb: |
1010
Interested in having a programming language debate? Want to discuss your favorite formal verifier? Maybe even learn some category theory while you’re at it?
1111
12-
Sigplan is the place to casually meet other fellow PL enthusiasts (often to some pizza : D) and discuss related topics, be it in compilers, functional programming, formal verification, or mathematical logic and type theory (with its endless applications to both programming and math, or even the tangentially related but deep discussion of what mathematics even is). If these topics seem daunting, don’t worry since all are welcome to join with or without background, and we encourage anyone to come learn!
12+
Sigplan is the place to casually meet other fellow PL enthusiasts (often to some pizza : D) and discuss related topics, be it in compilers, functional programming, formal verification, or mathematical logic and type theory (with its endless applications to both programming and math, or even the tangentially related but deep discussion of what mathematics even *is*). If these topics seem daunting, don’t worry since all are welcome to join with or without background, and we encourage anyone to come learn!
1313
1414
Besides the opportunity for socials and presentations on intriguing and requested topics, anyone is further welcome to give their own presentations. Whether to share a related topic they’re passionate about, or as motivation to encourage self learning and practice communication of said topics.
1515
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
status: active
22

3-
meeting_times:
4-
- date: wednesday
5-
start_time: 18:00
6-
end_time: 19:00
7-
location: ECEB 4070
8-
93
blurb: |
104
Sigtricity is the first SIG specializing in hardware and electrical engineering, while also incorporating CS and programming into hardware. We aim to bridge the gap between students who want to take on projects to build their resume and skill set, but do not know where to start. We provide explanations and directions that can be understood by any major or level of experience and provide all the parts. All you need to do is show up and be willing to learn.

0 commit comments

Comments
 (0)