Skip to main content

Welcome Contributors!

Thank you for your interest in contributing to Platzi Viewer! This guide will help you get started with development and ensure your contributions align with the project’s standards.

Getting Started

Prerequisites

  • Python 3.7+: Backend server and scripts
  • Modern browser: Chrome, Firefox, Safari, or Edge
  • Git: Version control
  • Google Cloud account: For Drive API access (if working with Drive features)

Development Setup

1

Clone the repository

git clone https://github.com/your-username/platzi-viewer.git
cd platzi-viewer
2

Create virtual environment

python -m venv .venv

# Windows
.venv\Scripts\activate

# Linux/Mac
source .venv/bin/activate
3

Install dependencies

pip install -r requirements.txt
4

Configure service account

  1. Create a Google Cloud project
  2. Enable Google Drive API
  3. Create a service account
  4. Download credentials as service_account.json
  5. Share Drive folder with service account email
See Google Drive Setup for details.
5

Build course cache

# First time only (takes 15-30 minutes)
python rebuild_cache_drive.py
6

Start development server

python server.py
Open http://localhost:8080 in your browser.

Development Workflow

Branch Strategy

git checkout -b feature/add-dark-mode
Branch naming convention:
  • feature/description - New features
  • fix/description - Bug fixes
  • docs/description - Documentation updates
  • refactor/description - Code refactoring
  • test/description - Test additions

Making Changes

1

Create feature branch

git checkout -b feature/my-awesome-feature
2

Make atomic commits

Commit frequently with clear messages:
git add .
git commit -m "feat: add keyboard shortcuts for video player"
3

Test your changes

  • Test in multiple browsers
  • Verify responsive design (mobile, tablet, desktop)
  • Check console for errors
  • Test with different video sizes
4

Push to your fork

git push origin feature/my-awesome-feature
5

Create Pull Request

  • Use the PR template
  • Describe changes clearly
  • Include screenshots/videos if applicable
  • Link related issues

Coding Standards

JavaScript (Frontend)

  • ES6+: Use modern JavaScript features
  • Modules: Organize code in importable modules
  • No jQuery: Pure JavaScript or modern frameworks only
  • Async/await: Prefer over promise chains
Example:
// ✅ Good
import { ApiService } from '../services/api.js';
import { state } from '../services/state.js';

class VideoPlayer {
    constructor(element) {
        this.element = element;
        this.state = state;
        this.api = ApiService;
    }
    
    async loadVideo(fileId) {
        const url = this.api.getVideoUrl(fileId);
        this.element.src = url;
    }
}

export { VideoPlayer };
// ❌ Bad
var player = $('#video');
player.attr('src', '/video/' + id);
Follow the View lifecycle pattern:
export const MyView = {
    async render() {
        // Return HTML string
        return `
            <div class="my-view">
                <h1>Hello World</h1>
            </div>
        `;
    },
    
    afterRender() {
        // Attach event listeners
        const button = document.querySelector('.my-button');
        button.addEventListener('click', this.handleClick);
    },
    
    cleanup() {
        // Remove listeners, clear timers
        const button = document.querySelector('.my-button');
        if (button) {
            button.removeEventListener('click', this.handleClick);
        }
    },
    
    handleClick(e) {
        console.log('Clicked!');
    }
};
Always handle errors gracefully:
// ✅ Good
async function loadCourses() {
    try {
        const data = await ApiService.fetchCourses();
        state.courses = data;
    } catch (error) {
        console.error('Failed to load courses:', error);
        showErrorMessage('Could not load courses. Please refresh.');
    }
}

// ❌ Bad
async function loadCourses() {
    const data = await ApiService.fetchCourses();
    state.courses = data;
}

Python (Backend)

Follow Python style guide:
  • 4 spaces for indentation
  • Snake_case for functions and variables
  • PascalCase for classes
  • Max line length: 120 characters
  • Docstrings for all functions/classes
Example:
# ✅ Good
from typing import Dict, List, Optional
import logging

logger = logging.getLogger(__name__)

def scan_courses(path: str) -> List[Dict]:
    """Scan courses directory and return course data.
    
    Args:
        path: Directory path to scan
        
    Returns:
        List of course dictionaries with metadata
        
    Raises:
        FileNotFoundError: If path does not exist
    """
    try:
        courses = []
        # Implementation...
        return courses
    except FileNotFoundError as e:
        logger.error(f"Directory not found: {path}")
        raise
    except Exception as e:
        logger.error(f"Error scanning courses: {e}")
        return []
# ❌ Bad
def scanCourses(path):
  courses=[]
  # Implementation...
  return courses
Use type annotations for better IDE support:
from typing import Dict, List, Optional, Tuple

def match_course(
    md_name: str,
    drive_names: Dict[str, str],
    threshold: float = 0.8
) -> Optional[Tuple[str, str]]:
    """Match course name to Drive folder.
    
    Returns:
        Tuple of (folder_name, folder_id) or None if no match
    """
    pass
Use Python’s logging module:
import logging

logger = logging.getLogger(__name__)

# ✅ Good
logger.info("Starting cache rebuild...")
logger.warning(f"Course not found: {course_name}")
logger.error(f"Drive API error: {error}", exc_info=True)

# ❌ Bad
print("Starting cache rebuild...")

CSS

Use Block Element Modifier naming:
/* Block */
.video-player { }

/* Element */
.video-player__controls { }
.video-player__play-button { }

/* Modifier */
.video-player--fullscreen { }
.video-player__play-button--disabled { }
Use CSS custom properties for theming:
:root {
    --color-primary: #1A73E8;
    --color-background: #1E1E1E;
    --color-text: #FFFFFF;
    --spacing-unit: 8px;
    --border-radius: 8px;
}

.button {
    background: var(--color-primary);
    padding: calc(var(--spacing-unit) * 2);
    border-radius: var(--border-radius);
}
Mobile-first approach:
/* Mobile first (default) */
.grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
}

/* Tablet */
@media (min-width: 768px) {
    .grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* Desktop */
@media (min-width: 1024px) {
    .grid {
        grid-template-columns: repeat(3, 1fr);
    }
}

Commit Message Convention

We follow Conventional Commits specification:

Format

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

Types

TypeDescriptionExample
featNew featurefeat(player): add keyboard shortcuts
fixBug fixfix(server): resolve memory leak in streaming
docsDocumentationdocs(readme): update installation steps
styleCode style changesstyle: format with black
refactorCode refactoringrefactor(api): simplify error handling
testAdd/modify teststest(player): add unit tests for controls
choreMaintenance taskschore: update dependencies
perfPerformance improvementperf(cache): optimize JSON parsing

Examples

feat(player): add picture-in-picture mode

Implement PiP API for video player allowing users
to continue watching while browsing courses.

Closes #123

Testing

Manual Testing Checklist

Before submitting a PR, test:
  • Chrome/Chromium
  • Firefox
  • Safari (macOS)
  • Edge

Unit Tests (Future)

When unit tests are implemented:
// Example test structure
describe('VideoPlayer', () => {
    it('should play video when play button is clicked', () => {
        const player = new VideoPlayer(videoElement);
        player.play();
        expect(videoElement.paused).toBe(false);
    });
    
    it('should save progress on pause', async () => {
        const player = new VideoPlayer(videoElement);
        await player.pause();
        expect(state.progress[courseId]).toBeDefined();
    });
});

Pull Request Process

PR Template

When creating a PR, include:
## Description
Brief description of changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing
- [ ] Tested in Chrome
- [ ] Tested in Firefox
- [ ] Tested on mobile
- [ ] No console errors

## Screenshots
(if applicable)

## Related Issues
Closes #123

Review Checklist

Reviewers will check:
  • Code works as expected
  • No regressions introduced
  • Handles edge cases
  • Browser compatibility verified
  • Follows style guidelines
  • Well documented
  • No code duplication
  • Maintainable and extensible
  • No negative performance impact
  • Efficient resource usage
  • No memory leaks
  • Optimized for common use case
  • No vulnerabilities introduced
  • Input validation proper
  • Sensitive data handled correctly
  • Follows security best practices

Priority Issues

Great places to start contributing:

High Priority 🔴

Fix CATEGORIES undefined

Critical error in server.py:248

Path sanitization

Security: Prevent directory traversal

Memory optimization

Reduce memory usage in video streaming

Race condition

Fix progress sync race condition

Medium Priority 🟡

  • Complete app_v2 migration: Finish moving from legacy to modular frontend
  • PWA implementation: Add service worker for offline support
  • Enhanced search: Add filters (difficulty, duration, instructor)
  • Mobile optimization: Improve touch gestures and layouts

Nice to Have 🟢

  • Theme system: Multiple color themes
  • Analytics dashboard: Learning statistics
  • Social features: Share progress with friends
  • Platzi API integration: Sync with official platform

Community Guidelines

Code of Conduct

  • Be respectful: Treat all contributors with respect
  • Be constructive: Provide helpful feedback
  • Be inclusive: Welcome developers of all skill levels
  • Be patient: Remember everyone is learning

Communication

GitHub Discussions

For questions and general discussion

GitHub Issues

For bugs and feature requests

Getting Help

  • Documentation: Check these docs first
  • Issues: Search existing issues before creating new ones
  • Discussions: Ask questions in GitHub Discussions
  • Email: [email protected] for sensitive matters

Recognition

All contributors are recognized:
  • GitHub Contributors: Automatic list on repository
  • CHANGELOG: Mentioned in release notes
  • README: Contributors section with avatars

Types of Contributions

  • Code: Features, fixes, refactoring
  • Documentation: Guides, tutorials, API docs
  • Testing: Bug reports, test cases
  • Design: UI/UX improvements, icons, themes
  • Translation: Localization to other languages

Additional Resources

Architecture Guide

Understand the project structure

Scripts Reference

Learn about development scripts

API Documentation

Backend API reference

Configuration Guide

Setup and environment variables

License

By contributing, you agree that your contributions will be licensed under the same MIT License that covers this project.
Thank you for contributing! Every contribution, no matter how small, helps make Platzi Viewer better for everyone.

Build docs developers (and LLMs) love