Simple site layout design
This commit is contained in:
125
assets/bib2yaml.py
Normal file
125
assets/bib2yaml.py
Normal file
@ -0,0 +1,125 @@
|
||||
import sys
|
||||
import re
|
||||
def remove_comments(text):
|
||||
return re.sub(r'(?m)^[ \t]*%.*\n?', '', text)
|
||||
|
||||
def find_entries(bibtex_content):
|
||||
entry_regex = re.compile(r'@(\w+)\s*[{(]', re.IGNORECASE)
|
||||
positions = [m.start() for m in entry_regex.finditer(bibtex_content)]
|
||||
entries = []
|
||||
for i, start in enumerate(positions):
|
||||
end = positions[i+1] if i+1 < len(positions) else len(bibtex_content)
|
||||
entries.append(bibtex_content[start:end].strip())
|
||||
return entries
|
||||
|
||||
def parse_bibtex_entry(entry):
|
||||
entry = entry.strip()
|
||||
if not entry or entry.startswith('%'):
|
||||
return None
|
||||
m = re.match(r'@(\w+)\s*[{(]\s*([^,]+),?', entry, re.DOTALL)
|
||||
if not m:
|
||||
return None
|
||||
entry_type, entry_key = m.groups()
|
||||
entry_type = entry_type.lower()
|
||||
if entry_type in ('comment', 'preamble'):
|
||||
return None
|
||||
if entry_type == 'string':
|
||||
return None
|
||||
body = entry[m.end():]
|
||||
body = body.rstrip('})').strip()
|
||||
fields = {}
|
||||
field_regex = re.compile(r'(\w+)\s*=\s*', re.DOTALL)
|
||||
pos = 0
|
||||
while pos < len(body):
|
||||
m = field_regex.match(body, pos)
|
||||
if not m:
|
||||
break
|
||||
field = m.group(1)
|
||||
pos = m.end()
|
||||
if body[pos] == '{':
|
||||
brace_level = 1
|
||||
val_start = pos + 1
|
||||
pos += 1
|
||||
while pos < len(body) and brace_level > 0:
|
||||
if body[pos] == '{':
|
||||
brace_level += 1
|
||||
elif body[pos] == '}':
|
||||
brace_level -= 1
|
||||
pos += 1
|
||||
value = body[val_start:pos-1]
|
||||
elif body[pos] == '"':
|
||||
val_start = pos + 1
|
||||
pos += 1
|
||||
while pos < len(body) and body[pos] != '"':
|
||||
if body[pos] == '\\':
|
||||
pos += 2
|
||||
else:
|
||||
pos += 1
|
||||
value = body[val_start:pos]
|
||||
pos += 1
|
||||
else:
|
||||
val_start = pos
|
||||
while pos < len(body) and body[pos] not in ',\n':
|
||||
pos += 1
|
||||
value = body[val_start:pos].strip()
|
||||
fields[field] = value.replace('\n', ' ').strip()
|
||||
while pos < len(body) and body[pos] in ', \n\r\t':
|
||||
pos += 1
|
||||
return entry_type, entry_key, fields
|
||||
|
||||
def escape_yaml_string(s):
|
||||
# Escape double quotes and backslashes, wrap in double quotes if needed
|
||||
if not s:
|
||||
return '""'
|
||||
if any(c in s for c in ':\'"{}[],&*#?|-<>=!%@`\\\n'):
|
||||
s = s.replace('\\', '\\\\').replace('"', '\\"')
|
||||
return f'"{s}"'
|
||||
return s
|
||||
|
||||
def dict_to_yaml(d, indent=0):
|
||||
lines = []
|
||||
for k, v in d.items():
|
||||
if isinstance(v, list):
|
||||
lines.append(' ' * indent + f"{k}:")
|
||||
for item in v:
|
||||
lines.append(' ' * (indent + 2) + f"- {escape_yaml_string(item)}")
|
||||
else:
|
||||
lines.append(' ' * indent + f"{k}: {escape_yaml_string(v)}")
|
||||
return '\n'.join(lines)
|
||||
|
||||
def bibtex_to_yaml(bibtex_content):
|
||||
bibtex_content = remove_comments(bibtex_content)
|
||||
entries = find_entries(bibtex_content)
|
||||
yaml_entries = []
|
||||
for entry in entries:
|
||||
parsed = parse_bibtex_entry(entry)
|
||||
if not parsed:
|
||||
continue
|
||||
entry_type, entry_key, fields = parsed
|
||||
entry_dict = {'key': entry_key}
|
||||
if "author" in fields:
|
||||
authors = [a.strip() for a in re.split(r'\s+and\s+', fields["author"])]
|
||||
entry_dict['author'] = authors
|
||||
del fields["author"]
|
||||
entry_dict.update(fields)
|
||||
yaml_entries.append(entry_dict)
|
||||
yaml_str = "publications:\n"
|
||||
for entry in yaml_entries:
|
||||
yaml_str += " - " + dict_to_yaml(entry, indent=4).lstrip().replace('\n', '\n ') + "\n"
|
||||
return yaml_str
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: {sys.argv[0]} <input.bib>")
|
||||
sys.exit(1)
|
||||
bibfile = sys.argv[1]
|
||||
with open(bibfile, 'r', encoding='utf-8') as f:
|
||||
bibtex_content = f.read()
|
||||
yaml_content = bibtex_to_yaml(bibtex_content)
|
||||
yamlfile = bibfile.rsplit('.', 1)[0] + '.yaml'
|
||||
with open(yamlfile, 'w', encoding='utf-8') as f:
|
||||
f.write(yaml_content)
|
||||
print(f"YAML exported to {yamlfile}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,22 +0,0 @@
|
||||
body {
|
||||
color: #222;
|
||||
font-family: sans-serif;
|
||||
line-height: 1.5;
|
||||
margin: 1rem;
|
||||
max-width: 768px;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 1px solid #222;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid #222;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00e;
|
||||
text-decoration: none;
|
||||
}
|
81
assets/sass/_fonts.sass
Normal file
81
assets/sass/_fonts.sass
Normal file
@ -0,0 +1,81 @@
|
||||
$font-path: '/fonts/static/'
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 100
|
||||
src: local('Source Sans 3 ExtraLight'), local('SourceSans3-ExtraLight'), url('#{$font-path}SourceSans3-ExtraLight.woff2') format('woff2'), url('#{$font-path}SourceSans3-ExtraLight.ttf') format('truetype')
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 200
|
||||
src: local('Source Sans 3 ExtraLight'), local('SourceSans3-ExtraLight'), url('#{$font-path}SourceSans3-ExtraLight.woff2') format('woff2'), url('#{$font-path}SourceSans3-ExtraLight.ttf') format('truetype')
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 300
|
||||
src: local('Source Sans 3 Light'), local('SourceSans3-Light'), url('#{$font-path}SourceSans3-Light.woff2') format('woff2'), url('#{$font-path}SourceSans3-Light.ttf') format('truetype')
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 400
|
||||
src: local('Source Sans 3 Regular'), local('SourceSans3-Regular'), url('#{$font-path}SourceSans3-Regular.woff2') format('woff2'), url('#{$font-path}SourceSans3-Regular.ttf') format('truetype')
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: italic
|
||||
font-display: swap
|
||||
font-weight: 400
|
||||
src: local('Source Sans 3 Italic'), local('SourceSans3-Italic'), url('#{$font-path}SourceSans3-Italic.woff2') format('woff2'), url('#{$font-path}SourceSans3-Italic.ttf') format('truetype')
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 500
|
||||
src: local('Source Sans 3 Medium'), local('SourceSans3-Medium'), url('#{$font-path}SourceSans3-Medium.woff2') format('woff2'), url('#{$font-path}SourceSans3-Medium.ttf') format('truetype')
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 600
|
||||
src: local('Source Sans 3 SemiBold'), local('SourceSans3-SemiBold'), url('#{$font-path}SourceSans3-SemiBold.woff2') format('woff2'), url('#{$font-path}SourceSans3-SemiBold.ttf') format('truetype')
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 700
|
||||
src: local('Source Sans 3 Bold'), local('SourceSans3-Bold'), url('#{$font-path}SourceSans3-Bold.woff2') format('woff2'), url('#{$font-path}SourceSans3-Bold.ttf') format('truetype')
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: italic
|
||||
font-display: swap
|
||||
font-weight: 700
|
||||
src: local('Source Sans 3 Bold Italic'), local('SourceSans3-BoldItalic'), url('#{$font-path}SourceSans3-BoldItalic.woff2') format('woff2'), url('#{$font-path}SourceSans3-BoldItalic.ttf') format('truetype')
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 800
|
||||
src: local('Source Sans 3 ExtraBold'), local('SourceSans3-ExtraBold'), url('#{$font-path}SourceSans3-ExtraBold.woff2') format('woff2'), url('#{$font-path}SourceSans3-ExtraBold.ttf') format('truetype')
|
||||
|
||||
@font-face
|
||||
font-family: 'Source Sans 3'
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
font-weight: 900
|
||||
src: local('Source Sans 3 Black'), local('SourceSans3-Black'), url('#{$font-path}SourceSans3-Black.woff2') format('woff2'), url('#{$font-path}SourceSans3-Black.ttf') format('truetype')
|
||||
|
||||
|
||||
*
|
||||
font-family: 'Source Sans 3', sans-serif
|
4
assets/sass/main.sass
Normal file
4
assets/sass/main.sass
Normal file
@ -0,0 +1,4 @@
|
||||
@import "fonts"
|
||||
*
|
||||
color: white
|
||||
background: black
|
Reference in New Issue
Block a user