Fix code formatting and linting issues
- Updated GitHub Actions workflow to use correct flake8 configuration - Fixed line length issues by using 88 characters as configured - Removed unused imports and trailing whitespace - Fixed f-string placeholders and unused variables - All linting checks now pass with project configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,6 @@ from .page import Page, PageCreate, PageUpdate
|
||||
__all__ = [
|
||||
"BaseModel",
|
||||
"Page",
|
||||
"PageCreate",
|
||||
"PageCreate",
|
||||
"PageUpdate",
|
||||
]
|
||||
]
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pydantic import BaseModel as PydanticBaseModel, ConfigDict
|
||||
from pydantic import BaseModel as PydanticBaseModel
|
||||
from pydantic import ConfigDict
|
||||
|
||||
|
||||
class BaseModel(PydanticBaseModel):
|
||||
"""Base model with common functionality for all data models.
|
||||
|
||||
|
||||
Provides:
|
||||
- Automatic validation via Pydantic
|
||||
- JSON serialization/deserialization
|
||||
- Field aliases for API compatibility
|
||||
- Consistent datetime handling
|
||||
"""
|
||||
|
||||
|
||||
model_config = ConfigDict(
|
||||
# Allow population by field name or alias
|
||||
populate_by_name=True,
|
||||
@@ -26,52 +27,50 @@ class BaseModel(PydanticBaseModel):
|
||||
# Allow extra fields for forward compatibility
|
||||
extra="ignore",
|
||||
# Serialize datetime as ISO format
|
||||
json_encoders={
|
||||
datetime: lambda v: v.isoformat() if v else None
|
||||
}
|
||||
json_encoders={datetime: lambda v: v.isoformat() if v else None},
|
||||
)
|
||||
|
||||
|
||||
def to_dict(self, exclude_none: bool = True) -> Dict[str, Any]:
|
||||
"""Convert model to dictionary.
|
||||
|
||||
|
||||
Args:
|
||||
exclude_none: Whether to exclude None values
|
||||
|
||||
|
||||
Returns:
|
||||
Dictionary representation of the model
|
||||
"""
|
||||
return self.model_dump(exclude_none=exclude_none, by_alias=True)
|
||||
|
||||
|
||||
def to_json(self, exclude_none: bool = True) -> str:
|
||||
"""Convert model to JSON string.
|
||||
|
||||
|
||||
Args:
|
||||
exclude_none: Whether to exclude None values
|
||||
|
||||
|
||||
Returns:
|
||||
JSON string representation of the model
|
||||
"""
|
||||
return self.model_dump_json(exclude_none=exclude_none, by_alias=True)
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "BaseModel":
|
||||
"""Create model instance from dictionary.
|
||||
|
||||
|
||||
Args:
|
||||
data: Dictionary data
|
||||
|
||||
|
||||
Returns:
|
||||
Model instance
|
||||
"""
|
||||
return cls(**data)
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str: str) -> "BaseModel":
|
||||
"""Create model instance from JSON string.
|
||||
|
||||
|
||||
Args:
|
||||
json_str: JSON string
|
||||
|
||||
|
||||
Returns:
|
||||
Model instance
|
||||
"""
|
||||
@@ -80,11 +79,11 @@ class BaseModel(PydanticBaseModel):
|
||||
|
||||
class TimestampedModel(BaseModel):
|
||||
"""Base model with timestamp fields."""
|
||||
|
||||
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
|
||||
@property
|
||||
def is_new(self) -> bool:
|
||||
"""Check if this is a new (unsaved) model."""
|
||||
return self.created_at is None
|
||||
return self.created_at is None
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Page-related data models for wikijs-python-sdk."""
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import Field, validator
|
||||
@@ -11,89 +10,89 @@ from .base import BaseModel, TimestampedModel
|
||||
|
||||
class Page(TimestampedModel):
|
||||
"""Represents a Wiki.js page.
|
||||
|
||||
|
||||
This model contains all the data for a wiki page including
|
||||
content, metadata, and computed properties.
|
||||
"""
|
||||
|
||||
|
||||
id: int = Field(..., description="Unique page identifier")
|
||||
title: str = Field(..., description="Page title")
|
||||
path: str = Field(..., description="Page path/slug")
|
||||
content: Optional[str] = Field(None, description="Page content")
|
||||
|
||||
|
||||
# Optional fields that may be present
|
||||
description: Optional[str] = Field(None, description="Page description")
|
||||
is_published: bool = Field(True, description="Whether page is published")
|
||||
is_private: bool = Field(False, description="Whether page is private")
|
||||
|
||||
|
||||
# Metadata
|
||||
tags: List[str] = Field(default_factory=list, description="Page tags")
|
||||
locale: str = Field("en", description="Page locale")
|
||||
|
||||
|
||||
# Author information
|
||||
author_id: Optional[int] = Field(None, alias="authorId")
|
||||
author_name: Optional[str] = Field(None, alias="authorName")
|
||||
author_email: Optional[str] = Field(None, alias="authorEmail")
|
||||
|
||||
# Editor information
|
||||
|
||||
# Editor information
|
||||
editor: Optional[str] = Field(None, description="Editor used")
|
||||
|
||||
|
||||
@validator("path")
|
||||
def validate_path(cls, v):
|
||||
"""Validate page path format."""
|
||||
if not v:
|
||||
raise ValueError("Path cannot be empty")
|
||||
|
||||
|
||||
# Remove leading/trailing slashes and normalize
|
||||
v = v.strip("/")
|
||||
|
||||
|
||||
# Check for valid characters (letters, numbers, hyphens, underscores, slashes)
|
||||
if not re.match(r"^[a-zA-Z0-9\-_/]+$", v):
|
||||
raise ValueError("Path contains invalid characters")
|
||||
|
||||
|
||||
return v
|
||||
|
||||
|
||||
@validator("title")
|
||||
def validate_title(cls, v):
|
||||
"""Validate page title."""
|
||||
if not v or not v.strip():
|
||||
raise ValueError("Title cannot be empty")
|
||||
|
||||
|
||||
# Limit title length
|
||||
if len(v) > 255:
|
||||
raise ValueError("Title cannot exceed 255 characters")
|
||||
|
||||
|
||||
return v.strip()
|
||||
|
||||
|
||||
@property
|
||||
def word_count(self) -> int:
|
||||
"""Calculate word count from content."""
|
||||
if not self.content:
|
||||
return 0
|
||||
|
||||
|
||||
# Simple word count - split on whitespace
|
||||
words = self.content.split()
|
||||
return len(words)
|
||||
|
||||
|
||||
@property
|
||||
def reading_time(self) -> int:
|
||||
"""Estimate reading time in minutes (assuming 200 words per minute)."""
|
||||
return max(1, self.word_count // 200)
|
||||
|
||||
|
||||
@property
|
||||
def url_path(self) -> str:
|
||||
"""Get the full URL path for this page."""
|
||||
return f"/{self.path}"
|
||||
|
||||
|
||||
def extract_headings(self) -> List[str]:
|
||||
"""Extract markdown headings from content.
|
||||
|
||||
|
||||
Returns:
|
||||
List of heading text (without # markers)
|
||||
"""
|
||||
if not self.content:
|
||||
return []
|
||||
|
||||
|
||||
headings = []
|
||||
for line in self.content.split("\n"):
|
||||
line = line.strip()
|
||||
@@ -102,15 +101,15 @@ class Page(TimestampedModel):
|
||||
heading = re.sub(r"^#+\s*", "", line).strip()
|
||||
if heading:
|
||||
headings.append(heading)
|
||||
|
||||
|
||||
return headings
|
||||
|
||||
|
||||
def has_tag(self, tag: str) -> bool:
|
||||
"""Check if page has a specific tag.
|
||||
|
||||
|
||||
Args:
|
||||
tag: Tag to check for
|
||||
|
||||
|
||||
Returns:
|
||||
True if page has the tag
|
||||
"""
|
||||
@@ -119,70 +118,70 @@ class Page(TimestampedModel):
|
||||
|
||||
class PageCreate(BaseModel):
|
||||
"""Data model for creating a new page."""
|
||||
|
||||
|
||||
title: str = Field(..., description="Page title")
|
||||
path: str = Field(..., description="Page path/slug")
|
||||
content: str = Field(..., description="Page content")
|
||||
|
||||
|
||||
# Optional fields
|
||||
description: Optional[str] = Field(None, description="Page description")
|
||||
is_published: bool = Field(True, description="Whether to publish immediately")
|
||||
is_private: bool = Field(False, description="Whether page should be private")
|
||||
|
||||
|
||||
tags: List[str] = Field(default_factory=list, description="Page tags")
|
||||
locale: str = Field("en", description="Page locale")
|
||||
editor: str = Field("markdown", description="Editor to use")
|
||||
|
||||
|
||||
@validator("path")
|
||||
def validate_path(cls, v):
|
||||
"""Validate page path format."""
|
||||
if not v:
|
||||
raise ValueError("Path cannot be empty")
|
||||
|
||||
|
||||
# Remove leading/trailing slashes and normalize
|
||||
v = v.strip("/")
|
||||
|
||||
|
||||
# Check for valid characters
|
||||
if not re.match(r"^[a-zA-Z0-9\-_/]+$", v):
|
||||
raise ValueError("Path contains invalid characters")
|
||||
|
||||
|
||||
return v
|
||||
|
||||
|
||||
@validator("title")
|
||||
def validate_title(cls, v):
|
||||
"""Validate page title."""
|
||||
if not v or not v.strip():
|
||||
raise ValueError("Title cannot be empty")
|
||||
|
||||
|
||||
if len(v) > 255:
|
||||
raise ValueError("Title cannot exceed 255 characters")
|
||||
|
||||
|
||||
return v.strip()
|
||||
|
||||
|
||||
class PageUpdate(BaseModel):
|
||||
"""Data model for updating an existing page."""
|
||||
|
||||
|
||||
# All fields optional for partial updates
|
||||
title: Optional[str] = Field(None, description="Page title")
|
||||
content: Optional[str] = Field(None, description="Page content")
|
||||
description: Optional[str] = Field(None, description="Page description")
|
||||
|
||||
|
||||
is_published: Optional[bool] = Field(None, description="Publication status")
|
||||
is_private: Optional[bool] = Field(None, description="Privacy status")
|
||||
|
||||
|
||||
tags: Optional[List[str]] = Field(None, description="Page tags")
|
||||
|
||||
|
||||
@validator("title")
|
||||
def validate_title(cls, v):
|
||||
"""Validate page title if provided."""
|
||||
if v is not None:
|
||||
if not v.strip():
|
||||
raise ValueError("Title cannot be empty")
|
||||
|
||||
|
||||
if len(v) > 255:
|
||||
raise ValueError("Title cannot exceed 255 characters")
|
||||
|
||||
|
||||
return v.strip()
|
||||
|
||||
return v
|
||||
|
||||
return v
|
||||
|
||||
Reference in New Issue
Block a user