PHPackages                             as-cornell/as\_courses - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. as-cornell/as\_courses

ActiveDrupal-custom-module[Utility &amp; Helpers](/categories/utility)

as-cornell/as\_courses
======================

Provides lists of courses pulled from the Cornell course roster API by subject and semester.

v2.0.0(2mo ago)1410↓100%GPL-3.0-or-laterPHP

Since Oct 17Pushed 2mo ago2 watchersCompare

[ Source](https://github.com/as-cornell/as_courses)[ Packagist](https://packagist.org/packages/as-cornell/as_courses)[ Docs](https://communications.as.cornell.edu)[ RSS](/packages/as-cornell-as-courses/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (6)DependenciesVersions (7)Used By (0)

[![Latest Stable Version](https://camo.githubusercontent.com/0705042167c9b7561389f58ced3a8224a4adc073743238d7853656cdbe7a03d4/68747470733a2f2f706f7365722e707567782e6f72672f61732d636f726e656c6c2f61735f636f75727365732f76)](https://packagist.org/packages/as-cornell/as_courses)

AS COURSES (as\_courses)
========================

[](#as-courses-as_courses)

INTRODUCTION
------------

[](#introduction)

A Drupal module that provides lists of courses pulled from the Cornell course roster API (classes.cornell.edu) by subject and semester. Built with service-oriented architecture and dependency injection for Drupal 10/11 best practices.

### Features

[](#features)

- **Dynamic Semester Generation** - Semesters auto-generate based on current year (no more manual updates!)
- **Service-Based API Client** - Proper HTTP client with error handling and logging
- **Twig Functions** - Multiple Twig functions for displaying course data in templates
- **Courses Controller** - Route to display all courses for selected subjects/semesters
- **Settings Form** - Configure semesters, subjects, and defaults via admin UI
- **Fully Testable** - Comprehensive unit and functional tests
- **Cached Responses** - API responses cached for performance (configurable duration)

### Architecture (v2.0+)

[](#architecture-v20)

- **Services**: Core business logic in injectable services
    - `CoursesApiService` - Cornell Classes API client
    - `SemesterGeneratorService` - Dynamic semester list generation
- **Dependency Injection**: All components use proper DI (no static calls)
- **Error Handling**: Comprehensive logging via dedicated logger channel
- **Configuration**: Schema-based configuration with install defaults

REQUIREMENTS
------------

[](#requirements)

- Drupal 10.0 or higher
- PHP 8.1 or higher
- Internet access to Cornell Classes API (classes.cornell.edu)

INSTALLATION
------------

[](#installation)

Install as you would normally install a contributed Drupal module. See [Installing Drupal Modules](https://www.drupal.org/docs/extending-drupal/installing-drupal-modules)for further information.

```
# Via Composer (if published to Packagist)
composer require as-cornell/as_courses

# Or install manually
# 1. Download/clone to web/modules/custom/as_courses
# 2. Enable the module
drush en as_courses -y
```

CONFIGURATION
-------------

[](#configuration)

### Admin Settings

[](#admin-settings)

Configure the module at: `/admin/config/content/as_courses`

**Settings**:

- **Semesters to display** - Select which semesters appear in tabs (checkboxes)
- **Default semester** - Which semester loads at `/courses` route
- **Course Prefixes** - Comma-separated list (e.g., `PSYCH,ECON,HIST`)

### Advanced Configuration

[](#advanced-configuration)

Edit `/admin/config/development/configuration` or add to `settings.php`:

```
// Adjust semester generation range
$config['as_courses.settings']['semester_start_year_offset'] = -1;  // Current year - 1
$config['as_courses.settings']['semester_end_year_offset'] = 5;     // Current year + 5

// API timeout (seconds)
$config['as_courses.settings']['api_timeout'] = 10;

// Cache duration (seconds)
$config['as_courses.settings']['cache_duration'] = 3600;  // 1 hour
```

USAGE
-----

[](#usage)

### Twig Functions

[](#twig-functions)

All Twig functions available in templates:

**1. Display Courses by Subject**

```
{# Get courses for Spring 2026, PSYCH subject, show 5 courses, random order #}
{% set courses = parse_courses_json('SP26', 'PSYCH', 5, 'random') %}
{% for course in courses %}
  {{ course.subject }} {{ course.number }}: {{ course.title }}
  {{ course.description }}
{% endfor %}
```

**2. Display Courses by Instructor (NetID)**

```
{# Get courses taught by NetID abc123 in Fall 2026 #}
{% set courses = parse_person_courses_netid_json('FA26', 'abc123') %}
{% for course in courses %}
  {{ course.subject }} {{ course.number }}: {{ course.title }}
{% endfor %}
```

**3. Get Current Semesters**

```
{# Get list of selected semesters from admin settings #}
{% set semesters = get_current_semesters() %}
{% for code in semesters %}
  {{ parse_semester_name(code) }}
{% endfor %}
```

**4. Format Semester Names**

```
{# Convert semester code to readable name #}
{{ parse_semester_name('SP26') }}  {# Output: Spring 2026 #}
{{ parse_semester_name('FA25') }}  {# Output: Fall 2025 #}
```

**5. Person's Courses by Subject**

```
{# Get courses for NetID xyz456 in PSYCH subject, Spring 2026 #}
{% set courses = parse_person_courses_json('SP26', 'PSYCH', 'xyz456') %}
```

**6. Person's Courses Filtered by Subjects**

```
{# Get NetID courses filtered to specific subject list #}
{% set subjects = ['PSYCH', 'ECON'] %}
{% set courses = parse_person_courses_netid_prefix_json('SP26', subjects, 'abc123') %}
```

### Using Services in Custom Code

[](#using-services-in-custom-code)

**API Service**:

```
// Get courses by subject
$courses_api = \Drupal::service('as_courses.api');
$courses = $courses_api->getCoursesBySubject('SP26', 'PSYCH,ECON');

// Get courses by instructor
$courses = $courses_api->getCoursesByInstructor('FA25', 'abc123');
```

**Semester Generator Service**:

```
// Get semester options for forms
$semester_gen = \Drupal::service('as_courses.semester_generator');
$options = $semester_gen->getSemesterOptions();
// Returns: ['FA25' => 'Fall 2025', 'WI26' => 'Winter 2026', ...]

// Get current semester
$current = $semester_gen->getCurrentSemester();
// Returns: 'SP26' (based on current date)

// Format semester code
$name = $semester_gen->formatSemesterName('SP26');
// Returns: 'Spring 2026'

// Get custom range
$semesters = $semester_gen->getSemestersBetween(2025, 2027);
```

### Controller Route

[](#controller-route)

Visit `/courses` or `/courses/{semester}` to see the default courses page (uses `courses.html.twig` template).

Example:

- `/courses` - Displays default semester
- `/courses/SP26` - Displays Spring 2026 courses
- `/courses/FA25` - Displays Fall 2025 courses

TESTING
-------

[](#testing)

### Run Unit Tests

[](#run-unit-tests)

```
# From Drupal root
vendor/bin/phpunit web/modules/custom/as_courses/tests/src/Unit

# Specific test
vendor/bin/phpunit web/modules/custom/as_courses/tests/src/Unit/CoursesApiServiceTest.php
```

### Run Functional Tests

[](#run-functional-tests)

```
vendor/bin/phpunit web/modules/custom/as_courses/tests/src/Functional
```

### Manual Testing Checklist

[](#manual-testing-checklist)

- Visit `/admin/config/content/as_courses` - Form loads with dynamic semesters
- Save settings form - No errors, config saves
- Visit `/courses` - Page loads with default semester
- Visit `/courses/SP26` - Page loads with specified semester
- Use Twig functions in template - Course data displays correctly
- Check logs: `drush watchdog:show --type=as_courses` - API calls logged
- Check cache: Course data cached properly

TROUBLESHOOTING
---------------

[](#troubleshooting)

### No Courses Appearing

[](#no-courses-appearing)

1. **Check API connectivity**:

    ```
    curl "https://classes.cornell.edu/api/2.0/search/classes.json?roster=SP26&subject=PSYCH"
    ```
2. **Check module logs**:

    ```
    drush watchdog:show --type=as_courses --count=50
    ```
3. **Clear cache**:

    ```
    drush cr
    ```
4. **Verify configuration**:

    ```
    drush config:get as_courses.defaults
    drush config:get as_courses.settings
    ```

### Semester List Not Updating

[](#semester-list-not-updating)

- Dynamic semester generation added in v2.0.0
- Clear cache: `drush cr`
- Adjust year range in `as_courses.settings` config

### Deprecated Function Warnings

[](#deprecated-function-warnings)

If you see deprecation warnings:

```
as_courses_get_courses_json() is deprecated in as_courses:2.0.0...

```

Update your code to use the service:

```
// Old (deprecated)
$courses = as_courses_get_courses_json($semester, $subjects);

// New (v2.0+)
$courses = \Drupal::service('as_courses.api')->getCoursesBySubject($semester, $subjects);
```

CHANGELOG
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for version history and upgrade notes.

MAINTAINERS
-----------

[](#maintainers)

Current maintainers for Drupal 10/11:

- Mark Wilson (markewilson)

### Contributing

[](#contributing)

- Report bugs and feature requests in the issue queue
- Follow Drupal coding standards
- Include tests with code changes
- See [CHANGELOG.md](CHANGELOG.md) for architecture details

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance88

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~100 days

Recently: every ~126 days

Total

6

Last Release

65d ago

Major Versions

v1.0.4 → v2.0.02026-03-06

### Community

Maintainers

![](https://www.gravatar.com/avatar/f17dcdbbef5d086a5058ec138bc2f825149a5711e83709868f43494eef805b51?d=identicon)[markewilson](/maintainers/markewilson)

---

Top Contributors

[![markewilson](https://avatars.githubusercontent.com/u/7050965?v=4)](https://github.com/markewilson "markewilson (26 commits)")

### Embed Badge

![Health badge](/badges/as-cornell-as-courses/health.svg)

```
[![Health](https://phpackages.com/badges/as-cornell-as-courses/health.svg)](https://phpackages.com/packages/as-cornell-as-courses)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
