Releases: btoron/pyOFSC
v2.24.1 - Logging & Code Quality
$(cat <<'EOF'
pyOFSC v2.24.1 - Logging & Code Quality
Release Date: March 4, 2026
🎯 New Features
Request/Response Logging (#110)
Optional httpx event hooks for automatic API call tracing on the async client:
import logging
logging.basicConfig(level=logging.DEBUG)
async with AsyncOFSC(clientID="...", secret="...", companyName="...", enable_logging=True) as client:
workzones = await client.metadata.get_workzones()
# DEBUG: Request: GET https://company.fs.ocs.oraclecloud.com/rest/ofscMetadata/v1/workZones
# DEBUG: Response: GET ... 200- Logs under
ofsc.async_clientlogger - HTTP errors (4xx/5xx) logged at WARNING level
- Disabled by default with zero overhead
🐛 Bug Fixes
baseUrl validation in async modules (#150)
core/_base.py and oauth.py could return None from baseUrl, causing cryptic urljoin(None, ...) errors. Now raises ValueError("Base URL is not configured") matching the pattern in capacity.py and statistics.py.
Security: OAuth token leak removed
Removed print() statement in ofsc/models/_base.py that printed the OAuth bearer token to stdout.
🔧 Improvements
Named loggers across library code
Replaced root logger (logging.debug(...)) with named loggers (logger = logging.getLogger(__name__)) in:
ofsc/common.pyofsc/core.pyofsc/models/_base.pyofsc/async_client/__init__.py
Pagination logging downgraded to DEBUG
5 INFO-level log calls in get_all_activities and get_all_properties pagination loops downgraded to DEBUG.
Removed leftover print statements
Removed 2 debug print() calls in ofsc/core.py (create_resource_location, get_all_activities).
README improvements
Updated README with clearer usage examples for both sync and async clients.
📊 Test Coverage
- 447 tests passing (6 new logging tests)
- Zero pydantic deprecation warnings
Files Changed
| File | Changes |
|---|---|
ofsc/async_client/__init__.py |
enable_logging param, event hooks, named logger |
ofsc/async_client/core/_base.py |
baseUrl None validation |
ofsc/async_client/oauth.py |
baseUrl None validation |
ofsc/common.py |
Named logger |
ofsc/core.py |
Named logger, remove prints, DEBUG pagination |
ofsc/models/_base.py |
Named logger, remove token print |
tests/async/test_async_ofsc.py |
6 new logging tests |
README.md |
Updated usage examples and logging docs |
| EOF | |
| ) |
v2.24.0 - Async Core & Write Operations
pyOFSC v2.24.0 - Async Core & Write Operations
Release Date: March 4, 2026
Highlights: Full async Core API, Async Metadata write operations, Async Capacity API, Statistics API, OAuth Bearer token auth, Pydantic model audit
🎯 Major Features
1. Async Core API - Full Implementation
Complete async implementation of the Core API covering all major resource types:
Activities
get_activities()- List activities with comprehensive filtering (GetActivitiesParams model)get_activity()- Get single activity detailscreate_activity()- Create new activityupdate_activity()- Update activity fieldsdelete_activity()- Delete activitymove_activity()- Move activity between resources
Resources (GET + Write/Delete)
get_resources()- List resources with filtersget_resource()- Get resource detailscreate_or_replace_resource()- Create/replace resource (ResourceCreate model)update_resource()- Partial update resourcedelete_resource()- Delete resourceget_resource_workzones()/update_resource_workzones()- Workzone assignmentsget_resource_workskills()/update_resource_workskills()- Workskill assignmentsget_resource_availability()- Resource availabilityget_calendars()/get_calendar()- Calendar managementget_resource_assistants()/get_resource_plans()- Assistant and plan managementget_position_history()- Position tracking- Inventory CRUD:
get_inventories(),create_inventory(),update_inventory(),delete_inventory(),move_inventory() - File properties:
get_resource_file_property(),set_resource_file_property(),delete_resource_file_property()
Users
get_users()- List usersget_user()- Get user detailscreate_or_update_user()- Create/update user (User/UserCreate models)delete_user()- Delete userget_collaboration_groups()/get_collaboration_group()- Group management
Other Core Operations
get_events()- Event stream with filtersget_subscriptions()/create_subscription()/delete_subscription()- Event subscriptionsget_daily_extract()- Bulk data extractionget_multiday_segments()- Multiday segment retrieval (#139)- File properties (CO057G/U/D):
get_resource_file_property(),set_resource_file_property(),delete_resource_file_property()(#140)
2. Async Metadata Write Operations (#138)
22 new async write methods covering the full metadata management lifecycle:
- Activity Types:
create_or_replace_activity_type(),delete_activity_type(),create_or_replace_activity_type_group(),delete_activity_type_group() - Workzones:
create_or_replace_workzone(),delete_workzone() - Workskills:
create_or_replace_work_skill(),delete_work_skill(),create_or_replace_work_skill_group(),delete_work_skill_group() - Capacity Areas:
create_or_replace_capacity_area(),delete_capacity_area(), plus 8 sub-resource write methods (#137) - Capacity Categories:
create_or_replace_capacity_category(),delete_capacity_category() - Time Slots:
create_or_replace_time_slot(),delete_time_slot() - Non-Working Reasons:
create_or_replace_non_working_reason(),delete_non_working_reason() - Shifts:
create_or_replace_shift()(with ShiftUpdate model),delete_shift() - Link Templates:
create_link_template(),delete_link_template() - Organizations:
create_or_replace_organization(),delete_organization()
3. Async Capacity API (#128)
Complete async implementation with 10 methods:
get_available_capacity()- Query available capacityget_quota()- Get quota information- Plus 8 additional capacity query methods
- Fixed OAuth token caching bug affecting capacity requests
- Added
determineAreaByWorkZoneparameter support
4. Statistics API - GET Operations (#141)
New async Statistics API module:
get_statistics()- Retrieve statistics data- PATCH serialization support with correct response model
- Full Pydantic model validation (
ofsc/models/statistics.py)
5. OAuth Bearer Token Auth for Async Client (#146)
get_token()- Async OAuth token retrieval via v2 endpoint (AU002P)- Bearer token authentication mode for async client
- Immutable
OFSOAuthRequestmodel - Token caching fix for async requests
6. Pydantic Model Audit (#149)
Comprehensive audit and tightening of all Pydantic models:
- Added
extra="allow"to models accepting arbitrary OFSC fields - Improved validation consistency across all model classes
- Updated
model_dump()calls to includemodeparameter for correct serialization - Enhanced
ResourceWorkzoneAssignmentfields for additional metadata - Fixed
MultidaySegmentListResponse(dedicated model without totalResults)
🏗️ Architecture Improvements
Async Core as Package with Mixins
The ofsc/async_client/core/ was refactored into a proper Python package:
core/__init__.py- ComposesAsyncOFSCorefrom mixinscore/_base.py- Base activities, events, daily extract, subscriptionscore/users.py-AsyncOFSCoreUsersMixinwith user managementcore/resources.py-AsyncOFSCoreResourcesMixinwith full resource operationscore/inventories.py- Standalone inventory CRUD
Model Files Organization
Models extracted into dedicated files:
ofsc/models/_base.py- Base types (OFSResponseList, etc.)ofsc/models/resources.py- 35+ resource modelsofsc/models/users.py- User and collaboration group modelsofsc/models/metadata.py- Metadata models (430+ lines)ofsc/models/statistics.py- Statistics API modelsofsc/models/capacity.py- Capacity API models
🔧 Code Quality
Ruff Integration
- Added
ruffas a project dependency with full configuration - Applied ruff formatting to the entire codebase
- Configured linting rules in
pyproject.toml
CI/CD Improvements
- Added GitHub Actions workflow for automated release tagging (
.github/workflows/release.yml) - Fixed 51 mocked tests to use
mock_instanceinstead ofasync_instance - Added
uses_local_datamarker to separate saved-response tests from CI - Added
uses_real_datamarkers to properly exclude live tests from CI - Parallel test execution support (#130)
Exception Handling
- Enhanced exception handling and response validation across all API interactions
- Improved error propagation for network and HTTP errors
🧪 Testing
New Test Files
tests/async/test_async_metadata_write.py- 772 lines, write operation teststests/async/test_async_metadata_roundtrip.py- 714 lines, round-trip validationtests/async/test_metadata_model_audit.py- 682 lines, model audit teststests/async/test_async_statistics.py- 1,030 lines, Statistics API teststests/async/test_async_oauth.py- 189 lines, OAuth teststests/async/test_async_populate_status.py- 152 lines, populate status teststests/REQUIREMENTS_TEST_MAPPING.md- Requirement-to-test mappingtests/TEST_EXECUTION_SUMMARY.md- Execution summary report
Test Coverage
- Full mocked test suite runnable without API credentials
- Live integration tests with
@pytest.mark.uses_real_data - Cross-API validation tests for capacity areas and resource workzones
🐛 Bug Fixes
- OAuth token caching - Fixed bug in async client token refresh
- MultidaySegmentListResponse - Dedicated model without totalResults
- Link template tests - Fixed to assert label in request body, not URL
- Link template creation - Updated to use simultaneous type and UUID for labels
- Workskill methods - Added missing model parameters and improved response handling
- Resource methods - Added required params to
get_calendars(),get_resource_assistants(),get_resource_plans() - Daily extract tests - Skip tests requiring dedicated instance credentials
- Token type casing - Standardized to lowercase in OAuth tests
📦 Dependency Updates
| Package | From | To |
|---|---|---|
| cryptography | 44.0.1 | 46.0.5 |
| requests | 2.32.3 | 2.32.4 |
| urllib3 | 2.2.3 | 2.6.3 |
📊 Statistics
| Metric | Count |
|---|---|
| Issues Resolved | #117, #118, #120, #121, #122, #124, #125, #126, #128, #130, #137, #138, #139, #140, #141, #146, #149 |
| API Methods Added | 60+ |
| Models Created/Updated | 30+ |
| Test Files Created | 8 |
| Net Lines Added | +9,460 |
🔄 Migration Guide
Breaking Changes
None - All changes are backward compatible.
New Features Usage
from ofsc import AsyncOFSC
async with AsyncOFSC(
clientID="your_client_id",
companyName="your_company",
secret="your_secret"
) as client:
# Core API - Activities
activities = await client.core.get_activities(date="2026-03-04")
# Core API - Resources (full CRUD)
resources = await client.core.get_resources()
await client.core.create_or_replace_resource(resource_data)
inventories = await client.core.get_inventories("resource_id")
# Core API - Users
users = await client.core.get_users()
await client.core.create_or_update_user(user_data)
# Metadata Write Operations
await client.metadata.create_or_replace_workzone(workzone)
await client.metadata.delete_workzone("ZONE_LABEL")
# Capacity API
capacity = await client.capacity.get_available_capacity(...)
# Statistics API
stats = await client.statistics.get_statistics(...)
# OAuth - Bearer Token
token = await client.oauth2.get_token()🙏 Contributors
- Borja Toron (@btoron) - Primary development
- Claude Sonnet 4.5/4.6 - AI pair programming assistance
🔗 Links
v2.21.0 - Async Metadata API Phase 2
pyOFSC v2.21.0 - Async Metadata API Phase 2
Release Date: December 30, 2025
Highlights: 9 new async API modules, 31 methods, comprehensive type safety improvements
🎯 Major Features
Async Metadata API - Phase 2 Implementation
This release completes Phase 2 of the async metadata API implementation, adding async support for 9 critical OFSC metadata modules.
1. Applications API
get_applications()- List all applicationsget_application(label)- Get application detailsget_application_api_accesses(label)- List API accessesget_application_api_access(label, accessId)- Get specific API access
2. Capacity Areas API
get_capacity_areas()- List all capacity areasget_capacity_area(label)- Get capacity area details
3. Capacity Categories API
get_capacity_categories(offset, limit)- List with paginationget_capacity_category(label)- Get category details
4. Forms API
get_forms(offset, limit)- List forms with paginationget_form(label)- Get form details
5. Map Layers API
get_map_layers(offset, limit)- List map layers with paginationget_map_layer(label)- Get map layer details
6. Plugins API
import_plugin_file(file_path)- Import plugin from XML fileimport_plugin(plugin_xml)- Import plugin from XML string
7. Routing Profiles API (Full CRUD)
get_routing_profiles(offset, limit)- List routing profilesget_routing_plans(profile_label, offset, limit)- List plansexport_routing_plan(profile_label, plan_label)- Export planimport_routing_plan(profile_label, plan_label, data)- Import planreplace_routing_plan(profile_label, plan_label, data)- Replace plancreate_routing_plan(profile_label, plan_label, data)- Create plandelete_routing_plan(profile_label, plan_label)- Delete plan
8. Shifts API
get_shifts(offset, limit)- List shifts with paginationget_shift(label)- Get shift details
9. Work Skills API (Full CRUD)
get_work_skills(offset, limit)- List work skillsget_work_skill(label)- Get work skill detailscreate_or_replace_work_skill(workskill)- Create/update work skilldelete_work_skill(label)- Delete work skillget_work_skill_conditions()- List all conditionsget_work_skill_groups(offset, limit)- List groupsget_work_skill_group(label)- Get group detailscreate_or_replace_work_skill_group(group)- Create/update groupdelete_work_skill_group(label)- Delete group
🔧 Code Quality Improvements
Type Safety Enhancements
- ✅ Replaced
List[]withlist[]- 70 occurrences updated for modern Python typing - ✅ Added pyright to dev dependencies - Static type checking support
- ✅ Fixed all pyright errors - Reduced from 13 errors to 0
- ✅ Added type: ignore annotations - 7 RootModel classes properly annotated
Model Improvements
- ✅ Fixed duplicate Link class - Renamed to
DailyExtractLink - ✅ Made Application fields Optional - Proper type safety
- ✅ Fixed baseUrl property - Correct type annotation
- ✅ Removed invalid
__next__()methods - From response list classes - ✅ Fixed enum type mismatch - In Recurrence model
- ✅ Added None checks - For optional member access and iterables
- ✅ Fixed Property.translations - Changed to
Optional[TranslationList]
Documentation
- ✅ PEP 287 docstrings - All async metadata methods converted to reStructuredText format
- ✅ New scripts/README.md - 504 lines documenting utility scripts
- ✅ Updated ENDPOINTS.md - Current implementation status for all 243 endpoints
Code Organization
- ✅ Reorganized models.py - Application and Routing Profile models in alphabetical regions
- ✅ Deleted empty regions - 4 regions removed for cleaner code
- ✅ Consistent naming - Fixed Workskill/WorkskillGroup inconsistencies
🧪 Testing
New Test Coverage
- 9 new test files with 3,340 lines
- 100+ test cases covering all new functionality
- 50+ saved API responses for validation testing
- All tests passing: 266/266 ✅
Test Structure
- Live tests with
@pytest.mark.uses_real_data - Model validation tests
- Saved response validation tests
- Error handling tests (404, authentication, etc.)
- Comprehensive CRUD operation tests
Coverage Metrics
- Overall: 84% (exceeds 80% target)
- async_client/metadata.py: 95%
- models.py: 87%
- exceptions.py: 100%
🐛 Bug Fixes
Critical Fixes
- RoutingPlanData Validation - Made fields optional for varied API responses
- Optional Member Access - Added None checks before calling methods
- Import Naming - Fixed WorkskillConditionList typo
- Type Annotations - Standardized across all models
Minor Fixes
- Updated capacity area validation test label
- Updated Status enum usage in CapacityArea and Workzone
- Corrected endpoint statuses in documentation
- Removed deprecated routing profiles capture script
📦 New Dependencies
Development
pyright>=1.1.390,<2- Static type checker for Python
📊 Statistics
| Metric | Count |
|---|---|
| Issues Resolved | 9 (#92, #93, #94, #95, #99, #102, #105, #106, #108) |
| API Methods Added | 31 |
| Models Created/Updated | 18 |
| Test Files Created | 9 |
| Test Cases Added | 100+ |
| Saved Responses | 50+ |
| Documentation Lines | 1,050+ |
| Net Lines Added | +5,253 |
🔄 Migration Guide
Breaking Changes
None - All changes are backward compatible.
New Features Usage
from ofsc import AsyncOFSC
async with AsyncOFSC(
clientID="your_client_id",
companyName="your_company",
secret="your_secret"
) as client:
# Applications API
apps = await client.metadata.get_applications()
app = await client.metadata.get_application("app_label")
# Routing Profiles API
profiles = await client.metadata.get_routing_profiles()
plans = await client.metadata.get_routing_plans("profile_label")
plan_data = await client.metadata.export_routing_plan(
"profile_label", "plan_label"
)
# Work Skills API (CRUD)
skills = await client.metadata.get_work_skills()
skill = await client.metadata.get_work_skill("skill_label")
await client.metadata.create_or_replace_work_skill(workskill)
await client.metadata.delete_work_skill("skill_label")
# All other new APIs follow the same pattern
forms = await client.metadata.get_forms()
shifts = await client.metadata.get_shifts()
capacity_areas = await client.metadata.get_capacity_areas()Type Checking Support
# Models now fully support modern Python typing
from ofsc.models import (
Application,
RoutingProfileList,
Workskill,
Form
)
# Use with mypy or pyright for static type checking
apps: list[Application] = [] # Modern syntax supported📝 Documentation
New Documentation
- scripts/README.md - Comprehensive guide for utility scripts
capture_api_responses.py- How to capture API responsesupdate_endpoints_doc.py- How to update endpoint documentation- Usage examples, best practices, troubleshooting
Updated Documentation
- ENDPOINTS.md - Current implementation status (89 sync, 53 async endpoints)
- All async metadata docstrings - Now in PEP 287 reStructuredText format
🚀 What's Next
Phase 3 (Upcoming)
- Remaining metadata endpoints (see issue #109)
- Additional CRUD operations
- Batch operations support
Future Releases
- Async Core API implementation
- Async Capacity API implementation
- OAuth token-based authentication for async client
- Enhanced error handling and retries
- Rate limiting support
🙏 Contributors
- Borja Toron (@btoron) - Primary development
- Claude Sonnet 4.5 - AI pair programming assistance
📋 Full Changelog
Features
- feat: implement async routing profiles API methods and add tests (86db72c)
- feat: implement async methods for capacity areas and add comprehensive tests (3448768)
- feat: implement async methods for capacity categories and add comprehensive tests (3dc52d1)
- feat: implement async methods for map layers and add comprehensive tests (aafcf98)
- feat: implement async methods for forms and add comprehensive tests (503377d)
- feat: implement async methods for shifts and add comprehensive tests (9671b40)
- feat: implement async methods for work skills and conditions with comprehensive tests (f87e963)
- feat: Add async applications and plugins API implementation (5db8cf1)
- feat: Add comprehensive README for utility scripts (5ead113)
- feat: add endpoints for work skills in API capture script (52bdbbc)
- feat: add live and model tests for CRUD operations (a7fd98b)
- feat: Update RoutingPlanData model for optional fields (f48d935)
- feat: Refactor metadata models for applications and routing profiles (5383307)
- feat: Add pyright to development dependencies (b9ad391)
- feat: update status fields to use Status enum (f2a77d0)
Bug Fixes
- fix: correct naming inconsistencies for Workskill and WorkskillGroup (7018dfa)
- fix: Correct typo in import statement for WorkskillConditionList (6e64aaa)
- fix: Update endpoint statuses in documentation (5557984)
- test: update expected label for capacity area validation (ff18bed)
Refactoring
- refactor: Replace List with built-in list type (202049d)
- refactor: Simplify model classes and update type hints (de8086f)
- refactor: Docstrings to use reStructuredText (35440fd)
- feat: remove deprecated routing profiles capture script (e2f569e)
Version Updates
- chore: Update version to 2.21.0 (01dfffe)
🔗 Links
Async Client
What's Changed
Full Changelog: v2.18.1...v2.19
v2.18.1
v2.18
v2.17
v2.15
v2.14.1
v2.14
Summary
This release adds comprehensive capacity management functionality to pyOFSC, including:
- New
OFSCapacitymodule for capacity and quota operations - Support for both
getAvailableCapacityandgetQuotaendpoints - Flexible parameter handling with CsvList support
- Generic internal converter function using type inspection
Major Changes
1. New Capacity Module (ofsc/capacity.py)
getAvailableCapacity(): Get available capacity with individual parameters- Supports list, CSV string, and CsvList inputs for areas, dates, categories
- Consistent with
getQuotasignature pattern
getQuota(): Get quota information with flexible parameters- Boolean parameters automatically converted to lowercase strings
- All string array parameters support multiple input formats
_convert_model_to_api_params(): Internal generic converter using inspection- Automatically detects CsvList and boolean fields
- No hardcoded field names - works with any Pydantic model
- DRY implementation shared by both public methods
2. Enhanced Models (ofsc/models.py)
CsvList: Utility model for comma-separated string listsfrom_list()andto_list()conversion methods- Automatic conversion in field validators
CapacityRequest: Updated to use CsvList fields with validatorsGetQuotaRequest: Request model with comprehensive parametersGetQuotaResponseandQuotaAreaItem: Response models for quota data- All models support flexible input formats (list[str], str, CsvList)
3. Module Integration
- Capacity module integrated as top-level API alongside core and metadata
- Access via
ofsc_instance.capacity.getAvailableCapacity()andofsc_instance.capacity.getQuota()
4. Comprehensive Test Suite
- 30+ mocked tests covering all functionality
- 19+ integration tests for real server validation
- Real server responses captured for consistent mocking
- Tests for all input formats and edge cases
- Performance tests included
API Examples
# Get available capacity
response = ofsc.capacity.getAvailableCapacity(
dates=["2025-06-25", "2025-06-26"],
areas=["FLUSA", "CAUSA"],
categories=["EST", "RES"]
)
# Get quota information
response = ofsc.capacity.getQuota(
dates="2025-06-25",
areas=["FLUSA", "CAUSA"],
aggregateResults=True,
categoryLevel=False
)
Breaking Changes
- getAvailableCapacity signature changed from accepting CapacityRequest object to individual parameters
- This aligns with the getQuota pattern for consistency
Testing
- All tests passing (30 mocked, 19 integration)
- Tested with real OFSC server using FLUSA/CAUSA areas
- Performance validated with week-long date ranges