Function bodies 58 total
on_page_context function · python · L31-L35 (5 LOC)plugins/social_override.py
def on_page_context(context, page, config, **kwargs):
"""Stash the custom image path on the page for the post_page hook."""
if page.meta and 'image' in page.meta:
page.custom_image = page.meta['image']
return contexton_post_page function · python · L38-L66 (29 LOC)plugins/social_override.py
def on_post_page(html, page, config, **kwargs):
"""Replace the social plugin's generated og:image / twitter:image
tags with the page's custom cover image URL."""
if not hasattr(page, 'custom_image'):
return html
site_url = config['site_url'].rstrip('/')
image_path = '/' + page.custom_image.lstrip('/')
full_image_url = site_url + image_path
# og:image uses property=
og_pattern = re.compile(r'<meta\s+property="og:image"[^>]*?>')
for tag in og_pattern.findall(html):
if '/assets/images/social/' in tag:
new_tag = f'<meta property="og:image" content="{full_image_url}">'
html = html.replace(tag, new_tag)
# Material emits twitter:image with property= (not name=), so match both
twitter_pattern = re.compile(
r'<meta\s+(?:property|name)="twitter:image"[^>]*?>'
)
for tag in twitter_pattern.findall(html):
if '/assets/images/social/' in tag:
# Preserve whichever attribute style init function · javascript · L10-L37 (28 LOC)skills/it-graph-generator/assets/template-script.js
async function init() {
try {
// Load the graph data
const response = await fetch('{{DATA_FILE}}');
const data = await response.json();
allNodes = data.nodes || [];
allEdges = data.edges || [];
// Extract unique node types
allNodes.forEach(node => {
if (node.type) nodeTypes.add(node.type);
});
// Initialize UI components
initializeFilterCheckboxes();
initializeLegend();
initializeNetwork();
setupEventListeners();
} catch (error) {
console.error('Error loading graph data:', error);
document.getElementById('network').innerHTML =
'<div style="padding: 20px; text-align: center; color: #ef4444;">' +
'Error loading graph data. Please check the console for details.' +
'</div>';
}
}initializeFilterCheckboxes function · javascript · L39-L60 (22 LOC)skills/it-graph-generator/assets/template-script.js
function initializeFilterCheckboxes() {
const container = document.getElementById('filterCheckboxes');
container.innerHTML = '';
Array.from(nodeTypes).sort().forEach(type => {
const label = document.createElement('label');
label.className = 'filter-checkbox';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = true;
checkbox.value = type;
checkbox.onchange = applyFilters;
const span = document.createElement('span');
span.textContent = type;
label.appendChild(checkbox);
label.appendChild(span);
container.appendChild(label);
});
}initializeLegend function · javascript · L62-L100 (39 LOC)skills/it-graph-generator/assets/template-script.js
function initializeLegend() {
const legendContent = document.getElementById('legendContent');
legendContent.innerHTML = '';
// Group nodes by type and get color/shape info
const typeInfo = {};
allNodes.forEach(node => {
if (!typeInfo[node.type]) {
typeInfo[node.type] = {
color: node.color || '#94a3b8',
shape: node.shape || 'dot',
icon: node.icon || null
};
}
});
// Create legend items
Object.keys(typeInfo).sort().forEach(type => {
const item = document.createElement('div');
item.className = 'legend-item';
const indicator = document.createElement('div');
indicator.className = 'legend-indicator';
if (typeInfo[type].icon) {
indicator.innerHTML = `<img src="${typeInfo[type].icon}" style="width: 20px; height: 20px;" alt="${type}">`;
} else {
indicator.style.backgroundColor = typeInfo[type].color;
initializeNetwork function · javascript · L102-L169 (68 LOC)skills/it-graph-generator/assets/template-script.js
function initializeNetwork() {
// Create vis.js DataSets
nodes = new vis.DataSet(allNodes);
edges = new vis.DataSet(allEdges);
const container = document.getElementById('network');
const data = { nodes, edges };
const options = {
nodes: {
font: { size: 14, color: '#1e293b' },
borderWidth: 2,
borderWidthSelected: 4,
shadow: true
},
edges: {
width: 2,
shadow: true,
smooth: {
type: 'continuous'
}
},
physics: {
enabled: true,
stabilization: {
iterations: 200
},
barnesHut: {
gravitationalConstant: -4000,
centralGravity: 0.3,
springLength: 150,
springConstant: 0.04,
damping: 0.09,
avoidOverlap: 0.5
}
},
interaction: {
hocreateTooltip function · javascript · L171-L180 (10 LOC)skills/it-graph-generator/assets/template-script.js
function createTooltip(node) {
let tooltip = `Type: ${node.type}\n`;
if (node.properties) {
tooltip += '\nProperties:\n';
Object.entries(node.properties).forEach(([key, value]) => {
tooltip += ` ${key}: ${value}\n`;
});
}
return tooltip;
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
displayNodeDetails function · javascript · L182-L209 (28 LOC)skills/it-graph-generator/assets/template-script.js
function displayNodeDetails(nodeData) {
if (!nodeData) return;
const detailsDiv = document.getElementById('nodeDetails');
let html = `
<div class="node-type">:${nodeData.type}</div>
<div class="node-label">${nodeData.label}</div>
`;
if (nodeData.properties) {
html += '<div class="properties-list">';
Object.entries(nodeData.properties).forEach(([key, value]) => {
html += `
<div class="property-item">
<span class="property-key">${key}:</span>
<span class="property-value">${value}</span>
</div>
`;
});
html += '</div>';
const propCount = Object.keys(nodeData.properties).length;
html += `<div class="property-count">${propCount} ${propCount === 1 ? 'property' : 'properties'}</div>`;
}
detailsDiv.innerHTML = html;
}applyFilters function · javascript · L211-L230 (20 LOC)skills/it-graph-generator/assets/template-script.js
function applyFilters() {
const checkboxes = document.querySelectorAll('#filterCheckboxes input[type="checkbox"]');
const selectedTypes = Array.from(checkboxes)
.filter(cb => cb.checked)
.map(cb => cb.value);
// Update nodes visibility
allNodes.forEach(node => {
const shouldShow = selectedTypes.includes(node.type);
nodes.update({ id: node.id, hidden: !shouldShow });
});
// Update edges visibility based on visible nodes
allEdges.forEach(edge => {
const fromNode = nodes.get(edge.from);
const toNode = nodes.get(edge.to);
const shouldShow = !fromNode.hidden && !toNode.hidden;
edges.update({ id: edge.id, hidden: !shouldShow });
});
}searchNodes function · javascript · L232-L271 (40 LOC)skills/it-graph-generator/assets/template-script.js
function searchNodes() {
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
if (!searchTerm) {
// Reset view
network.fit();
return;
}
// Find matching nodes
const matchingNodes = allNodes.filter(node => {
// Search in label
if (node.label.toLowerCase().includes(searchTerm)) return true;
// Search in properties
if (node.properties) {
return Object.values(node.properties).some(value =>
String(value).toLowerCase().includes(searchTerm)
);
}
return false;
});
if (matchingNodes.length > 0) {
// Focus on the first matching node
const nodeId = matchingNodes[0].id;
network.focus(nodeId, {
scale: 1.5,
animation: {
duration: 1000,
easingFunction: 'easeInOutQuad'
}
});
network.selectNodes([nodeId]);
displayNodeDetselectAllFilters function · javascript · L273-L277 (5 LOC)skills/it-graph-generator/assets/template-script.js
function selectAllFilters() {
const checkboxes = document.querySelectorAll('#filterCheckboxes input[type="checkbox"]');
checkboxes.forEach(cb => cb.checked = true);
applyFilters();
}unselectAllFilters function · javascript · L279-L283 (5 LOC)skills/it-graph-generator/assets/template-script.js
function unselectAllFilters() {
const checkboxes = document.querySelectorAll('#filterCheckboxes input[type="checkbox"]');
checkboxes.forEach(cb => cb.checked = false);
applyFilters();
}setupEventListeners function · javascript · L285-L292 (8 LOC)skills/it-graph-generator/assets/template-script.js
function setupEventListeners() {
// Enter key for search
document.getElementById('searchInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
searchNodes();
}
});
}download_icons function · python · L57-L118 (62 LOC)src/get-free-network-icons/download-icons-working.py
def download_icons():
"""Download all icons with proper error handling and rate limiting"""
os.makedirs(OUTPUT_DIR, exist_ok=True)
success_count = 0
fail_count = 0
failed_icons = []
print(f"Starting download of {len(icons)} icons...")
print(f"Output directory: {OUTPUT_DIR}")
print(f"Rate limit: {DELAY_BETWEEN_REQUESTS}s between requests\n")
for i, (url, fname) in enumerate(icons, 1):
print(f"[{i}/{len(icons)}] Downloading {fname}...", end=" ")
try:
# Make the request
r = requests.get(url, headers=headers, timeout=10)
if r.ok:
# Save the file
filepath = os.path.join(OUTPUT_DIR, fname)
with open(filepath, "wb") as f:
f.write(r.content)
size_kb = len(r.content) / 1024
print(f"✅ ({size_kb:.1f} KB)")
success_count += 1
else:
print(f"❌ HTTP {r.status_code}")download_icons function · python · L102-L165 (64 LOC)src/get-free-network-icons/download-it-device-icons.py
def download_icons():
"""Download IT device and management icons"""
os.makedirs(OUTPUT_DIR, exist_ok=True)
success_count = 0
fail_count = 0
failed_icons = []
print(f"🔽 Downloading {len(all_icons)} IT device & management icons...")
print(f"📁 Output directory: {OUTPUT_DIR}")
print(f"⏱️ Rate limit: {DELAY_BETWEEN_REQUESTS}s between requests")
print(f"📜 License: MIT (Bootstrap Icons) + CC0 (Simple Icons)\n")
for i, (url, fname) in enumerate(all_icons, 1):
print(f"[{i:2d}/{len(all_icons)}] {fname:30s}", end=" ")
try:
r = requests.get(url, headers=headers, timeout=10)
if r.ok:
filepath = os.path.join(OUTPUT_DIR, fname)
with open(filepath, "wb") as f:
f.write(r.content)
size_kb = len(r.content) / 1024
print(f"✅ ({size_kb:4.1f} KB)")
success_count += 1
else:
print(f"❌ HTTP {r.statusAll rows scored by the Repobility analyzer (https://repobility.com)
download_icons function · python · L67-L129 (63 LOC)src/get-free-network-icons/download-network-diagram-icons.py
def download_icons():
"""Download network diagram icons"""
os.makedirs(OUTPUT_DIR, exist_ok=True)
success_count = 0
fail_count = 0
failed_icons = []
print(f"🔽 Downloading {len(icons)} network diagram icons...")
print(f"📁 Output directory: {OUTPUT_DIR}")
print(f"⏱️ Rate limit: {DELAY_BETWEEN_REQUESTS}s between requests")
print(f"📜 License: Font Awesome Free License + Simple Icons CC0\n")
for i, (url, fname) in enumerate(icons, 1):
print(f"[{i:2d}/{len(icons)}] {fname:25s}", end=" ")
try:
r = requests.get(url, headers=headers, timeout=10)
if r.ok:
filepath = os.path.join(OUTPUT_DIR, fname)
with open(filepath, "wb") as f:
f.write(r.content)
size_kb = len(r.content) / 1024
print(f"✅ ({size_kb:4.1f} KB)")
success_count += 1
else:
print(f"❌ HTTP {r.status_code}")
fdownload_icons function · python · L103-L164 (62 LOC)src/get-free-network-icons/download-network-icons.py
def download_icons():
"""Download all icons with error handling and rate limiting"""
os.makedirs(OUTPUT_DIR, exist_ok=True)
success_count = 0
fail_count = 0
failed_icons = []
print(f"🔽 Downloading {len(icons)} IT infrastructure icons...")
print(f"📁 Output directory: {OUTPUT_DIR}")
print(f"⏱️ Rate limit: {DELAY_BETWEEN_REQUESTS}s between requests")
print(f"📜 License: CC0 (Public Domain) from Simple Icons\n")
for i, (url, fname) in enumerate(icons, 1):
print(f"[{i:2d}/{len(icons)}] {fname:25s}", end=" ")
try:
r = requests.get(url, headers=headers, timeout=10)
if r.ok:
filepath = os.path.join(OUTPUT_DIR, fname)
with open(filepath, "wb") as f:
f.write(r.content)
size_kb = len(r.content) / 1024
print(f"✅ ({size_kb:4.1f} KB)")
success_count += 1
else:
print(f"❌ HTTP {r.status_codeTextbookAnalyzer class · python · L10-L278 (269 LOC)src/site-metrics/advanced-metrics.py
class TextbookAnalyzer:
"""Analyzes the quality metrics of an intelligent textbook built with mkdocs-material."""
def __init__(self, repo_path: str):
self.repo_path = Path(repo_path)
self.docs_path = self.repo_path / 'docs'
self.mkdocs_path = self.repo_path / 'mkdocs.yml'
def analyze(self) -> Dict[str, Any]:
"""Performs comprehensive analysis of the textbook."""
metrics = {
'basic_metrics': self._get_basic_metrics(),
'content_structure': self._analyze_content_structure(),
'engagement_features': self._analyze_engagement_features(),
'technical_quality': self._analyze_technical_quality()
}
return metrics
def _count_words(self) -> int:
"""Counts words in markdown files, excluding code blocks and metadata."""
if not self.docs_path.exists():
print(f"Warning: Docs directory not found at {self.docs_path}")
return 0
__init__ method · python · L13-L16 (4 LOC)src/site-metrics/advanced-metrics.py
def __init__(self, repo_path: str):
self.repo_path = Path(repo_path)
self.docs_path = self.repo_path / 'docs'
self.mkdocs_path = self.repo_path / 'mkdocs.yml'analyze method · python · L18-L26 (9 LOC)src/site-metrics/advanced-metrics.py
def analyze(self) -> Dict[str, Any]:
"""Performs comprehensive analysis of the textbook."""
metrics = {
'basic_metrics': self._get_basic_metrics(),
'content_structure': self._analyze_content_structure(),
'engagement_features': self._analyze_engagement_features(),
'technical_quality': self._analyze_technical_quality()
}
return metrics_count_words method · python · L28-L64 (37 LOC)src/site-metrics/advanced-metrics.py
def _count_words(self) -> int:
"""Counts words in markdown files, excluding code blocks and metadata."""
if not self.docs_path.exists():
print(f"Warning: Docs directory not found at {self.docs_path}")
return 0
total_words = 0
md_files = list(self.docs_path.glob('**/*.md'))
for md_file in md_files:
try:
with open(md_file, 'r', encoding='utf-8') as f:
content = f.read()
# Process content to remove non-word elements
# Remove YAML front matter
content = re.sub(r'\A---[\s\S]*?---', '', content)
# Remove code blocks
content = re.sub(r'```[\s\S]*?```', '', content)
# Remove inline code
content = re.sub(r'`[^`]*`', '', content)
# Remove HTML tags
content = re.sub(r_count_microsims method · python · L66-L75 (10 LOC)src/site-metrics/advanced-metrics.py
def _count_microsims(self) -> int:
"""Counts MicroSim implementations."""
sims_dir = self.docs_path / 'sims'
try:
if not sims_dir.exists():
return 0
return len([d for d in sims_dir.glob('*') if d.is_dir() and not d.name.startswith('_')])
except Exception as e:
print(f"Warning: Error counting microsims: {str(e)}")
return 0_count_glossary_terms method · python · L77-L89 (13 LOC)src/site-metrics/advanced-metrics.py
def _count_glossary_terms(self) -> int:
"""Counts glossary terms."""
try:
glossary_path = self.docs_path / 'glossary.md'
if not glossary_path.exists():
return 0
with open(glossary_path, 'r', encoding='utf-8') as f:
content = f.read()
terms = re.findall(r'^####\s+\S.*$', content, re.MULTILINE)
return len(terms)
except Exception as e:
print(f"Warning: Error counting glossary terms: {str(e)}")
return 0Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
_get_basic_metrics method · python · L91-L100 (10 LOC)src/site-metrics/advanced-metrics.py
def _get_basic_metrics(self) -> Dict[str, int]:
"""Gets basic quantitative metrics."""
return {
'markdown_files': len(list(self.docs_path.glob('**/*.md'))),
'images': len(list(self.docs_path.glob('**/*.png'))) +
len(list(self.docs_path.glob('**/*.jpg'))),
'word_count': self._count_words(),
'microsims': self._count_microsims(),
'glossary_terms': self._count_glossary_terms()
}_analyze_content_structure method · python · L102-L115 (14 LOC)src/site-metrics/advanced-metrics.py
def _analyze_content_structure(self) -> Dict[str, Any]:
"""Analyzes the content structure and organization."""
try:
with open(self.mkdocs_path, 'r') as f:
mkdocs_config = yaml.safe_load(f)
nav_structure = mkdocs_config.get('nav', [])
except Exception:
nav_structure = []
return {
'navigation_depth': self._calculate_nav_depth(nav_structure),
'admonitions': self._count_admonitions(),
'code_examples': self._count_code_blocks()
}_calculate_nav_depth method · python · L117-L123 (7 LOC)src/site-metrics/advanced-metrics.py
def _calculate_nav_depth(self, nav: List) -> int:
"""Calculates maximum navigation depth."""
def get_depth(item) -> int:
if isinstance(item, dict):
return 1 + max((get_depth(v) for v in item.values()), default=0)
return 0
return max((get_depth(item) for item in nav), default=0)_count_admonitions method · python · L125-L135 (11 LOC)src/site-metrics/advanced-metrics.py
def _count_admonitions(self) -> int:
"""Counts admonition blocks in content."""
count = 0
for md_file in self.docs_path.glob('**/*.md'):
try:
with open(md_file, 'r', encoding='utf-8') as f:
content = f.read()
count += len(re.findall(r'!!!.*?\n', content))
except Exception:
continue
return count_count_code_blocks method · python · L137-L147 (11 LOC)src/site-metrics/advanced-metrics.py
def _count_code_blocks(self) -> int:
"""Counts code blocks in markdown files."""
count = 0
for md_file in self.docs_path.glob('**/*.md'):
try:
with open(md_file, 'r', encoding='utf-8') as f:
content = f.read()
count += len(re.findall(r'```[a-zA-Z0-9]*\n[\s\S]*?\n```', content))
except Exception:
continue
return count_analyze_engagement_features method · python · L149-L154 (6 LOC)src/site-metrics/advanced-metrics.py
def _analyze_engagement_features(self) -> Dict[str, Any]:
"""Analyzes engagement and interactive features."""
return {
'microsim_complexity': self._analyze_microsim_complexity(),
'has_analytics': self._check_analytics_config()
}_analyze_microsim_complexity method · python · L156-L187 (32 LOC)src/site-metrics/advanced-metrics.py
def _analyze_microsim_complexity(self) -> Dict[str, int]:
"""Analyzes complexity of MicroSims based on code size."""
sims_dir = self.docs_path / 'sims'
if not sims_dir.exists():
return {'simple': 0, 'medium': 0, 'complex': 0}
complexities = {'simple': 0, 'medium': 0, 'complex': 0}
for sim_dir in sims_dir.glob('*'):
if not sim_dir.is_dir():
continue
# Count files and code lines in sim directory
file_count = len(list(sim_dir.glob('**/*.*')))
code_lines = 0
for file in sim_dir.glob('**/*.js'):
try:
with open(file, 'r') as f:
code_lines += len(f.readlines())
except Exception:
continue
# Classify complexity
if code_lines < 100:
complexities['simple'] += 1
_check_analytics_config method · python · L189-L198 (10 LOC)src/site-metrics/advanced-metrics.py
def _check_analytics_config(self) -> bool:
"""Checks if Google Analytics is configured."""
try:
with open(self.mkdocs_path, 'r') as f:
config = yaml.safe_load(f)
extra = config.get('extra', {})
analytics = extra.get('analytics', {})
return bool(analytics.get('provider') and analytics.get('property'))
except Exception:
return FalseRepobility · open methodology · https://repobility.com/research/
_analyze_technical_quality method · python · L200-L205 (6 LOC)src/site-metrics/advanced-metrics.py
def _analyze_technical_quality(self) -> Dict[str, Any]:
"""Analyzes technical implementation quality."""
return {
'build_config_complete': self._verify_build_config(),
'responsive_design': self._check_responsive_design()
}_verify_build_config method · python · L207-L223 (17 LOC)src/site-metrics/advanced-metrics.py
def _verify_build_config(self) -> Dict[str, bool]:
"""Verifies completeness of mkdocs.yml configuration."""
required_fields = {
'site_name': False,
'theme': False,
'nav': False
}
try:
with open(self.mkdocs_path, 'r') as f:
config = yaml.safe_load(f)
for field in required_fields:
required_fields[field] = field in config
except Exception:
pass
return required_fields_check_responsive_design method · python · L225-L239 (15 LOC)src/site-metrics/advanced-metrics.py
def _check_responsive_design(self) -> Dict[str, bool]:
"""Checks for responsive design features."""
responsive_features = {
'mobile_navigation': False
}
try:
with open(self.mkdocs_path, 'r') as f:
config = yaml.safe_load(f)
theme_features = config.get('theme', {}).get('features', [])
responsive_features['mobile_navigation'] = 'navigation.tabs.mobile' in theme_features
except Exception:
pass
return responsive_featuresgenerate_report method · python · L241-L278 (38 LOC)src/site-metrics/advanced-metrics.py
def generate_report(self) -> str:
"""Generates a human-readable quality report."""
metrics = self.analyze()
report = []
report.append("# Intelligent Textbook Quality Report\n")
# Basic Metrics
report.append("## Basic Metrics")
for key, value in metrics['basic_metrics'].items():
report.append(f"- {key.replace('_', ' ').title()}: {value}")
# Content Structure
report.append("\n## Content Structure")
for key, value in metrics['content_structure'].items():
report.append(f"- {key.replace('_', ' ').title()}: {value}")
# Engagement Features
report.append("\n## Engagement Features")
for key, value in metrics['engagement_features'].items():
if isinstance(value, dict):
report.append(f"- {key.replace('_', ' ').title()}:")
for subkey, subvalue in value.items():
report.apmain function · python · L280-L294 (15 LOC)src/site-metrics/advanced-metrics.py
def main():
import argparse
parser = argparse.ArgumentParser(description='Analyze intelligent textbook quality metrics')
parser.add_argument('repo_path', help='Path to the textbook repository')
parser.add_argument('--format', choices=['json', 'text'], default='text',
help='Output format (default: text)')
args = parser.parse_args()
analyzer = TextbookAnalyzer(args.repo_path)
if args.format == 'json':
print(json.dumps(analyzer.analyze(), indent=2))
else:
print(analyzer.generate_report())extract_details_content function · python · L13-L67 (55 LOC)src/site-metrics/analyze-details-content.py
def extract_details_content(docs_dir: Path) -> List[Dict]:
"""Extract all <details> tag content from chapter markdown files."""
chapters_dir = docs_dir / 'chapters'
if not chapters_dir.exists():
return []
details_list = []
for chapter_dir in sorted(chapters_dir.iterdir()):
if not chapter_dir.is_dir():
continue
index_file = chapter_dir / 'index.md'
if not index_file.exists():
continue
with open(index_file, 'r', encoding='utf-8') as f:
content = f.read()
# Extract chapter title
title_match = re.search(r'^#\s+(.+)$', content, re.MULTILINE)
chapter_title = title_match.group(1) if title_match else chapter_dir.name
# Find all <details> blocks
details_pattern = r'<details>(.*?)</details>'
matches = re.finditer(details_pattern, content, re.DOTALL | re.IGNORECASE)
for match in matches:
details_ccategorize_visualization_types function · python · L70-L78 (9 LOC)src/site-metrics/analyze-details-content.py
def categorize_visualization_types(details_list: List[Dict]) -> Dict[str, List[Dict]]:
"""Group visualizations by type."""
categories = defaultdict(list)
for item in details_list:
viz_type = item['type'].lower()
categories[viz_type].append(item)
return dict(categories)assess_skill_priority function · python · L81-L131 (51 LOC)src/site-metrics/analyze-details-content.py
def assess_skill_priority(categories: Dict[str, List[Dict]], existing_sims: List[str]) -> List[Dict]:
"""
Assess priority for each visualization type based on:
- Impact: How many instances exist (more = higher impact)
- Effort: Similarity to existing MicroSims (less similar = higher effort)
"""
# Define similarity to existing MicroSims (0-10 scale, 10 = very similar)
similarity_scores = {
'microsim': 10, # Already have these
'graph-model': 9, # Very similar to graph viewer
'infographic': 8, # Similar to existing infographic template
'diagram': 6, # Medium similarity, structured layouts
'network-diagram': 6, # Similar to graph viewer but different focus
'chart': 5, # Different from current sims
'workflow': 4, # Sequential/timeline visualization
'interactive': 7, # General category, similar to existing
'timeline': 3, # Time-based, less similar
'unknown': 5, # Default midRepobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
generate_markdown_report function · python · L134-L261 (128 LOC)src/site-metrics/analyze-details-content.py
def generate_markdown_report(details_list: List[Dict],
categories: Dict[str, List[Dict]],
priorities: List[Dict],
output_file: Path) -> None:
"""Generate a markdown report of the details analysis."""
with open(output_file, 'w', encoding='utf-8') as f:
f.write("# Details Tag Content Analysis\n\n")
f.write("This report analyzes all `<details>` tags in the textbook chapters to categorize visualization types and prioritize skill development.\n\n")
f.write("## Summary Statistics\n\n")
f.write(f"- **Total `<details>` tags:** {len(details_list)}\n")
f.write(f"- **Unique visualization types:** {len(categories)}\n\n")
f.write("## Visualization Type Distribution\n\n")
f.write("| Type | Count | Percentage |\n")
f.write("|------|-------|------------|\n")
total = len(details_list)
for viz_type, items in sorted(categories.itemmain function · python · L264-L302 (39 LOC)src/site-metrics/analyze-details-content.py
def main():
"""Main function to analyze details content."""
repo_root = Path(__file__).parent.parent.parent
docs_dir = repo_root / 'docs'
if not docs_dir.exists():
print(f"Error: docs directory not found at {docs_dir}")
return 1
print("Analyzing <details> tag content...")
# Extract all details content
details_list = extract_details_content(docs_dir)
if not details_list:
print("No <details> tags found in chapters")
return 1
# Categorize by type
categories = categorize_visualization_types(details_list)
# List existing MicroSims
sims_dir = docs_dir / 'sims'
existing_sims = [d.name for d in sims_dir.iterdir() if d.is_dir()] if sims_dir.exists() else []
# Assess priorities
priorities = assess_skill_priority(categories, existing_sims)
# Generate report
output_file = docs_dir / 'details-analysis.md'
generate_markdown_report(details_list, categories, priorities, output_file)
pricount_chapters function · python · L14-L22 (9 LOC)src/site-metrics/get-ibook-metrics.py
def count_chapters(docs_dir: Path) -> int:
"""Count the number of chapters in the docs/chapters directory."""
chapters_dir = docs_dir / 'chapters'
if not chapters_dir.exists():
return 0
# Count directories that contain index.md, excluding the chapters index itself
chapters = [d for d in chapters_dir.iterdir()
if d.is_dir() and (d / 'index.md').exists()]
return len(chapters)analyze_chapters function · python · L25-L73 (49 LOC)src/site-metrics/get-ibook-metrics.py
def analyze_chapters(docs_dir: Path) -> Dict[str, any]:
"""Analyze chapter structure and content."""
chapters_dir = docs_dir / 'chapters'
if not chapters_dir.exists():
return {'count': 0, 'sections': [], 'details': []}
chapters = sorted([d for d in chapters_dir.iterdir()
if d.is_dir() and (d / 'index.md').exists()])
total_sections = 0
total_details = 0
chapter_data = []
for chapter_dir in chapters:
index_file = chapter_dir / 'index.md'
with open(index_file, 'r', encoding='utf-8') as f:
content = f.read()
# Count level 2 headers (## ) as sections
sections = len(re.findall(r'^##\s+[^#]', content, re.MULTILINE))
# Count <details> tags
details = len(re.findall(r'<details>', content, re.IGNORECASE))
# Get chapter title (first # header)
title_match = re.search(r'^#\s+(.+)$', content, re.MULTILINE)
title = title_matcount_learning_graph_concepts function · python · L76-L85 (10 LOC)src/site-metrics/get-ibook-metrics.py
def count_learning_graph_concepts(docs_dir: Path) -> int:
"""Count concepts in the learning graph CSV file."""
lg_file = docs_dir / 'learning-graph' / 'learning-graph.csv'
if not lg_file.exists():
return 0
with open(lg_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
# Subtract 1 for header row
return len(lines) - 1 if len(lines) > 1 else 0count_glossary_terms function · python · L88-L98 (11 LOC)src/site-metrics/get-ibook-metrics.py
def count_glossary_terms(docs_dir: Path) -> int:
"""Count number of level 4 headers (####) in the glossary.md file."""
glossary_path = docs_dir / 'glossary.md'
if not glossary_path.exists():
return 0
with open(glossary_path, 'r', encoding='utf-8') as f:
content = f.read()
# Count level 4 headers (####)
terms = re.findall(r'^####\s+', content, re.MULTILINE)
return len(terms)count_microsims function · python · L101-L106 (6 LOC)src/site-metrics/get-ibook-metrics.py
def count_microsims(docs_dir: Path) -> int:
"""Count number of MicroSims in the /docs/sims directory."""
sims_dir = docs_dir / 'sims'
if not sims_dir.exists():
return 0
return len([d for d in sims_dir.iterdir() if d.is_dir()])count_markdown_files function · python · L109-L111 (3 LOC)src/site-metrics/get-ibook-metrics.py
def count_markdown_files(docs_dir: Path) -> int:
"""Count total number of markdown files in the docs directory."""
return len(list(docs_dir.glob('**/*.md')))All rows scored by the Repobility analyzer (https://repobility.com)
count_images function · python · L114-L130 (17 LOC)src/site-metrics/get-ibook-metrics.py
def count_images(docs_dir: Path) -> Tuple[int, Dict[str, int]]:
"""Count total number of image files in the docs directory."""
png_count = len(list(docs_dir.glob('**/*.png')))
jpg_count = len(list(docs_dir.glob('**/*.jpg')))
jpeg_count = len(list(docs_dir.glob('**/*.jpeg')))
svg_count = len(list(docs_dir.glob('**/*.svg')))
gif_count = len(list(docs_dir.glob('**/*.gif')))
breakdown = {
'PNG': png_count,
'JPG': jpg_count,
'JPEG': jpeg_count,
'SVG': svg_count,
'GIF': gif_count
}
return sum(breakdown.values()), breakdowncount_words_in_markdown function · python · L133-L150 (18 LOC)src/site-metrics/get-ibook-metrics.py
def count_words_in_markdown(docs_dir: Path) -> int:
"""Count total number of words in all markdown files."""
total_words = 0
for md_file in docs_dir.glob('**/*.md'):
with open(md_file, 'r', encoding='utf-8') as f:
content = f.read()
# Remove code blocks
content = re.sub(r'```.*?```', '', content, flags=re.DOTALL)
# Remove inline code
content = re.sub(r'`.*?`', '', content)
# Remove URLs
content = re.sub(r'http[s]?://\S+', '', content)
# Remove HTML tags
content = re.sub(r'<.*?>', '', content)
# Split and count remaining words
words = content.split()
total_words += len(words)
return total_wordscount_prompts function · python · L153-L158 (6 LOC)src/site-metrics/get-ibook-metrics.py
def count_prompts(docs_dir: Path) -> int:
"""Count number of AI prompts in the prompts directory."""
prompts_dir = docs_dir / 'prompts'
if not prompts_dir.exists():
return 0
return len(list(prompts_dir.glob('*.md'))) - 1 # Exclude index.mdpage 1 / 2next ›