electiondata-my-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. electiondata_my_mcp-0.1.0/.cursor/rules/ponytail.mdc +30 -0
  2. electiondata_my_mcp-0.1.0/.cursor/rules/pytest.mdc +337 -0
  3. electiondata_my_mcp-0.1.0/.cursor/rules/uv.mdc +29 -0
  4. electiondata_my_mcp-0.1.0/.cursor/skills/query-electiondatamy-api/SKILL.md +91 -0
  5. electiondata_my_mcp-0.1.0/.cursor/skills/query-electiondatamy-api/reference.md +164 -0
  6. electiondata_my_mcp-0.1.0/.env.example +1 -0
  7. electiondata_my_mcp-0.1.0/.github/workflows/publish.yml +88 -0
  8. electiondata_my_mcp-0.1.0/.gitignore +34 -0
  9. electiondata_my_mcp-0.1.0/CHANGELOG.md +0 -0
  10. electiondata_my_mcp-0.1.0/PKG-INFO +253 -0
  11. electiondata_my_mcp-0.1.0/README.md +238 -0
  12. electiondata_my_mcp-0.1.0/RESOURCES.md +13 -0
  13. electiondata_my_mcp-0.1.0/pyproject.toml +71 -0
  14. electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/__init__.py +3 -0
  15. electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/__main__.py +7 -0
  16. electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/duckdb_lake.py +164 -0
  17. electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/prompt_loader.py +135 -0
  18. electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/prompts/query-builder-prompt.md +684 -0
  19. electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/query_validator.py +128 -0
  20. electiondata_my_mcp-0.1.0/src/electiondata_my_mcp/server.py +202 -0
  21. electiondata_my_mcp-0.1.0/tests/conftest.py +34 -0
  22. electiondata_my_mcp-0.1.0/tests/test_duckdb_lake.py +188 -0
  23. electiondata_my_mcp-0.1.0/tests/test_main.py +32 -0
  24. electiondata_my_mcp-0.1.0/tests/test_prompt_loader.py +120 -0
  25. electiondata_my_mcp-0.1.0/tests/test_query_validator.py +93 -0
  26. electiondata_my_mcp-0.1.0/tests/test_server.py +204 -0
  27. electiondata_my_mcp-0.1.0/uv.lock +1223 -0
@@ -0,0 +1,30 @@
1
+ # Ponytail, lazy senior dev mode
2
+
3
+ You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.
4
+
5
+ Before writing any code, stop at the first rung that holds:
6
+
7
+ 1. Does this need to be built at all? (YAGNI)
8
+ 2. Does it already exist in this codebase? Reuse the helper, util, or pattern that's already here, don't re-write it.
9
+ 3. Does the standard library already do this? Use it.
10
+ 4. Does a native platform feature cover it? Use it.
11
+ 5. Does an already-installed dependency solve it? Use it.
12
+ 6. Can this be one line? Make it one line.
13
+ 7. Only then: write the minimum code that works.
14
+
15
+ The ladder runs after you understand the problem, not instead of it: read the task and the code it touches, trace the real flow end to end, then climb.
16
+
17
+ Bug fix = root cause, not symptom: a report names a symptom. Grep every caller of the function you touch and fix the shared function once — one guard there is a smaller diff than one per caller, and patching only the path the ticket names leaves a sibling caller still broken.
18
+
19
+ Rules:
20
+
21
+ - No abstractions that weren't explicitly requested.
22
+ - No new dependency if it can be avoided.
23
+ - No boilerplate nobody asked for.
24
+ - Deletion over addition. Boring over clever. Fewest files possible.
25
+ - Shortest working diff wins, but only once you understand the problem. The smallest change in the wrong place isn't lazy, it's a second bug.
26
+ - Question complex requests: "Do you actually need X, or does Y cover it?"
27
+ - Pick the edge-case-correct option when two stdlib approaches are the same size, lazy means less code, not the flimsier algorithm.
28
+ - Mark deliberate simplifications that cut a real corner with a known ceiling (global lock, O(n²) scan, naive heuristic) with a `ponytail:` comment naming the ceiling and upgrade path.
29
+
30
+ Not lazy about: understanding the problem (read it fully and trace the real flow before picking a rung, a small diff you don't understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test.
@@ -0,0 +1,337 @@
1
+ ---
2
+ description: This rule file outlines comprehensive best practices for using pytest in Python projects, covering code organization, testing strategies, performance optimization, security measures, and common pitfalls to avoid.
3
+ globs: tests/*
4
+ ---
5
+ # Pytest Best Practices: A Comprehensive Guide
6
+
7
+ This document provides a detailed guide to using pytest effectively in Python projects, covering various aspects from code organization to security considerations. It aims to provide actionable guidance for developers to improve their testing practices and build robust applications.
8
+
9
+ ## Library Information:
10
+ - Name: pytest
11
+ - Tags: development, testing, python
12
+
13
+ ## 1. Code Organization and Structure
14
+
15
+ A well-organized codebase is crucial for maintainability and testability. Here are best practices for structuring your pytest projects:
16
+
17
+ ### 1.1. Directory Structure
18
+
19
+ - **Separate `tests/` directory:** Keep your tests in a directory separate from your application code, typically named `tests/`. This promotes isolation and cleaner project structure.
20
+
21
+
22
+ my_project/
23
+ ├── my_app/
24
+ │ ├── __init__.py
25
+ │ ├── module1.py
26
+ │ └── module2.py
27
+ ├── tests/
28
+ │ ├── __init__.py
29
+ │ ├── test_module1.py
30
+ │ └── test_module2.py
31
+ └── pyproject.toml
32
+
33
+
34
+ - **`src` layout (Recommended):** Consider using a `src` layout to further isolate application code from the project root. This prevents import conflicts and improves clarity.
35
+
36
+
37
+ my_project/
38
+ ├── src/
39
+ │ └── my_app/
40
+ │ ├── __init__.py
41
+ │ ├── module1.py
42
+ │ └── module2.py
43
+ ├── tests/
44
+ │ ├── __init__.py
45
+ │ ├── test_module1.py
46
+ │ └── test_module2.py
47
+ └── pyproject.toml
48
+
49
+
50
+ ### 1.2. File Naming Conventions
51
+
52
+ - **`test_*.py` or `*_test.py`:** pytest automatically discovers test files matching these patterns.
53
+ - **Descriptive names:** Use clear and descriptive names for your test files to indicate what they are testing (e.g., `test_user_authentication.py`).
54
+
55
+ ### 1.3. Module Organization
56
+
57
+ - **Mirror application structure:** Structure your test modules to mirror the structure of your application code. This makes it easier to locate tests for specific modules.
58
+ - **`__init__.py`:** Include `__init__.py` files in your test directories to ensure they are treated as Python packages.
59
+
60
+ ### 1.4. Component Architecture
61
+
62
+ - **Isolate components:** Design your application with well-defined components that can be tested independently.
63
+ - **Dependency injection:** Use dependency injection to provide components with their dependencies, making it easier to mock and stub external resources during testing.
64
+
65
+ ### 1.5. Code Splitting
66
+
67
+ - **Small, focused functions:** Break down large functions into smaller, focused functions that are easier to test.
68
+ - **Modular design:** Organize your code into modules with clear responsibilities.
69
+
70
+ ## 2. Common Patterns and Anti-patterns
71
+
72
+ ### 2.1. Design Patterns
73
+
74
+ - **Arrange-Act-Assert (AAA):** Structure your tests following the AAA pattern for clarity.
75
+ - **Arrange:** Set up the test environment and prepare any necessary data.
76
+ - **Act:** Execute the code being tested.
77
+ - **Assert:** Verify that the code behaved as expected.
78
+
79
+ python
80
+ def test_example():
81
+ # Arrange
82
+ data = ...
83
+ expected_result = ...
84
+
85
+ # Act
86
+ result = function_under_test(data)
87
+
88
+ # Assert
89
+ assert result == expected_result
90
+
91
+
92
+ - **Fixture factory:** Use fixture factories to create reusable test data.
93
+
94
+ python
95
+ import pytest
96
+
97
+ @pytest.fixture
98
+ def user_factory():
99
+ def create_user(username, email):
100
+ return {"username": username, "email": email}
101
+ return create_user
102
+
103
+ def test_create_user(user_factory):
104
+ user = user_factory("testuser", "test@example.com")
105
+ assert user["username"] == "testuser"
106
+
107
+
108
+ ### 2.2. Recommended Approaches
109
+
110
+ - **Use fixtures for setup and teardown:** Fixtures help manage test dependencies and ensure a clean test environment.
111
+ - **Parameterize tests:** Use `@pytest.mark.parametrize` to run the same test with different inputs and expected outputs, reducing code duplication.
112
+ - **Use descriptive names for tests and fixtures:** This makes it easier to understand the purpose of each test and fixture.
113
+ - **Single Assertion per Test:** A single assertion per test makes it easier to identify the specific failure point.
114
+
115
+ ### 2.3. Anti-patterns and Code Smells
116
+
117
+ - **Over-reliance on fixtures:** Avoid creating too many fixtures, especially for simple data. Use direct data definition in the test if it's not reused.
118
+ - **Implicit dependencies:** Make dependencies explicit by passing them as arguments to your functions and tests.
119
+ - **Testing implementation details:** Focus on testing the behavior of your code, not the implementation details. This makes your tests more resilient to refactoring.
120
+ - **Skipping Tests Without a Reason:** Don't skip tests without a valid reason or comment explaining why.
121
+
122
+ ### 2.4. State Management
123
+
124
+ - **Stateless tests:** Ensure your tests are stateless and independent to avoid unexpected side effects. Each test should set up its own data and clean up after itself.
125
+ - **Fixture scopes:** Use fixture scopes (`session`, `module`, `function`) to control the lifecycle of fixtures and manage state effectively.
126
+
127
+ ### 2.5. Error Handling
128
+
129
+ - **Test exception handling:** Write tests to verify that your code handles exceptions correctly.
130
+
131
+ python
132
+ import pytest
133
+
134
+ def divide(a, b):
135
+ if b == 0:
136
+ raise ValueError("Cannot divide by zero")
137
+ return a / b
138
+
139
+ def test_divide_by_zero():
140
+ with pytest.raises(ValueError) as e:
141
+ divide(10, 0)
142
+ assert str(e.value) == "Cannot divide by zero"
143
+
144
+
145
+ - **Use `pytest.raises`:** Use `pytest.raises` to assert that a specific exception is raised.
146
+ - **Log errors:** Ensure your application logs errors appropriately, and consider writing tests to verify that errors are logged correctly.
147
+
148
+ ## 3. Performance Considerations
149
+
150
+ ### 3.1. Optimization Techniques
151
+
152
+ - **Profile slow tests:** Use the `--durations` option to identify slow tests and optimize them.
153
+ - **Parallel test execution:** Use `pytest-xdist` to run tests in parallel and reduce overall test execution time. `pip install pytest-xdist` then run `pytest -n auto`. The `auto` option utilizes all available CPU cores.
154
+ - **Caching:** Cache expensive computations to avoid redundant calculations during testing.
155
+
156
+ ### 3.2. Memory Management
157
+
158
+ - **Resource cleanup:** Ensure your tests clean up any resources they allocate, such as temporary files or database connections.
159
+ - **Limit fixture scope:** Use the appropriate fixture scope to minimize the lifetime of fixtures and reduce memory consumption.
160
+
161
+ ### 3.3. Bundle Size Optimization
162
+
163
+ - **N/A:** Pytest itself doesn't directly impact bundle sizes, but your application code should be optimized separately.
164
+
165
+ ### 3.4. Lazy Loading
166
+
167
+ - **N/A:** Lazy loading is more relevant to application code than pytest itself, but can be used within fixtures if necessary to defer initialization.
168
+
169
+ ## 4. Security Best Practices
170
+
171
+ ### 4.1. Common Vulnerabilities
172
+
173
+ - **Injection attacks:** Prevent injection attacks by validating and sanitizing user inputs.
174
+ - **Cross-site scripting (XSS):** Protect against XSS vulnerabilities by escaping user-generated content.
175
+ - **Authentication and authorization flaws:** Implement secure authentication and authorization mechanisms to protect sensitive data.
176
+
177
+ ### 4.2. Input Validation
178
+
179
+ - **Validate all inputs:** Validate all user inputs to ensure they conform to expected formats and ranges.
180
+ - **Use parameterized tests:** Use parameterized tests to test input validation logic with a variety of inputs, including edge cases and invalid values.
181
+
182
+ ### 4.3. Authentication and Authorization
183
+
184
+ - **Test authentication:** Write tests to verify that your authentication mechanisms are working correctly.
185
+ - **Test authorization:** Write tests to verify that users only have access to the resources they are authorized to access.
186
+
187
+ ### 4.4. Data Protection
188
+
189
+ - **Encrypt sensitive data:** Encrypt sensitive data at rest and in transit.
190
+ - **Use secure storage:** Store sensitive data in secure storage locations with appropriate access controls.
191
+
192
+ ### 4.5. Secure API Communication
193
+
194
+ - **Use HTTPS:** Always use HTTPS for API communication to protect data in transit.
195
+ - **Validate API responses:** Validate API responses to ensure they are valid and haven't been tampered with.
196
+
197
+ ## 5. Testing Approaches
198
+
199
+ ### 5.1. Unit Testing
200
+
201
+ - **Test individual units:** Unit tests should focus on testing individual functions, methods, or classes in isolation.
202
+ - **Mock dependencies:** Use mocking to isolate units under test from their dependencies.
203
+
204
+ ### 5.2. Integration Testing
205
+
206
+ - **Test interactions:** Integration tests should focus on testing the interactions between different components of your application.
207
+ - **Use real dependencies (where appropriate):** For integration tests, it's often appropriate to use real dependencies, such as databases or external APIs, to ensure that the different components work together correctly. Consider using test containers for database and service dependencies.
208
+
209
+ ### 5.3. End-to-End Testing
210
+
211
+ - **Test complete workflows:** End-to-end tests should focus on testing complete user workflows, from start to finish.
212
+ - **Use browser automation:** Use browser automation tools like Selenium or Playwright to simulate user interactions with your application.
213
+
214
+ ### 5.4. Test Organization
215
+
216
+ - **Organize tests by feature:** Group tests by the feature they are testing to improve organization and maintainability.
217
+ - **Use clear naming conventions:** Use clear naming conventions for your tests and test files to indicate what they are testing.
218
+
219
+ ### 5.5. Mocking and Stubbing
220
+
221
+ - **Use `mocker` fixture:** Use the `mocker` fixture provided by the `pytest-mock` plugin for mocking and stubbing.
222
+ - **Mock external dependencies:** Mock external dependencies, such as databases or APIs, to isolate your tests and prevent them from relying on external resources.
223
+ - **Use `autospec=True`:** Use `autospec=True` when mocking to ensure that your mocks have the same API as the original objects. This helps prevent errors caused by incorrect mock implementations.
224
+
225
+ python
226
+ def test_example(mocker):
227
+ mock_external_api = mocker.patch("module.external_api", autospec=True)
228
+ mock_external_api.return_value = {"data": "test data"}
229
+
230
+
231
+ ## 6. Common Pitfalls and Gotchas
232
+
233
+ ### 6.1. Frequent Mistakes
234
+
235
+ - **Not isolating tests:** Failing to isolate tests can lead to unpredictable results and make it difficult to debug failures.
236
+ - **Testing implementation details:** Testing implementation details makes your tests brittle and difficult to maintain.
237
+ - **Ignoring warnings:** Ignoring warnings from pytest can mask underlying problems in your tests.
238
+
239
+ ### 6.2. Edge Cases
240
+
241
+ - **Empty inputs:** Test your code with empty inputs to ensure it handles them gracefully.
242
+ - **Invalid inputs:** Test your code with invalid inputs to ensure it handles them correctly and raises appropriate exceptions.
243
+ - **Boundary conditions:** Test your code with boundary conditions to ensure it handles them correctly.
244
+
245
+ ### 6.3. Version-Specific Issues
246
+
247
+ - **Check release notes:** Check the release notes for each new version of pytest to be aware of any breaking changes or new features.
248
+ - **Pin dependencies:** Pin your pytest dependency to a specific version to avoid unexpected behavior caused by updates.
249
+
250
+ ### 6.4. Compatibility Concerns
251
+
252
+ - **Check compatibility:** Check the compatibility of pytest with other technologies you are using, such as specific versions of Python or Django.
253
+
254
+ ### 6.5. Debugging Strategies
255
+
256
+ - **Use `--pdb`:** Use the `--pdb` option to drop into the Python debugger when a test fails.
257
+ - **Use logging:** Use logging to add debugging information to your tests.
258
+ - **Simplify tests:** Simplify failing tests to isolate the cause of the failure.
259
+
260
+ ## 7. Tooling and Environment
261
+
262
+ ### 7.1. Recommended Development Tools
263
+
264
+ - **IDE:** Use a good IDE with pytest support, such as VS Code with the Python extension, PyCharm, or Sublime Text with the appropriate plugins.
265
+ - **pytest-watch:** Use `pytest-watch` for automatic test rerunning on file changes. `pip install pytest-watch`, then run `ptw`.
266
+
267
+ ### 7.2. Build Configuration
268
+
269
+ - **Use `pyproject.toml`:** Use a `pyproject.toml` file to configure your pytest settings.
270
+
271
+ toml
272
+ [tool.pytest.ini_options]
273
+ addopts = [
274
+ "--cov=my_app",
275
+ "--cov-report term-missing",
276
+ "-v",
277
+ ]
278
+ testpaths = [
279
+ "tests",
280
+ ]
281
+
282
+
283
+ ### 7.3. Linting and Formatting
284
+
285
+ - **Use `flake8-pytest-style`:** Use the `flake8-pytest-style` plugin to enforce pytest-specific coding standards. `pip install flake8 flake8-pytest-style`
286
+ - **Use `black` or `autopep8`:** Use a code formatter like `black` or `autopep8` to ensure consistent code formatting. `pip install black`, then run `black .`
287
+
288
+ ### 7.4. Deployment
289
+
290
+ - **Include tests in your deployment pipeline:** Ensure your tests are run as part of your deployment pipeline to prevent regressions.
291
+ - **Use a dedicated test environment:** Use a dedicated test environment to avoid interfering with your production environment.
292
+
293
+ ### 7.5. CI/CD Integration
294
+
295
+ - **Integrate with CI/CD:** Integrate pytest with your CI/CD system, such as GitHub Actions, GitLab CI, or Jenkins, to automatically run your tests on every commit.
296
+
297
+ Example GitHub Actions workflow (`.github/workflows/test.yml`):
298
+
299
+
300
+ name: Test
301
+ on:
302
+ push:
303
+ branches: [ main ]
304
+ pull_request:
305
+ branches: [ main ]
306
+ jobs:
307
+ build:
308
+ runs-on: ubuntu-latest
309
+ steps:
310
+ - uses: actions/checkout@v3
311
+ - name: Set up Python 3.10
312
+ uses: actions/setup-python@v3
313
+ with:
314
+ python-version: "3.10"
315
+ - name: Install dependencies
316
+ run: |
317
+ python -m pip install --upgrade pip
318
+ pip install pytest pytest-cov flake8 flake8-pytest-style black
319
+ pip install -e . # Install your project in editable mode
320
+ - name: Lint with flake8
321
+ run: |
322
+ flake8 .
323
+ - name: Test with pytest
324
+ run: |
325
+ pytest --cov --cov-report xml
326
+ - name: Upload coverage to Codecov
327
+ uses: codecov/codecov-action@v3
328
+ with:
329
+ token: ${{ secrets.CODECOV_TOKEN }}
330
+ flags: unittests
331
+ env_vars: OS,PYTHON
332
+ name: codecov-pytest
333
+
334
+
335
+ By following these best practices, you can write effective and maintainable tests with pytest, improving the quality and reliability of your Python applications.---
336
+ alwaysApply: true
337
+ ---
@@ -0,0 +1,29 @@
1
+ ---
2
+ description: Install Python packages, access Python CLI, run python code and scripts, execute Python commands, and manage Python environments. Use uv run for all Python execution instead of direct python commands.
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Python Package Management with uv
7
+
8
+ Use uv exclusively for Python package management in all projects.
9
+
10
+ ## Package Management Commands
11
+
12
+ - All Python dependencies **must be installed, synchronized, and locked** using uv
13
+ - Never use pip, pip-tools, poetry, or conda directly for dependency management
14
+
15
+ Use these commands
16
+
17
+ - Install dependencies: `uv add <package>`
18
+ - Remove dependencies: `uv remove <package>`
19
+ - Sync dependencies: `uv sync`
20
+
21
+ ## Running Python Code
22
+
23
+ - Run a Python script with `uv run <script-name>.py`
24
+ - Run Python tools like Pytest with `uv run pytest` or `uv run ruff`
25
+ - Launch a Python repl with `uv run python`---
26
+ alwaysApply: true
27
+ ------
28
+ alwaysApply: true
29
+ ---
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: query-electiondatamy-api
3
+ description: Queries and analyzes Malaysian election data through the ElectionData.MY API. Use when the user asks for candidates, seats, parties, coalitions, elections, by-elections, contest results, turnout, majorities, or vote shares from ElectionData.MY.
4
+ ---
5
+
6
+ # Query ElectionData.MY API
7
+
8
+ Use the v1 API to answer focused questions about Malaysian election data. Read [reference.md](reference.md) before choosing endpoints or parameters.
9
+
10
+ ## Scope
11
+
12
+ Use the API for focused, dynamic queries. Recommend the ElectionData.MY data lake instead when the user needs:
13
+
14
+ - bulk analysis or the full dataset;
15
+ - geospatial or boundary files;
16
+ - local analytical workflows better served by static files.
17
+
18
+ ## Authentication and safety
19
+
20
+ - Base URL: `https://api.electiondata.my/v1`
21
+ - Only `GET` requests are supported.
22
+ - Every request requires `Authorization: Bearer <API key>`.
23
+ - Use the `ELECTIONDATAMY_API_KEY` environment variable. Check that it exists without printing it:
24
+
25
+ ```bash
26
+ test -n "$ELECTIONDATAMY_API_KEY"
27
+ ```
28
+
29
+ - If it is missing, ask the user to set it in their terminal. Do not ask them to paste the key into chat.
30
+ - Never print, persist, commit, or interpolate the key as a literal.
31
+
32
+ ## Query workflow
33
+
34
+ 1. Translate the question into the entity, scope, election type, and date needed.
35
+ 2. Select the endpoint chain from the reference.
36
+ 3. When an identifier is unknown, call the relevant dropdown endpoint first. Do not guess UIDs, slugs, election identifiers, seat names, states, or dates.
37
+ 4. Pass identifiers returned by discovery endpoints exactly as documented:
38
+ - Candidate queries use `uid`.
39
+ - Party queries use the selected entry's `maps_to` as `uid`, never its historical `uid`.
40
+ - Result detail queries reuse `seat`, `state`, and `date` from a companion endpoint without transformation.
41
+ 5. URL-encode every query parameter. Prefer `curl --get --data-urlencode`.
42
+ 6. Inspect the complete response before filtering or aggregating it.
43
+ 7. Answer concisely, naming the endpoint and query scope used. Clearly separate returned fields from calculations or interpretation.
44
+
45
+ ## Request pattern
46
+
47
+ ```bash
48
+ curl --silent --show-error --fail-with-body --get \
49
+ "https://api.electiondata.my/v1/<endpoint>" \
50
+ --header "Authorization: Bearer ${ELECTIONDATAMY_API_KEY}" \
51
+ --data-urlencode "parameter=value"
52
+ ```
53
+
54
+ For an endpoint without parameters, omit `--get` and `--data-urlencode`.
55
+
56
+ ## Endpoint chaining
57
+
58
+ - Candidate lookup: `/candidates/dropdown` → `/candidates?uid=...` → optionally `/results`
59
+ - Seat history: `/seats/dropdown` → `/seats/results?slug=...` → optionally `/results`
60
+ - Party or coalition history: `/parties/dropdown` → `/parties/results`
61
+ - Election overview: `/elections/dropdown` → one or more of `/elections/by_party`, `/elections/by_seat`, `/elections/stats`
62
+ - By-election list: `/byelections` → optionally `/results`
63
+ - Contest detail: obtain `seat`, `state`, and `date` from a companion endpoint → `/results`
64
+
65
+ ## Interpretation rules
66
+
67
+ - Treat ISO dates as polling dates unless the field explicitly describes a boundary-change event.
68
+ - Percentage fields are already percentages; do not multiply them by 100.
69
+ - `won_uncontested` can legitimately produce zero or null vote statistics.
70
+ - In party queries, `{"results":[]}` means no contests in that scope, not an API error.
71
+ - With seat `lineage=true`, distinguish election rows by the presence of `election_name`; rows without it are boundary-change events.
72
+ - Preserve the API's `parlimen` and `dun` values when filtering.
73
+ - Do not infer current party identity from `known_as`; explain historical names and canonical mappings when relevant.
74
+
75
+ ## Errors
76
+
77
+ - `400`: missing or invalid parameters, invalid combinations, malformed date, or missing version prefix.
78
+ - `401`: missing or invalid API key.
79
+ - `404`: unknown endpoint or no matching resource.
80
+ - `405`: method other than `GET`.
81
+ - `500`: server-side failure.
82
+
83
+ Error bodies contain an `error` field. Report the useful message, correct the request when possible, and do not silently reinterpret an invalid query.
84
+
85
+ ## Answer quality
86
+
87
+ - State material filters such as state, election, election type, lineage mode, and date.
88
+ - Include units for vote counts, seats, turnout, and percentages.
89
+ - For comparisons, verify that scopes and denominators match.
90
+ - Mention missing or null data rather than silently dropping it.
91
+ - Do not claim the API is real-time; dropdown datasets update around election-related publication events described in the docs.
@@ -0,0 +1,164 @@
1
+ # ElectionData.MY v1 reference
2
+
3
+ Base URL: `https://api.electiondata.my/v1`
4
+
5
+ All endpoints are authenticated `GET` requests.
6
+
7
+ ## Candidates
8
+
9
+ ### `/candidates/dropdown`
10
+
11
+ No parameters. Returns an array of candidates ordered by contests, with:
12
+
13
+ - `uid`, `name`
14
+ - `c` contests, `w` wins, `l` losses
15
+
16
+ Use `uid` in `/candidates`.
17
+
18
+ ### `/candidates`
19
+
20
+ Required: `uid`
21
+
22
+ Returns an array ordered newest to oldest. Each row identifies the candidate, election, seat, state, party and coalition, votes, vote percentage, polling date, and result.
23
+
24
+ Result values: `won`, `won_uncontested`, `lost`, `lost_deposit`.
25
+
26
+ ## Seats
27
+
28
+ ### `/seats/dropdown`
29
+
30
+ No parameters. Response: `{ "seats": [...] }`
31
+
32
+ Each row contains:
33
+
34
+ - `seat`: full current seat name
35
+ - `slug`: identifier for `/seats/results`
36
+ - `type`: `parlimen` or `dun`
37
+
38
+ ### `/seats/results`
39
+
40
+ Required: `slug`
41
+
42
+ Optional: `lineage=true|false` (default `false`)
43
+
44
+ Response: `{ "results": [...] }`, newest first.
45
+
46
+ Election rows include `election_name`, `seat`, `state`, `date`, winner `name`, party and coalition identifiers, majority, and turnout.
47
+
48
+ With `lineage=true`, the array also contains boundary-change rows with `date`, `change_en`, and `change_ms`. A row with `election_name` is an election result; a row without it is a boundary-change event.
49
+
50
+ ## Parties and coalitions
51
+
52
+ ### `/parties/dropdown`
53
+
54
+ No parameters. Response: `{ "data": [...] }`
55
+
56
+ Each row contains `type`, `uid`, `maps_to`, `acronym`, `name_en`, and `name_bm`.
57
+
58
+ Historical names may have legacy UIDs. Always pass `maps_to`, not `uid`, to `/parties/results`.
59
+
60
+ ### `/parties/results`
61
+
62
+ Required:
63
+
64
+ - `type`: `party` or `coalition`
65
+ - `uid`: the dropdown row's `maps_to`
66
+ - `state`
67
+ - `election_type`: `parlimen` or `dun`
68
+
69
+ Valid states:
70
+
71
+ `Malaysia`, `Semenanjung`, `Johor`, `Kedah`, `Kelantan`, `Melaka`, `Negeri Sembilan`, `Pahang`, `Perak`, `Perlis`, `Pulau Pinang`, `Sabah`, `Sarawak`, `Selangor`, `Terengganu`, `W.P. Kuala Lumpur`, `W.P. Labuan`, `W.P. Putrajaya`
72
+
73
+ `dun` is invalid for `Malaysia`, `Semenanjung`, and every `W.P. *` state.
74
+
75
+ Response: `{ "results": [...] }`, newest first. Rows contain historical identity (`known_as_uid`, `known_as`), election and date, seats contested and won, seat percentages, votes, and vote percentage. An empty `results` array is valid.
76
+
77
+ ## Elections
78
+
79
+ ### `/elections/dropdown`
80
+
81
+ No parameters. Response: `{ "elections": [...] }`
82
+
83
+ Each row contains `state`, `type`, `election`, and `date`. Pass `state` and `election` directly to the following election endpoints.
84
+
85
+ ### `/elections/by_party`
86
+
87
+ Required: `state`, `election`
88
+
89
+ Response: `{ "by_party": [...] }`, ordered by votes descending. Rows include party and coalition identifiers, seats contested and won, total seats, seat percentages, votes, total valid votes, and vote percentage.
90
+
91
+ ### `/elections/by_seat`
92
+
93
+ Required: `state`, `election`
94
+
95
+ Response: `{ "by_seat": [...] }`, one row per constituency. Rows contain the seat, date, winner, winning and losing parties and coalitions, candidate count, registered voters, turnout, majority, and rejected-vote statistics.
96
+
97
+ ### `/elections/stats`
98
+
99
+ Required: `state`, `election`
100
+
101
+ Response: `{ "stats": [<single aggregate row>] }`
102
+
103
+ The row contains registered voters, turnout, turnout percentage, rejected votes, rejected-vote percentage, and candidate count.
104
+
105
+ ## By-elections
106
+
107
+ ### `/byelections`
108
+
109
+ No parameters. Response: `{ "data": [...] }`, newest first.
110
+
111
+ Each row contains `seat`, `state`, `date`, winner, party and coalition identifiers, registered voters, turnout, rejected votes, majority, and corresponding percentages.
112
+
113
+ Use a row's `seat`, `state`, and `date` to request full contest details from `/results`.
114
+
115
+ ## Contest details
116
+
117
+ ### `/results`
118
+
119
+ Required:
120
+
121
+ - `seat`
122
+ - `state`
123
+ - `date` in `YYYY-MM-DD`
124
+
125
+ Obtain all three values from candidates, seats, elections, or by-elections responses and pass them through unchanged.
126
+
127
+ Response:
128
+
129
+ - `ballot`: candidates ordered by votes descending, with party, coalition, votes, vote percentage, and result
130
+ - `stats`: a one-row array containing date, registered voters, turnout, rejected votes, majority, and corresponding percentages
131
+
132
+ For uncontested wins, vote counts may be zero and percentage statistics may be null.
133
+
134
+ ## Common examples
135
+
136
+ Find a candidate's history:
137
+
138
+ ```bash
139
+ curl --silent --show-error --fail-with-body --get \
140
+ "https://api.electiondata.my/v1/candidates" \
141
+ --header "Authorization: Bearer ${ELECTIONDATAMY_API_KEY}" \
142
+ --data-urlencode "uid=CMVBA"
143
+ ```
144
+
145
+ Get election results by party:
146
+
147
+ ```bash
148
+ curl --silent --show-error --fail-with-body --get \
149
+ "https://api.electiondata.my/v1/elections/by_party" \
150
+ --header "Authorization: Bearer ${ELECTIONDATAMY_API_KEY}" \
151
+ --data-urlencode "state=Malaysia" \
152
+ --data-urlencode "election=GE-15"
153
+ ```
154
+
155
+ Get one contest:
156
+
157
+ ```bash
158
+ curl --silent --show-error --fail-with-body --get \
159
+ "https://api.electiondata.my/v1/results" \
160
+ --header "Authorization: Bearer ${ELECTIONDATAMY_API_KEY}" \
161
+ --data-urlencode "seat=P.001 Padang Besar" \
162
+ --data-urlencode "state=Perlis" \
163
+ --data-urlencode "date=2022-11-19"
164
+ ```
@@ -0,0 +1 @@
1
+ ELECTIONDATAMY_API_KEY=""