Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
L
Lesson Template
Manage
Activity
Members
Labels
Plan
Issues
0
Issue boards
Milestones
Wiki
Code
Merge requests
0
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
prace-lessons
Lesson Template
Commits
6e8a62aa
Commit
6e8a62aa
authored
8 years ago
by
Greg Wilson
Browse files
Options
Downloads
Patches
Plain Diff
Validation tool for lesson sites
parent
84b45ebe
Branches
Branches containing commit
Tags
Tags containing commit
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
_includes/syllabus.html
+48
-46
48 additions, 46 deletions
_includes/syllabus.html
bin/validator
+190
-0
190 additions, 0 deletions
bin/validator
with
238 additions
and
46 deletions
_includes/syllabus.html
+
48
−
46
View file @
6e8a62aa
...
...
@@ -2,57 +2,59 @@
Display syllabus in tabular form.
Days are displayed if at least one episode has 'start = true'.
{% endcomment %}
<h2>
Schedule
</h2>
<div
class=
"syllabus"
>
<h2>
Schedule
</h2>
{% assign day = 0 %}
{% assign multiday = false %}
{% for episode in site.episodes %}
{% if episode.start %}{% assign multiday = true %}{% break %}{% endif %}
{% endfor %}
{% assign current = site.start_time %}
{% assign day = 0 %}
{% assign multiday = false %}
{% for episode in site.episodes %}
{% if episode.start %}{% assign multiday = true %}{% break %}{% endif %}
{% endfor %}
{% assign current = site.start_time %}
<table
class=
"table table-striped"
>
{% for episode in site.episodes %}
{% if episode.start %} {% comment %} Starting a new day? {% endcomment %}
{% assign day = day | plus: 1 %}
{% if day > 1 %} {% comment %} If about to start day 2 or later, show finishing time for previous day {% endcomment %}
{% assign hours = current | divided_by: 60 %}
{% assign minutes = current | modulo: 60 %}
<tr>
{% if multiday %}
<td></td>
{% endif %}
<td
class=
"col-md-1"
>
{% if hours
<
10
%}0{%
endif
%}{{
hours
}}
:
{%
if
minutes
<
10
%}0{%
endif
%}{{
minutes
}}</
td
>
<td
class=
"col-md-3"
>
Finish
</td>
<td
class=
"col-md-7"
></td>
</tr>
<table
class=
"table table-striped"
>
{% for episode in site.episodes %}
{% if episode.start %} {% comment %} Starting a new day? {% endcomment %}
{% assign day = day | plus: 1 %}
{% if day > 1 %} {% comment %} If about to start day 2 or later, show finishing time for previous day {% endcomment %}
{% assign hours = current | divided_by: 60 %}
{% assign minutes = current | modulo: 60 %}
<tr>
{% if multiday %}
<td></td>
{% endif %}
<td
class=
"col-md-1"
>
{% if hours
<
10
%}0{%
endif
%}{{
hours
}}
:
{%
if
minutes
<
10
%}0{%
endif
%}{{
minutes
}}</
td
>
<td
class=
"col-md-3"
>
Finish
</td>
<td
class=
"col-md-7"
></td>
</tr>
{% endif %}
{% assign current = site.start_time %} {% comment %}Re-set start time of this episode to general daily start time {% endcomment %}
{% endif %}
{% assign current = site.start_time %} {% comment %}Re-set start time of this episode to general daily start time {% endcomment %}
{% endif %}
{% assign hours = current | divided_by: 60 %}
{% assign minutes = current | modulo: 60 %}
<tr>
{% if multiday %}
<td
class=
"col-md-1"
>
{% if episode.start %}Day {{ day }}{% endif %}
</td>
{% endif %}
<td
class=
"col-md-1"
>
{% if hours
<
10
%}0{%
endif
%}{{
hours
}}
:
{%
if
minutes
<
10
%}0{%
endif
%}{{
minutes
}}</
td
>
<td
class=
"col-md-3"
>
<a
href=
"{{ site.root }}/{{ episode.url }}"
>
{{ episode.title }}
</a>
</td>
<td
class=
"col-md-7"
>
{% if episode.break %}
Break
{% else %}
{% if episode.questions %}
{{ episode.questions | join: '
<br/>
' }}
{% endif %}
{% endif %}
</td>
</tr>
{% assign current = current | plus: episode.teaching | plus: episode.exercises | plus: episode.break %}
{% endfor %}
{% assign hours = current | divided_by: 60 %}
{% assign minutes = current | modulo: 60 %}
<tr>
{% if multiday %}
<td
class=
"col-md-1"
>
{% if episode.start %}Day {{ day }}{% endif %}
</td>
{% endif %}
{% if multiday %}
<td
>
</td>
{% endif %}
<td
class=
"col-md-1"
>
{% if hours
<
10
%}0{%
endif
%}{{
hours
}}
:
{%
if
minutes
<
10
%}0{%
endif
%}{{
minutes
}}</
td
>
<td
class=
"col-md-3"
>
<a
href=
"{{ site.root }}/{{ episode.url }}"
>
{{ episode.title }}
</a>
</td>
<td
class=
"col-md-7"
>
{% if episode.break %}
Break
{% else %}
{% if episode.questions %}
{{ episode.questions | join: '
<br/>
' }}
{% endif %}
{% endif %}
</td>
<td
class=
"col-md-3"
>
Finish
</td>
<td
class=
"col-md-7"
></td>
</tr>
{% assign current = current | plus: episode.teaching | plus: episode.exercises | plus: episode.break %}
{% endfor %}
{% assign hours = current | divided_by: 60 %}
{% assign minutes = current | modulo: 60 %}
<tr>
{% if multiday %}
<td></td>
{% endif %}
<td
class=
"col-md-1"
>
{% if hours
<
10
%}0{%
endif
%}{{
hours
}}
:
{%
if
minutes
<
10
%}0{%
endif
%}{{
minutes
}}</
td
>
<td
class=
"col-md-3"
>
Finish
</td>
<td
class=
"col-md-7"
></td>
</tr>
</table>
</table>
</div>
This diff is collapsed.
Click to expand it.
bin/validator
0 → 100755
+
190
−
0
View file @
6e8a62aa
#!/usr/bin/env python
'''
Validate layout of generated HTML pages.
(We validate the HTML because source may not be in Markdown.)
'''
import
sys
import
os
import
glob
import
fnmatch
import
yaml
from
optparse
import
OptionParser
from
bs4
import
BeautifulSoup
from
lxml
import
etree
# Default configuration.
DEFAULT_CONFIG
=
'''
\
patterns:
'
*.html
'
:
- has_title_in_head
- has_navbar
- has_title_in_body
- has_footer
index.html:
- has_prereq
- has_syllabus
'
*-*/index.html
'
:
- has_objectives
'''
# Record all the rules.
RULES
=
{}
def
rule
(
fn
):
RULES
[
fn
.
__name__
]
=
fn
return
fn
def
main
():
'''
Main driver: check all files with all rules that apply.
'''
args
=
parse_args
()
read_config
(
args
)
docs
=
read_all_docs
(
args
.
source_dir
)
_require
(
docs
,
'
No source files found in {0}
'
.
format
(
args
.
source_dir
))
all_filenames
=
docs
.
keys
()
for
filename
in
all_filenames
:
if
args
.
verbose
>
0
:
print
(
filename
,
'
...
'
,
file
=
sys
.
stderr
)
for
pattern
in
args
.
patterns
:
full_pattern
=
os
.
path
.
join
(
args
.
source_dir
,
pattern
)
if
fnmatch
.
fnmatch
(
filename
,
full_pattern
):
for
rule
in
args
.
patterns
[
pattern
]:
if
args
.
verbose
>
1
:
print
(
'
...
'
,
rule
,
file
=
sys
.
stderr
)
RULES
[
rule
](
filename
,
docs
[
filename
])
def
parse_args
():
'''
Parse command-line arguments.
'''
parser
=
OptionParser
()
parser
.
add_option
(
'
-c
'
,
'
--config
'
,
default
=
None
,
dest
=
'
config_file
'
,
help
=
'
configuration file
'
)
parser
.
add_option
(
'
-s
'
,
'
--source
'
,
default
=
'
_site
'
,
dest
=
'
source_dir
'
,
help
=
'
source directory
'
)
parser
.
add_option
(
'
-v
'
,
'
--verbose
'
,
default
=
0
,
action
=
'
count
'
,
dest
=
'
verbose
'
,
help
=
'
report actions
'
)
args
,
extras
=
parser
.
parse_args
()
_require
(
not
extras
,
'
Unexpected trailing command-line arguments
"
{0}
"'
.
format
(
extras
))
return
args
def
read_config
(
args
):
'''
Read configuration file.
'''
if
args
.
config_file
:
with
open
(
args
.
config_file
,
'
r
'
)
as
reader
:
args
.
config
=
yaml
.
load
(
reader
)
else
:
args
.
config
=
yaml
.
load
(
DEFAULT_CONFIG
)
args
.
patterns
=
args
.
config
[
'
patterns
'
]
def
read_all_docs
(
source_dir
):
'''
Read all HTML pages under the source directory.
Returns a dictionary of (path, doc).
'''
pattern
=
os
.
path
.
join
(
source_dir
,
'
**/*.html
'
)
result
=
{}
for
path
in
glob
.
iglob
(
pattern
,
recursive
=
True
):
try
:
with
open
(
path
,
'
r
'
)
as
reader
:
raw
=
reader
.
read
().
replace
(
'
<!doctype html>
\n
'
,
''
)
soup
=
BeautifulSoup
(
raw
,
'
html.parser
'
).
prettify
()
doc
=
etree
.
fromstring
(
soup
)
result
[
path
]
=
doc
except
IOError
as
e
:
print
(
'
Unable to open {0}: {1}
'
.
format
(
path
,
e
),
file
=
sys
.
stderr
)
sys
.
exit
(
1
)
return
result
@rule
def
has_footer
(
filename
,
doc
):
'''
Document has footer element.
'''
_check_1
(
filename
,
doc
,
'
footers
'
,
'
//footer
'
)
@rule
def
has_navbar
(
filename
,
doc
):
'''
Document has header element.
'''
_check_1
(
filename
,
doc
,
'
div navbar
'
,
'
//div[@class=
"
navbar-header
"
]
'
)
@rule
def
has_objectives
(
filename
,
doc
):
'''
Episode has objectives.
'''
_check_1
(
filename
,
doc
,
'
objectives div
'
,
'
//blockquote[@class=
"
objectives
"
]
'
)
@rule
def
has_prereq
(
filename
,
doc
):
'''
Index page has prerequisites block.
'''
_check_1
(
filename
,
doc
,
'
prerequisites blockquote
'
,
'
//blockquote[@class=
"
prereq
"
]
'
)
@rule
def
has_syllabus
(
filename
,
doc
):
'''
Index page has syllabus.
'''
_check_1
(
filename
,
doc
,
'
syllabus
'
,
'
//div[@class=
"
syllabus
"
]
'
)
_check_1
(
filename
,
doc
,
'
syllabus title
'
,
'
//div[@class=
"
syllabus
"
]/h2
'
)
_check_1
(
filename
,
doc
,
'
syllabus table
'
,
'
//div[@class=
"
syllabus
"
]/table
'
)
@rule
def
has_title_in_head
(
filename
,
doc
):
'''
Document has a title in the head.
'''
_check_1
(
filename
,
doc
,
'
title in head
'
,
'
//head//title
'
)
@rule
def
has_title_in_body
(
filename
,
doc
):
'''
Document has a title in the body.
'''
_check_1
(
filename
,
doc
,
'
title in body
'
,
'
//body//h1[@class=
"
maintitle
"
]
'
)
def
_check_1
(
filename
,
doc
,
rulename
,
xpath
):
'''
Check that an equality holds.
'''
actual
=
doc
.
xpath
(
xpath
)
if
len
(
actual
)
!=
1
:
print
(
'
In {0}, checking {1}: expected 1 match, got {2}
'
.
format
(
filename
,
rulename
,
len
(
actual
)))
def
_require
(
condition
,
message
):
'''
Fail if condition not met.
'''
if
not
condition
:
print
(
message
,
file
=
sys
.
stderr
)
sys
.
exit
(
1
)
if
__name__
==
'
__main__
'
:
main
()
This diff is collapsed.
Click to expand it.
Preview
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment