Skip to content

feat: add KYCAS#148

Open
ndigvijay wants to merge 1 commit into
pesu-dev:devfrom
ndigvijay:KYCAS
Open

feat: add KYCAS#148
ndigvijay wants to merge 1 commit into
pesu-dev:devfrom
ndigvijay:KYCAS

Conversation

@ndigvijay
Copy link
Copy Markdown
Contributor

📌 Description

Re-introduce Know Your Class and Section (KYCAS) data into the PESUAuth API by restoring the old get_know_your_class_and_section() helper, adapted to use the new authenticated PESU Academy endpoint POST /Academy/a/getStudentClassInfo.

The previous public endpoint (/Academy/getStudentClassInfo) was deprecated by PESU Academy and now returns 404. The new endpoint requires an authenticated session (JSESSIONID + CSRF token) that the login step already provides. This PR uses that authenticated session transparently — no additional API parameter or round-trip is needed from the user's perspective.

  • Adds know_your_class_and_section boolean parameter back to the authenticate() method (default false)
  • Returns KYCAS data under a separate know_your_class_and_section key in the response
  • The new endpoint POST /Academy/a/getStudentClassInfo requires controllerMode=370, actionType=174, and loginId=<SRN>, all of which are handled internally
  • Field filtering (fields param) applies to both profile and know_your_class_and_section independently, matching the original behavior
  • KYCAS fetch failures are non-fatal — they raise KYCASFetchError (HTTP 502) with the same error-handling pattern as other upstream failures

🧱 Type of Change

  • ✨ New feature – Adds functionality without breaking existing APIs

🧪 How Has This Been Tested?

  • Unit Tests (tests/unit/)
  • Functional Tests (tests/functional/)
  • Integration Tests (tests/integration/)
  • Manual Testing

⚙️ Test Configuration:

  • OS: macOS
  • Python: 3.14
  • Docker build tested

✅ Checklist

  • My code follows the CONTRIBUTING.md guidelines
  • I've performed a self-review of my changes
  • I've added/updated necessary comments and docstrings
  • I've updated relevant docs (README or endpoint docs)
  • No new warnings introduced
  • I've added tests to cover my changes
  • All tests pass locally (scripts/run_tests.py)
  • I've run linting and formatting (pre-commit run --all-files)
  • Docker image builds and runs correctly
  • Changes are backwards compatible (if applicable)
  • Feature flags or .env vars updated (if applicable)
  • I've tested across multiple environments (if applicable)
  • Benchmarks still meet expected performance (scripts/benchmark/benchmark_requests.py)

🛠️ Affected API Behaviour

  • app/app.py – Modified /authenticate route logic
  • app/pesu.py – Updated scraping or authentication handling

🧩 Models

  • app/models/request.py – Input validation or request schema changes
  • app/models/response.py – Authentication response formatting
  • app/models/profile.py – Profile extraction logic
  • app/models/kycas.py – New KYCAS response model

🐳 DevOps & Config

  • Dockerfile – Changes to base image or build process
  • .github/workflows/*.yaml – CI/CD pipeline or deployment updates
  • pyproject.toml / requirements.txt – Dependency version changes
  • .pre-commit-config.yaml – Linting or formatting hook changes

📊 Benchmarks & Analysis

  • scripts/benchmark_auth.py – Performance or latency measurement changes
  • scripts/analyze_benchmark.py – Benchmark result analysis changes
  • scripts/run_tests.py – Custom test runner logic or behavior updates

📸 Screenshots / API Demos

# Request
curl -X POST http://localhost:5000/authenticate \
  -H "Content-Type: application/json" \
  -d '{"username": "PESXXUGYYZZZ", "password": "***", "profile": true, "know_your_class_and_section": true}'

# Response (truncated)
{
  "status": true,
  "message": "Login successful.",
  "profile": {
    "name": "John Doe",
    "srn": "PESXXUGYYZZZ",
    ...
  },
  "know_your_class_and_section": {
    "class": "Sem-X",
    "cycle": "NA",
    "department": "Computer Science and Engineering",
    "institute_name": "PES University"
  }
}

🧠 Additional Notes

Implementation details

  • The old get_know_your_class_and_section() used BeautifulSoup; the new version uses selectolax.HTMLParser for consistency with the rest of the codebase
  • The Pydantic KYCASModel uses validation_alias="class" / serialization_alias="class" because class is a Python reserved keyword, accessed internally as class_field
  • model_dump(by_alias=True) was added in app.py to ensure class_field serializes as "class" in JSON output (no-op for ProfileModel since it has no aliases)

Files changed

File Change
app/exceptions/authentication.py Added KYCASFetchError (HTTP 502)
app/models/kycas.py New — Pydantic model for KYCAS response
app/models/__init__.py Export KYCASModel
app/models/request.py Added know_your_class_and_section: bool field
app/models/response.py Added know_your_class_and_section: KYCASModel field
app/pesu.py Added KYCAS_HEADER_TO_KEY_MAP, get_know_your_class_and_section() method, know_your_class_and_section param on authenticate(), updated DEFAULT_FIELDS
app/app.py Pass new param from request, by_alias=True in model_dump
app/docs/authenticate.py Added KYCAS request/response examples

@ndigvijay ndigvijay requested a review from a team as a code owner May 14, 2026 17:04
@ndigvijay ndigvijay requested a review from aditeyabaral May 14, 2026 17:08
Copy link
Copy Markdown
Member

@aditeyabaral aditeyabaral left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, cannot comment on whether it functionally works. Please test thoroughly once it is deployed to dev. Make the following cosmetic changes and respond to the questions I've posted.

You should also update all the documentation in the README to include this feature.

Comment thread app/docs/authenticate.py
"profile": True,
},
},
"auth_with_class_and_section": {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets rename to auth_with_kycas

Comment thread app/docs/authenticate.py
},
},
"authentication_with_kycas": {
"summary": "Authentication with KYCAS",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expand the acronym here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be too long no?

Comment thread app/docs/authenticate.py
},
"auth_with_class_and_section": {
"summary": "Authentication with KYCAS",
"description": "Authentication with Know Your Class and Section data",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a "..." (quotes) to emphasize the phrase

Comment thread app/docs/authenticate.py
},
},
"kycas_fetch_error": {
"summary": "KYCAS page fetching failed",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expand the acronym (all expansions should have quotes)

Comment thread app/docs/authenticate.py
},
},
"auth_with_class_and_section": {
"summary": "Authentication with KYCAS",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does the summary field show up? If it is user-facing, use the expansion with quotes

Comment thread app/models/kycas.py
class KYCASModel(BaseModel):
"""Model representing the Know Your Class and Section data."""

model_config = ConfigDict(populate_by_name=True)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we not use model_config = ConfigDict(strict=True) like other models?

Comment thread app/models/kycas.py
validation_alias="class",
serialization_alias="class",
title="Class",
description="Class of the user.",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix grammar. User belongs to a class, user does not have a class. Same for section, department. (follow the template for section).

Comment thread app/models/kycas.py
Comment on lines +43 to +54
cycle: str | None = Field(
None,
title="Cycle",
description="Cycle of the user.",
json_schema_extra={"example": "NA"},
)
department: str | None = Field(
None,
title="Department",
description="Department of the user.",
json_schema_extra={"example": "Computer Science and Engineering"},
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix grammar

Comment thread app/models/kycas.py
institute_name: str | None = Field(
None,
title="Institute Name",
description="Institute name of the user.",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix grammar

Comment thread app/pesu.py
username (str): The username of the user, usually their PRN/email/phone number.
password (str): The password of the user.
profile (bool, optional): Whether to fetch the profile information or not. Defaults to False.
know_your_class_and_section (bool, optional): Whether to fetch the class and section
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"...from the <> page"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants