Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SR2024 Layout (Area Helpdesks) #30

Merged
merged 4 commits into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/build-sr2024.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Build SR2024 floorplan outputs

on:
push:
paths:
- '2024/**'
- '.github/workflows/build-sr2024.yml'
jobs:
build-2024:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.11"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r 2024/requirements.txt

sudo apt-get update
sudo apt-get install inkscape

- name: Install fonts
run: |
sudo mkdir -p /usr/share/fonts/sr-fonts

for f in resources/*.zip
do
unzip $f -d /usr/share/fonts/sr-fonts/$(basename ${f/.zip})
done

sudo fc-cache -fv

- name: Build sr2024 floorplans
run: |
cd 2024
./generate_svg.py --teams team_names.yaml

./populate-map.py
inkscape --export-type=pdf,png --export-dpi=192 output/map-with-teams.svg

- name: Archive floorplan files
uses: actions/upload-artifact@v3
with:
name: "sr2024 floorplans"
path: 2024/output
4 changes: 4 additions & 0 deletions 2024/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/venv

full plans/
output/
82 changes: 82 additions & 0 deletions 2024/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Notes for 2024

The templates here are intended to be 100:1 scale diagram of all the areas used in SUSU, they are believed to be within 5% accurate.
Locations highlighted in red on one of the venue related layers require measurements to be taken to assure their size.

#### Requires:
- Python 3.6+
- PyYaml

```bash
pip3 install --user -U -r requirements.txt
```

## Layers
#### Floorplan layers:
- Notes *(Additional notes)*
- Dimensions
- VenueDimensions *(Dimensions relating to venue floorplan)*
- AreaDimensions *(Dimensions relating to area placement and layout)*
- TLA *(Team names)*
- Network
- Displays *(SRcomp info monitors)*
- VenueNetwork *(Venue installed networking)*
- NetworkEquipment *(Additional cables, switches, routers and access points)*
- Power
- VenuePower *(Venue power outlets)*
- PowerEquipment *(Required power locations)*
- Areas
- AreaTables *(Position of designated tables)*
- AreaZones *(Marked out zones)*
- Shepherding *(Shepherd specific highlighting)*
- General
- GeneralLabels *(global drawing labels)*
- VenueFloorplan *(venue floorplans)*
- Exits *(Fire exits and lanes)*


#### Key Layers:
- General
- Network
- Power
- Areas
- Shepherding


#### YAML Layer Specification
```yaml
image: "cube.svg"
title: "Cube Layout"
show:
- ALL
hide:
- shepherding
embed:
-
marker: "KEY__"
image: "key.svg"
show:
- ALL
hide:
- shepherding
```

#### JSON Layer Specification
```json
{
"image": "cube.svg",
"title": "Cube Layout",
"show": [
"All"
],
"hide": [
"shepherding"
],
"embed": [{
"marker": "KEY__",
"image": "key.svg",
"show":[ "ALL" ],
"hide": [ "shepherding" ]
}]
}
```
212 changes: 212 additions & 0 deletions 2024/generate_svg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
#!/usr/bin/env python3

import re
import json
import argparse
import xml.etree.ElementTree as ET
from pathlib import Path
from contextlib import suppress

import yaml


def register_xml_namespaces(xml_file):
with open(xml_file, 'r') as file:
ns = dict([node for _, node in ET.iterparse(file, events=['start-ns'])])

for prefix in ns:
ET.register_namespace(prefix, ns[prefix])

return ns


def load_config(conf_file):
try:
with open(conf_file, 'r') as file:
conf = json.load(file) # load json specification
except json.JSONDecodeError:
print(f"Failed to read {conf_file} as JSON, trying YAML")
with open(conf_file, 'r') as file:
conf = yaml.safe_load(file) # load yaml specification

return conf


def print_layers(
root, show=[], hide=[],
ns={
'svg': 'http://www.w3.org/2000/svg',
'inkscape': 'http://www.inkscape.org/namespaces/inkscape'
},
prefix="", autoshow=False
):
if prefix:
prefix += '/'

for child in root.findall('./svg:g[@inkscape:groupmode="layer"]', ns):
label = prefix + child.get(f'{{{ns["inkscape"]}}}label', "")

if autoshow:
if label in hide:
child.set('style', 'display:none')
print(f'Hide: {label}')
else:
child.attrib.pop('style', None)
print(f'Show: {label}')
else:
if label in hide:
child.set('style', 'display:none')
print(f'Hide: {label}')
elif label in show or 'ALL' in show:
child.attrib.pop('style', None)
print(f'Show: {label}')
child = print_layers(child, show, hide, ns, prefix=label, autoshow=True)
else:
child.set('style', 'display:none')
print(f'Hide: {label}')

return root


def set_titles(root, title, version, scale, ns):
with suppress(IndexError, AttributeError, TypeError):
root.find('.//svg:text[svg:tspan="{{title}}"]', ns)[0].text = title
with suppress(IndexError, AttributeError, TypeError):
root.find('.//svg:text[svg:tspan="{{version}}"]', ns)[0].text = f"Version: {version}"
with suppress(IndexError, AttributeError, TypeError):
root.find('.//svg:text[svg:tspan="{{scale}}" ]', ns)[0].text = f"Scale 1:{scale:.0f}"

return root


def insert_tlas(svg_root, teams, ns):
TLA = svg_root.find('svg:g[@inkscape:label="TLA"]', ns) # add team names
if TLA:
for team in TLA.findall('.//svg:text/svg:tspan', ns):
team_no = re.search(r'@T_(\d+)', team.text)
if team_no is not None:
team.text = teams.get(int(team_no[1]), '')

return svg_root


def embed_svg(embedded, root, ns, template_dir, team_names=None):
print(f"Embedding {embedded['image']}")
embedded_root = ET.parse(Path(template_dir) / embedded['image']).getroot() # load svg

if team_names is not None:
embedded_root = insert_tlas(embedded_root, team_names, ns)

# display only selected layers or ALL
embedded_root = print_layers(
embedded_root,
embedded.get('show', ['ALL']),
embedded.get('hide', []),
ns=ns,
)

embed_marker = embedded['marker']
# get container that we will be inserting into
embed_parent = root.find(f'.//svg:rect[@id="{embed_marker}"]/..', ns)
if embed_parent is None:
print(f"Failed to find the marker {embed_marker}")
return

# get the element that will be replaced by the embed
embed_child = embed_parent.find(f'./svg:rect[@id="{embed_marker}"]', ns)
if embed_child is None:
print(f"Failed to find the marker {embed_marker}")
return

embed_index = list(embed_parent).index(embed_child)

for field in ['x', 'y', 'width', 'height']: # set x, y, height & width from the placeholder
embedded_root.set(field, embed_child.get(field))

embed_parent[embed_index] = embedded_root # replace element with svg
print(f"Embedded {embedded['image']}")
return root


def generate_svg(spec_path, template_dir, out_dir, base_scale, teams_file=None):
team_names = None
spec = load_config(spec_path)

svg_file = Path(template_dir) / spec['image']
ns = register_xml_namespaces(svg_file)

root_tree = ET.parse(svg_file) # load svg
root = root_tree.getroot()

root = set_titles(
root,
spec.get('title', spec['image']),
spec.get('version', 0.1),
spec.get('scale', 1) * base_scale,
ns,
)

if teams_file:
team_names = load_config(teams_file)
root = insert_tlas(root, team_names, ns)

# set scale
try:
old_width = float(root.get('width')[:-2])
old_height = float(root.get('height')[:-2])
except TypeError:
print("Invalid SVG")

scale = spec.get('scale', 1)
root.set('width', str(old_width / scale) + "cm")
root.set('height', str(old_height / scale) + "cm")

# display only selected layers or ALL
print_layers(root, spec.get('show', ['ALL']), spec.get('hide', []), ns=ns)

for embedded in spec.get('embed', []): # add nested svgs (including key)
root = embed_svg(embedded, root, ns, template_dir, team_names)

out_dir = Path(out_dir)
out_dir.mkdir(exist_ok=True)

out_file = spec.get('title', 'output').replace(' ', '_') + '.svg'
root_tree.write(out_dir / out_file, xml_declaration=True, encoding='UTF-8')


def main():
parser = argparse.ArgumentParser()
parser.add_argument('specs', type=Path, nargs=argparse.OPTIONAL, default=Path('layouts/'), help=(
"Folder or file containing the YAML/JSON that defines what output SVG's are created, "
"defaults to '%(default)s'"
))
parser.add_argument('-s', '--base-scale', type=int, default=100, help=(
"The initial 1:X scale that the template files are at, defaults to %(default)s"
))
parser.add_argument('-t', '--templates', type=Path, default=Path('templates/'), help=(
"Folder containing the template SVG's, defaults to '%(default)s'"
))
parser.add_argument('--teams', type=Path, default=None, help=(
"The YAML/JSON file containing a mapping of number to TLA"
))
parser.add_argument('-o', '--output', type=Path, default=Path('output/'), help=(
"Folder to store the output SVG's, defaults to '%(default)s'"
))

args = parser.parse_args()

if args.specs.is_file():
generate_svg(args.specs, args.templates, args.output, args.base_scale, teams_file=args.teams)
elif args.specs.is_dir():
for spec in args.specs.iterdir():
if not spec.is_file():
continue

print(f"Processing spec file {spec}")
generate_svg(spec, args.templates, args.output, args.base_scale, teams_file=args.teams)
else:
print("The specification is neither a file nor directory")


if __name__ == "__main__":
main()
15 changes: 15 additions & 0 deletions 2024/layouts/cube_layout.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
image: "cube.svg"
title: "Cube Layout"
version: 0.1
show:
- ALL
hide:
- Shepherding
embed:
-
marker: "KEY__"
image: "key.svg"
show:
- ALL
hide:
- Shepherding
17 changes: 17 additions & 0 deletions 2024/layouts/cube_network.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
image: "cube.svg"
title: "Cube Network"
version: 0.1
show:
- General
- Network
- Dimensions
- Areas
- Notes
embed:
-
marker: "KEY__"
image: "key.svg"
show:
- ALL
hide:
- Shepherding
Loading
Loading