pyUSPTO 0.6.1__tar.gz → 0.6.2__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 (175) hide show
  1. {pyuspto-0.6.1 → pyuspto-0.6.2}/CHANGELOG.md +33 -1
  2. {pyuspto-0.6.1/src/pyUSPTO.egg-info → pyuspto-0.6.2}/PKG-INFO +1 -1
  3. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/error_handling_example.py +2 -1
  4. {pyuspto-0.6.1 → pyuspto-0.6.2}/requirements-dev.txt +3 -3
  5. {pyuspto-0.6.1 → pyuspto-0.6.2}/requirements.txt +1 -1
  6. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/base.py +18 -0
  7. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/bulk_data.py +8 -4
  8. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/enriched_citations.py +32 -35
  9. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/oa_actions.py +26 -27
  10. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/oa_citations.py +36 -31
  11. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/oa_rejections.py +25 -25
  12. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/patent_data.py +121 -335
  13. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/petition_decisions.py +78 -97
  14. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/ptab_appeals.py +34 -38
  15. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/ptab_interferences.py +67 -49
  16. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/ptab_trials.py +93 -109
  17. pyuspto-0.6.2/src/pyUSPTO/clients/query.py +288 -0
  18. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/exceptions.py +17 -1
  19. {pyuspto-0.6.1 → pyuspto-0.6.2/src/pyUSPTO.egg-info}/PKG-INFO +1 -1
  20. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO.egg-info/SOURCES.txt +2 -0
  21. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO.egg-info/scm_file_list.json +2 -0
  22. pyuspto-0.6.2/src/pyUSPTO.egg-info/scm_version.json +8 -0
  23. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_base.py +19 -0
  24. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_bulk_data_clients.py +56 -2
  25. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_enriched_citation_clients.py +28 -0
  26. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_oa_actions_clients.py +63 -0
  27. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_oa_citations_clients.py +32 -0
  28. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_oa_rejections_clients.py +32 -0
  29. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_patent_data_clients.py +108 -8
  30. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_petition_decision_clients.py +65 -0
  31. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_ptab_appeals_client.py +3 -3
  32. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_ptab_interferences_client.py +9 -3
  33. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/test_ptab_trials_client.py +5 -5
  34. pyuspto-0.6.2/tests/clients/test_query.py +148 -0
  35. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/test_enriched_citations_integration.py +26 -0
  36. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/test_oa_actions_integration.py +14 -0
  37. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/test_oa_citations_integration.py +13 -0
  38. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/test_oa_rejections_integration.py +13 -0
  39. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/test_patent_data_integration.py +83 -0
  40. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/test_petition_decisions_integration.py +13 -0
  41. pyuspto-0.6.2/tests/integration/test_ptab_appeals_integration.py +448 -0
  42. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/test_ptab_interferences_integration.py +14 -0
  43. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/test_ptab_trials_integration.py +14 -0
  44. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/test_exceptions.py +42 -0
  45. pyuspto-0.6.1/src/pyUSPTO.egg-info/scm_version.json +0 -8
  46. pyuspto-0.6.1/tests/integration/test_ptab_appeals_integration.py +0 -522
  47. {pyuspto-0.6.1 → pyuspto-0.6.2}/.devcontainer/devcontainer-lock.json +0 -0
  48. {pyuspto-0.6.1 → pyuspto-0.6.2}/.devcontainer/devcontainer.json +0 -0
  49. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  50. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  51. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/ISSUE_TEMPLATE/documentation-updates.yml +0 -0
  52. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  53. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  54. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/dependabot.yml +0 -0
  55. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/workflows/codeql.yml +0 -0
  56. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/workflows/publish-docs.yaml +0 -0
  57. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/workflows/publish-to-test-pypi.yml +0 -0
  58. {pyuspto-0.6.1 → pyuspto-0.6.2}/.github/workflows/python-tests.yml +0 -0
  59. {pyuspto-0.6.1 → pyuspto-0.6.2}/.gitignore +0 -0
  60. {pyuspto-0.6.1 → pyuspto-0.6.2}/.readthedocs.yaml +0 -0
  61. {pyuspto-0.6.1 → pyuspto-0.6.2}/ADVANCED.md +0 -0
  62. {pyuspto-0.6.1 → pyuspto-0.6.2}/CODE_OF_CONDUCT.md +0 -0
  63. {pyuspto-0.6.1 → pyuspto-0.6.2}/CONTRIBUTING.md +0 -0
  64. {pyuspto-0.6.1 → pyuspto-0.6.2}/LICENSE +0 -0
  65. {pyuspto-0.6.1 → pyuspto-0.6.2}/README.md +0 -0
  66. {pyuspto-0.6.1 → pyuspto-0.6.2}/_version.pyi +0 -0
  67. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/Makefile +0 -0
  68. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/_static/.gitkeep +0 -0
  69. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/about.rst +0 -0
  70. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/advanced.rst +0 -0
  71. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/bulk_data.rst +0 -0
  72. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/enriched_citations.rst +0 -0
  73. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/index.rst +0 -0
  74. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/oa_actions.rst +0 -0
  75. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/oa_citations.rst +0 -0
  76. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/oa_rejections.rst +0 -0
  77. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/patent_data.rst +0 -0
  78. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/petition_decisions.rst +0 -0
  79. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/ptab_appeals.rst +0 -0
  80. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/ptab_interferences.rst +0 -0
  81. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/clients/ptab_trials.rst +0 -0
  82. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/config/config.rst +0 -0
  83. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/config/http_config.rst +0 -0
  84. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/config/index.rst +0 -0
  85. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/exceptions.rst +0 -0
  86. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/index.rst +0 -0
  87. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/base.rst +0 -0
  88. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/bulk_data.rst +0 -0
  89. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/enriched_citations.rst +0 -0
  90. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/index.rst +0 -0
  91. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/oa_actions.rst +0 -0
  92. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/oa_citations.rst +0 -0
  93. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/oa_rejections.rst +0 -0
  94. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/patent_data.rst +0 -0
  95. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/petition_decisions.rst +0 -0
  96. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/ptab.rst +0 -0
  97. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/models/utils.rst +0 -0
  98. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/api/warnings.rst +0 -0
  99. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/conf.py +0 -0
  100. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/configuration.rst +0 -0
  101. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/development.rst +0 -0
  102. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/bulk_data.rst +0 -0
  103. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/enriched_citations.rst +0 -0
  104. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/ifw_example.rst +0 -0
  105. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/index.rst +0 -0
  106. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/oa_actions.rst +0 -0
  107. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/oa_citations.rst +0 -0
  108. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/oa_rejections.rst +0 -0
  109. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/patent_data.rst +0 -0
  110. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/petition_decisions.rst +0 -0
  111. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/ptab_appeals.rst +0 -0
  112. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/ptab_interferences.rst +0 -0
  113. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/examples/ptab_trials.rst +0 -0
  114. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/index.rst +0 -0
  115. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/installation.rst +0 -0
  116. {pyuspto-0.6.1 → pyuspto-0.6.2}/docs/source/quickstart.rst +0 -0
  117. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/bulk_data_example.py +0 -0
  118. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/enriched_citations_example.py +0 -0
  119. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/ifw_example.py +0 -0
  120. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/oa_actions_example.py +0 -0
  121. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/oa_citations_example.py +0 -0
  122. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/oa_rejections_example.py +0 -0
  123. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/patent_data_example.py +0 -0
  124. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/petition_decisions_example.py +0 -0
  125. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/ptab_appeals_example.py +0 -0
  126. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/ptab_interferences_example.py +0 -0
  127. {pyuspto-0.6.1 → pyuspto-0.6.2}/examples/ptab_trials_example.py +0 -0
  128. {pyuspto-0.6.1 → pyuspto-0.6.2}/pyUSPTO.code-workspace +0 -0
  129. {pyuspto-0.6.1 → pyuspto-0.6.2}/pyproject.toml +0 -0
  130. {pyuspto-0.6.1 → pyuspto-0.6.2}/setup.cfg +0 -0
  131. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/__init__.py +0 -0
  132. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/clients/__init__.py +0 -0
  133. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/config.py +0 -0
  134. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/http_config.py +0 -0
  135. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/__init__.py +0 -0
  136. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/base.py +0 -0
  137. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/bulk_data.py +0 -0
  138. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/enriched_citations.py +0 -0
  139. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/oa_actions.py +0 -0
  140. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/oa_citations.py +0 -0
  141. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/oa_rejections.py +0 -0
  142. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/patent_data.py +0 -0
  143. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/petition_decisions.py +0 -0
  144. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/ptab.py +0 -0
  145. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/models/utils.py +0 -0
  146. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/py.typed +0 -0
  147. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/utils/__init__.py +0 -0
  148. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO/warnings.py +0 -0
  149. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO.egg-info/dependency_links.txt +0 -0
  150. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO.egg-info/requires.txt +0 -0
  151. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO.egg-info/top_level.txt +0 -0
  152. {pyuspto-0.6.1 → pyuspto-0.6.2}/src/pyUSPTO.egg-info/zip-safe +0 -0
  153. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/__init__.py +0 -0
  154. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/clients/__init__.py +0 -0
  155. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/conftest.py +0 -0
  156. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/__init__.py +0 -0
  157. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/conftest.py +0 -0
  158. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/integration/test_bulk_data_integration.py +0 -0
  159. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/__init__.py +0 -0
  160. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/test_bulk_data_models.py +0 -0
  161. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/test_enriched_citation_models.py +0 -0
  162. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/test_oa_actions_models.py +0 -0
  163. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/test_oa_citations_models.py +0 -0
  164. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/test_oa_rejections_models.py +0 -0
  165. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/test_patent_data_models.py +0 -0
  166. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/test_petition_decision_models.py +0 -0
  167. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/test_ptab_models.py +0 -0
  168. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/models/test_utils.py +0 -0
  169. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/test_config.py +0 -0
  170. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/test_edge_cases.py +0 -0
  171. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/test_http_config.py +0 -0
  172. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/test_init.py +0 -0
  173. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/test_version.py +0 -0
  174. {pyuspto-0.6.1 → pyuspto-0.6.2}/tests/utils/__init__.py +0 -0
  175. {pyuspto-0.6.1 → pyuspto-0.6.2}/tox.ini +0 -0
@@ -5,7 +5,39 @@ All notable changes to the pyUSPTO package will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [0.6.0]
8
+ ## [Unreleased]
9
+
10
+ ### Changed
11
+
12
+ - `sanitize_application_number` now returns series-code input as 8 digits (`18/511,739` → `18511739`). Previously it kept the slash, so `get_application_by_number` and the other per-application methods requested `.../applications/18/511739`, which the API rejects with HTTP 403. This fixes every method that takes an application number, including `get_IFW_metadata(application_number=...)` and `get_IFW(application_number=...)`.
13
+ - A 403 response with the body `{"message":"Missing Authentication Token"}` now adds to its exception message that USPTO also returns this body when the requested URL path does not exist. It is still raised as `USPTOApiAuthError`.
14
+ - `error_handling_example.py` notes that a "Missing Authentication Token" error can also mean a bad URL path.
15
+ - Every client builds its convenience filters with one shared module, `pyUSPTO.clients.query`. `PTABTrialsClient` and `PTABInterferencesClient` no longer quote a single-word text value. Live, `trialStatusCategory:Instituted` and `trialStatusCategory:"Instituted"` both return 220 proceedings. `PTABInterferencesClient` also wraps each party-name filter's `OR` group in parentheses, which returned the same records as before in live checks.
16
+
17
+ ### Fixed
18
+
19
+ - `application_number_q` (used by `search_applications` and `get_search_results`) now goes through `sanitize_application_number`, so `14/412875`, `14/412,875`, and `14412875` all match, and `PCT/US2025/047756` is sent as `PCTUS2025047756`. Previously the value was sent verbatim and `14/412875` matched nothing. Values containing `*`, and values the sanitizer rejects (such as range or `OR` expressions), are not sanitized.
20
+ - `application_number_q` values that are not a single application number (wildcards, `OR` and range expressions) are now sent inside parentheses, so the field applies to every term. Previously it applied only to the first term: `14412875 OR 10000000` searched `10000000` as free text and returned 3 applications instead of 1.
21
+ - The application-number filters on the other clients get the same cleanup as `application_number_q`, including the parentheses above: `PetitionDecisionsClient` (`application_number_q`), `PTABAppealsClient` (`application_number_text_q`), `PTABInterferencesClient` (`senior_party_application_number_q`, `junior_party_application_number_q`), `PTABTrialsClient.search_decisions` (`application_number_q`), and `patent_application_number_q` on `OAActionsClient`, `OACitationsClient`, `OARejectionsClient`, and `EnrichedCitationsClient`. Previously `17/765301` matched nothing in any of them, and `17/765,301` matched nothing in the office action and enriched citation APIs.
22
+ - Text filters (names, titles, categories, classifications) are quoted when the value contains whitespace or one of `+ - & | ! ( ) { } [ ] ^ " ~ : \ /`, and double quotes and backslashes inside the quotes are escaped. A single plain word, including one with a `*` or `?` wildcard, is sent unquoted. A wildcard value that also contains one of those characters (`Garcia-Lopez*`) is quoted, so its wildcard is searched as plain text. Live, quoted `"Garcia-Lopez*"` returns the same 119 applications as `"Garcia-Lopez"`, and the unquoted form, sent before this change, returned HTTP 404. Before, the filters that quoted at all did so only for a space (or, for `classification_q`, a slash), so `assignee_name_q="Smith-Kline"` was sent unquoted, and an inner quote broke the phrase: `inventor_name_q='Smith "John'` matched nothing and now matches. Applies to the convenience filters of every search client.
23
+ - Code filters (such as `patent_number_q`) send a value containing spaces inside parentheses, so the field applies to every term. `patent_number_q="10000000 OR 10000001"` returned 3 applications and now returns 2. Applies to the convenience filters of every search client.
24
+ - `PTABAppealsClient` `appellant_name_q`, `requestor_name_q`, and `decision_type_category_q` were never quoted and are now quoted under the rule above. A multi-word value searched every word after the first as free text: `appellant_name_q="Block, Inc."` returned 4303 decisions from 2025 instead of 1.
25
+ - The office action and enriched citation category filters are code filters under the rule above: `OAActionsClient` `access_level_category_q` and `application_type_category_q`, `action_type_category_q` on `OACitationsClient` and `OARejectionsClient`, and `EnrichedCitationsClient` `office_action_category_q`. Their values are single-word codes (`PUBLIC`, `REGULAR`, `rejected`, `CTNF`), so `office_action_category_q="CTNF OR CTFR"` is sent as `officeActionCategory:(CTNF OR CTFR)`, and the `OR` now applies to the field.
26
+ - `customer_number_q=[]` and `status_code_q=[]` raise `ValueError` instead of sending the malformed clause `field:()`. Dropping the filter instead would have run the search unfiltered.
27
+ - `OAActionsClient` `group_art_unit_number_q=""` adds no filter. It sent `groupArtUnitNumber:`.
28
+ - `PetitionDecisionsClient.get_decision_by_id`, `BulkDataClient.get_product_by_id`, and `BulkDataClient.download_file` (when the file has no download URI) now percent-encode the ID or file name in the URL path. Previously a `?` or `/` in the value changed the request; `get_decision_by_id("abc?x")` requested decision `abc`. A non-string ID, such as a `uuid.UUID`, still works as it did before.
29
+ - `PTABTrialsClient.search_decisions(document_type_description_q=...)` escapes double quotes and backslashes in the value. A `"` in the value closed the quoted search early and broke the query.
30
+ - `BulkDataClient.download_file(file_name=...)` uses `file_name` only as the local save name. When the file had no download URI, it also requested `file_name` from the server instead of the real file, so `download_file(file_data, file_name="custom.zip")` asked for `.../custom.zip`.
31
+
32
+ ## [0.6.1] - 2026-10-27
33
+
34
+ ### Changed
35
+
36
+ - `requirements.txt` pins `certifi` 2026.10.23 (was 2026.7.22).
37
+ - The lint job (ruff, mypy) and the docs workflow run on Python 3.15 (was 3.14).
38
+ - Development dependency updates: certifi 2026.10.23, coverage 7.17.0, pluggy 1.7.0, pytest 9.2.0, pytest-mock 3.17.0, ruff 0.17.3, sphinx 9.2.0, sphinx-autodoc-typehints 3.17.4, sphinxcontrib-jsmath 2.0.0.
39
+
40
+ ## [0.6.0] - 2026-10-27
9
41
 
10
42
  ### Added
11
43
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyUSPTO
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: A Modern Python client for accessing the United States Patent and Trademark Office (USPTO) Open Data Portal (ODP) APIs.
5
5
  Author-email: Andrew Piechocki <apiechocki@dunlapcodding.com>
6
6
  License-Expression: MIT
@@ -32,6 +32,7 @@ try:
32
32
  except USPTOApiAuthError as e:
33
33
  print(f"Authentication failed: {e}")
34
34
  print("Check your API key and try again.")
35
+ print("A 'Missing Authentication Token' message can also mean a bad URL path.")
35
36
 
36
37
  print("-" * 40)
37
38
  print("Example 2: Not found errors")
@@ -95,7 +96,7 @@ try:
95
96
  except USPTOApiRateLimitError:
96
97
  print("Rate limited - wait and retry")
97
98
  except USPTOApiAuthError:
98
- print("Authentication failed - check API key")
99
+ print("Authentication failed - check API key or URL path")
99
100
  except USPTOTimeout:
100
101
  print("Request timed out - try again")
101
102
  except USPTOConnectionError:
@@ -47,7 +47,7 @@ mdit-py-plugins==0.6.1
47
47
  # via myst-parser
48
48
  mdurl==0.1.2
49
49
  # via markdown-it-py
50
- mypy==2.4.0
50
+ mypy==2.4.1
51
51
  # via pyUSPTO (pyproject.toml)
52
52
  mypy-extensions==1.1.0
53
53
  # via mypy
@@ -116,11 +116,11 @@ sphinxcontrib-qthelp==2.0.0
116
116
  # via sphinx
117
117
  sphinxcontrib-serializinghtml==2.0.0
118
118
  # via sphinx
119
- types-requests==2.33.0.20261006
119
+ types-requests==2.33.0.20261030
120
120
  # via pyUSPTO (pyproject.toml)
121
121
  typing-extensions==4.16.0
122
122
  # via mypy
123
- tzdata==2026.5
123
+ tzdata==2026.6
124
124
  # via pyUSPTO (pyproject.toml)
125
125
  urllib3==2.8.0
126
126
  # via
@@ -12,7 +12,7 @@ idna==3.20
12
12
  # via requests
13
13
  requests==2.34.2
14
14
  # via pyUSPTO (pyproject.toml)
15
- tzdata==2026.5
15
+ tzdata==2026.6
16
16
  # via pyUSPTO (pyproject.toml)
17
17
  urllib3==2.8.0
18
18
  # via
@@ -14,6 +14,7 @@ from typing import (
14
14
  TypeVar,
15
15
  runtime_checkable,
16
16
  )
17
+ from urllib.parse import quote
17
18
 
18
19
  import requests
19
20
 
@@ -196,6 +197,23 @@ class BaseUSPTOClient(Generic[T]):
196
197
  base = custom_base_url if custom_base_url else self.base_url
197
198
  return f"{base}/{endpoint.lstrip('/')}"
198
199
 
200
+ @staticmethod
201
+ def _quote_path_value(value: object) -> str:
202
+ """Percent-encode a caller value for use as one URL path segment.
203
+
204
+ Every reserved character is encoded, including ``/``, ``?``, and
205
+ ``#``, so the value cannot add path segments, a query string, or a
206
+ fragment to the request. A non-string value, such as a ``uuid.UUID``,
207
+ is formatted with ``str()`` first.
208
+
209
+ Args:
210
+ value: The raw value, such as a record ID or file name.
211
+
212
+ Returns:
213
+ The encoded value.
214
+ """
215
+ return quote(str(value), safe="")
216
+
199
217
  def _execute_request(
200
218
  self,
201
219
  method: str,
@@ -92,7 +92,9 @@ class BulkDataClient(BaseUSPTOClient[BulkDataResponse]):
92
92
  ... latest=True
93
93
  ... )
94
94
  """
95
- endpoint = self.ENDPOINTS["product_by_id"].format(product_id=product_id)
95
+ endpoint = self.ENDPOINTS["product_by_id"].format(
96
+ product_id=self._quote_path_value(product_id)
97
+ )
96
98
 
97
99
  params = {}
98
100
  if file_data_from_date:
@@ -147,7 +149,9 @@ class BulkDataClient(BaseUSPTOClient[BulkDataResponse]):
147
149
  Args:
148
150
  file_data: FileData object containing download info and product_identifier.
149
151
  destination: Directory to save/extract to. Defaults to current directory.
150
- file_name: Override filename. Defaults to file_data.file_name.
152
+ file_name: Name to save the file under locally. Defaults to
153
+ file_data.file_name. The download always requests
154
+ file_data.file_name from the server.
151
155
  overwrite: Whether to overwrite existing files. Defaults to False.
152
156
  extract: Whether to auto-extract archives. Defaults to False.
153
157
 
@@ -171,8 +175,8 @@ class BulkDataClient(BaseUSPTOClient[BulkDataResponse]):
171
175
  download_url = file_data.file_download_uri
172
176
  else:
173
177
  endpoint = self.ENDPOINTS["download_file"].format(
174
- productIdentifier=file_data.product_identifier,
175
- fileName=default_file_name,
178
+ productIdentifier=self._quote_path_value(file_data.product_identifier),
179
+ fileName=self._quote_path_value(file_data.file_name),
176
180
  )
177
181
  download_url = f"{self.base_url}/{endpoint}"
178
182
 
@@ -9,6 +9,14 @@ from collections.abc import Iterator
9
9
  from typing import Any
10
10
 
11
11
  from pyUSPTO.clients.base import BaseUSPTOClient
12
+ from pyUSPTO.clients.query import (
13
+ all_of,
14
+ application_number,
15
+ boolean,
16
+ date_range,
17
+ phrase,
18
+ term,
19
+ )
12
20
  from pyUSPTO.config import USPTOConfig
13
21
  from pyUSPTO.models.enriched_citations import (
14
22
  EnrichedCitation,
@@ -84,6 +92,12 @@ class EnrichedCitationsClient(BaseUSPTOClient[EnrichedCitationResponse]):
84
92
  You can provide either a direct post_body, a query string, or use convenience
85
93
  parameters that will be automatically combined into a query.
86
94
 
95
+ ``cited_document_identifier_q`` is quoted when it contains spaces or
96
+ special characters, with inner quotes escaped. The code filters
97
+ (office action and citation category codes, tech centers, art units)
98
+ wrap a value that contains spaces, such as ``"CTNF OR CTFR"``, in
99
+ parentheses, so the field applies to every term.
100
+
87
101
  Args:
88
102
  query: Direct query string in USPTO search syntax.
89
103
  sort: Sort order for results.
@@ -92,6 +106,8 @@ class EnrichedCitationsClient(BaseUSPTOClient[EnrichedCitationResponse]):
92
106
  post_body: Optional POST body for complex queries. When provided,
93
107
  all other parameters are ignored.
94
108
  patent_application_number_q: Filter by patent application number.
109
+ Any common form matches (``17/765,301``, ``17765301``). Write
110
+ each number in an ``OR`` or range expression as 8 digits.
95
111
  cited_document_identifier_q: Filter by cited document identifier.
96
112
  office_action_category_q: Filter by office action category (e.g., "CTNF").
97
113
  citation_category_code_q: Filter by citation category code (e.g., "X", "Y").
@@ -141,41 +157,22 @@ class EnrichedCitationsClient(BaseUSPTOClient[EnrichedCitationResponse]):
141
157
  # Build query from convenience parameters
142
158
  final_q = query
143
159
  if final_q is None:
144
- q_parts = []
145
- if patent_application_number_q:
146
- q_parts.append(f"patentApplicationNumber:{patent_application_number_q}")
147
- if cited_document_identifier_q:
148
- v = (
149
- f'"{cited_document_identifier_q}"'
150
- if " " in cited_document_identifier_q
151
- else cited_document_identifier_q
152
- )
153
- q_parts.append(f"citedDocumentIdentifier:{v}")
154
- if office_action_category_q:
155
- q_parts.append(f"officeActionCategory:{office_action_category_q}")
156
- if citation_category_code_q:
157
- q_parts.append(f"citationCategoryCode:{citation_category_code_q}")
158
- if tech_center_q:
159
- q_parts.append(f"techCenter:{tech_center_q}")
160
- if group_art_unit_number_q:
161
- q_parts.append(f"groupArtUnitNumber:{group_art_unit_number_q}")
162
- if examiner_cited_q is not None:
163
- q_parts.append(
164
- f"examinerCitedReferenceIndicator:{str(examiner_cited_q).lower()}"
165
- )
166
-
167
- # Handle office action date range
168
- if office_action_date_from_q and office_action_date_to_q:
169
- q_parts.append(
170
- f"officeActionDate:[{office_action_date_from_q} TO {office_action_date_to_q}]"
171
- )
172
- elif office_action_date_from_q:
173
- q_parts.append(f"officeActionDate:>={office_action_date_from_q}")
174
- elif office_action_date_to_q:
175
- q_parts.append(f"officeActionDate:<={office_action_date_to_q}")
176
-
177
- if q_parts:
178
- final_q = " AND ".join(q_parts)
160
+ final_q = all_of(
161
+ application_number(
162
+ "patentApplicationNumber", patent_application_number_q
163
+ ),
164
+ phrase("citedDocumentIdentifier", cited_document_identifier_q),
165
+ term("officeActionCategory", office_action_category_q),
166
+ term("citationCategoryCode", citation_category_code_q),
167
+ term("techCenter", tech_center_q),
168
+ term("groupArtUnitNumber", group_art_unit_number_q),
169
+ boolean("examinerCitedReferenceIndicator", examiner_cited_q),
170
+ date_range(
171
+ "officeActionDate",
172
+ office_action_date_from_q,
173
+ office_action_date_to_q,
174
+ ),
175
+ )
179
176
 
180
177
  if final_q is not None:
181
178
  body["criteria"] = final_q
@@ -10,6 +10,12 @@ from collections.abc import Iterator
10
10
  from typing import Any
11
11
 
12
12
  from pyUSPTO.clients.base import BaseUSPTOClient
13
+ from pyUSPTO.clients.query import (
14
+ all_of,
15
+ application_number,
16
+ date_range,
17
+ term,
18
+ )
13
19
  from pyUSPTO.config import USPTOConfig
14
20
  from pyUSPTO.models.oa_actions import (
15
21
  OAActionsFieldsResponse,
@@ -77,6 +83,11 @@ class OAActionsClient(BaseUSPTOClient[OAActionsResponse]):
77
83
  office action documents. You can provide either a direct post_body,
78
84
  a criteria string, or use convenience parameters.
79
85
 
86
+ Every convenience filter is a code filter (document codes, categories,
87
+ tech centers, art units). A value that contains spaces, such as
88
+ ``"rejected OR objected"``, is wrapped in parentheses, so the field
89
+ applies to every term.
90
+
80
91
  Args:
81
92
  criteria: Direct Solr query string (e.g., ``"patentApplicationNumber:14485382"``).
82
93
  sort: Sort order for results (e.g., ``"submissionDate desc"``).
@@ -85,6 +96,8 @@ class OAActionsClient(BaseUSPTOClient[OAActionsResponse]):
85
96
  post_body: Optional POST body dict for complex queries. When provided,
86
97
  all other parameters are ignored.
87
98
  patent_application_number_q: Filter by patent application number.
99
+ Any common form matches (``17/765,301``, ``17765301``). Write
100
+ each number in an ``OR`` or range expression as 8 digits.
88
101
  legacy_document_code_identifier_q: Filter by document code (e.g., ``"CTNF"``, ``"NOA"``).
89
102
  group_art_unit_number_q: Filter by group art unit number.
90
103
  tech_center_q: Filter by technology center code.
@@ -132,33 +145,19 @@ class OAActionsClient(BaseUSPTOClient[OAActionsResponse]):
132
145
  # Build criteria from convenience parameters
133
146
  final_criteria = criteria
134
147
  if final_criteria is None:
135
- q_parts = []
136
- if patent_application_number_q:
137
- q_parts.append(f"patentApplicationNumber:{patent_application_number_q}")
138
- if legacy_document_code_identifier_q:
139
- q_parts.append(
140
- f"legacyDocumentCodeIdentifier:{legacy_document_code_identifier_q}"
141
- )
142
- if group_art_unit_number_q is not None:
143
- q_parts.append(f"groupArtUnitNumber:{group_art_unit_number_q}")
144
- if tech_center_q:
145
- q_parts.append(f"techCenter:{tech_center_q}")
146
- if access_level_category_q:
147
- q_parts.append(f"accessLevelCategory:{access_level_category_q}")
148
- if application_type_category_q:
149
- q_parts.append(f"applicationTypeCategory:{application_type_category_q}")
150
-
151
- if submission_date_from_q and submission_date_to_q:
152
- q_parts.append(
153
- f"submissionDate:[{submission_date_from_q} TO {submission_date_to_q}]"
154
- )
155
- elif submission_date_from_q:
156
- q_parts.append(f"submissionDate:>={submission_date_from_q}")
157
- elif submission_date_to_q:
158
- q_parts.append(f"submissionDate:<={submission_date_to_q}")
159
-
160
- if q_parts:
161
- final_criteria = " AND ".join(q_parts)
148
+ final_criteria = all_of(
149
+ application_number(
150
+ "patentApplicationNumber", patent_application_number_q
151
+ ),
152
+ term("legacyDocumentCodeIdentifier", legacy_document_code_identifier_q),
153
+ term("groupArtUnitNumber", group_art_unit_number_q),
154
+ term("techCenter", tech_center_q),
155
+ term("accessLevelCategory", access_level_category_q),
156
+ term("applicationTypeCategory", application_type_category_q),
157
+ date_range(
158
+ "submissionDate", submission_date_from_q, submission_date_to_q
159
+ ),
160
+ )
162
161
 
163
162
  if final_criteria is not None:
164
163
  body["criteria"] = final_criteria
@@ -10,6 +10,14 @@ from collections.abc import Iterator
10
10
  from typing import Any
11
11
 
12
12
  from pyUSPTO.clients.base import BaseUSPTOClient
13
+ from pyUSPTO.clients.query import (
14
+ all_of,
15
+ application_number,
16
+ boolean,
17
+ contains,
18
+ date_range,
19
+ term,
20
+ )
13
21
  from pyUSPTO.config import USPTOConfig
14
22
  from pyUSPTO.models.oa_citations import (
15
23
  OACitationRecord,
@@ -79,6 +87,11 @@ class OACitationsClient(BaseUSPTOClient[OACitationsResponse]):
79
87
  Office Action citation records. You can provide either a direct post_body,
80
88
  a criteria string, or use convenience parameters.
81
89
 
90
+ Every convenience filter is a code filter (document codes, categories,
91
+ tech centers, art units). A value that contains spaces, such as
92
+ ``"rejected OR objected"``, is wrapped in parentheses, so the field
93
+ applies to every term.
94
+
82
95
  Args:
83
96
  criteria: Direct Solr query string (e.g., ``"patentApplicationNumber:16845502"``).
84
97
  sort: Sort order for results (e.g., ``"createDateTime desc"``).
@@ -87,6 +100,8 @@ class OACitationsClient(BaseUSPTOClient[OACitationsResponse]):
87
100
  post_body: Optional POST body dict for complex queries. When provided,
88
101
  all other parameters are ignored.
89
102
  patent_application_number_q: Filter by patent application number.
103
+ Any common form matches (``17/765,301``, ``17765301``). Write
104
+ each number in an ``OR`` or range expression as 8 digits.
90
105
  legal_section_code_q: Filter by legal section code (e.g., ``"101"``, ``"103"``).
91
106
  action_type_category_q: Filter by action type (e.g., ``"rejected"``).
92
107
  tech_center_q: Filter by technology center code.
@@ -137,37 +152,27 @@ class OACitationsClient(BaseUSPTOClient[OACitationsResponse]):
137
152
  # Build criteria from convenience parameters
138
153
  final_criteria = criteria
139
154
  if final_criteria is None:
140
- q_parts = []
141
- if patent_application_number_q:
142
- q_parts.append(f"patentApplicationNumber:{patent_application_number_q}")
143
- if legal_section_code_q:
144
- q_parts.append(f"legalSectionCode:*{legal_section_code_q}*")
145
- if action_type_category_q:
146
- q_parts.append(f"actionTypeCategory:{action_type_category_q}")
147
- if tech_center_q:
148
- q_parts.append(f"techCenter:{tech_center_q}")
149
- if work_group_q:
150
- q_parts.append(f"workGroup:{work_group_q}")
151
- if group_art_unit_number_q:
152
- q_parts.append(f"groupArtUnitNumber:{group_art_unit_number_q}")
153
- if examiner_cited_reference_indicator_q is not None:
154
- val = str(examiner_cited_reference_indicator_q).lower()
155
- q_parts.append(f"examinerCitedReferenceIndicator:{val}")
156
- if applicant_cited_examiner_reference_indicator_q is not None:
157
- val = str(applicant_cited_examiner_reference_indicator_q).lower()
158
- q_parts.append(f"applicantCitedExaminerReferenceIndicator:{val}")
159
-
160
- if create_date_time_from_q and create_date_time_to_q:
161
- q_parts.append(
162
- f"createDateTime:[{create_date_time_from_q} TO {create_date_time_to_q}]"
163
- )
164
- elif create_date_time_from_q:
165
- q_parts.append(f"createDateTime:>={create_date_time_from_q}")
166
- elif create_date_time_to_q:
167
- q_parts.append(f"createDateTime:<={create_date_time_to_q}")
168
-
169
- if q_parts:
170
- final_criteria = " AND ".join(q_parts)
155
+ final_criteria = all_of(
156
+ application_number(
157
+ "patentApplicationNumber", patent_application_number_q
158
+ ),
159
+ contains("legalSectionCode", legal_section_code_q),
160
+ term("actionTypeCategory", action_type_category_q),
161
+ term("techCenter", tech_center_q),
162
+ term("workGroup", work_group_q),
163
+ term("groupArtUnitNumber", group_art_unit_number_q),
164
+ boolean(
165
+ "examinerCitedReferenceIndicator",
166
+ examiner_cited_reference_indicator_q,
167
+ ),
168
+ boolean(
169
+ "applicantCitedExaminerReferenceIndicator",
170
+ applicant_cited_examiner_reference_indicator_q,
171
+ ),
172
+ date_range(
173
+ "createDateTime", create_date_time_from_q, create_date_time_to_q
174
+ ),
175
+ )
171
176
 
172
177
  if final_criteria is not None:
173
178
  body["criteria"] = final_criteria
@@ -10,6 +10,12 @@ from collections.abc import Iterator
10
10
  from typing import Any
11
11
 
12
12
  from pyUSPTO.clients.base import BaseUSPTOClient
13
+ from pyUSPTO.clients.query import (
14
+ all_of,
15
+ application_number,
16
+ date_range,
17
+ term,
18
+ )
13
19
  from pyUSPTO.config import USPTOConfig
14
20
  from pyUSPTO.models.oa_rejections import (
15
21
  OARejectionsFieldsResponse,
@@ -76,6 +82,11 @@ class OARejectionsClient(BaseUSPTOClient[OARejectionsResponse]):
76
82
  office action rejection records. You can provide either a direct
77
83
  post_body, a criteria string, or use convenience parameters.
78
84
 
85
+ Every convenience filter is a code filter (document codes, categories,
86
+ tech centers, art units). A value that contains spaces, such as
87
+ ``"rejected OR objected"``, is wrapped in parentheses, so the field
88
+ applies to every term.
89
+
79
90
  Args:
80
91
  criteria: Direct Solr query string (e.g., ``"patentApplicationNumber:12190351"``).
81
92
  sort: Sort order for results (e.g., ``"submissionDate desc"``).
@@ -84,6 +95,8 @@ class OARejectionsClient(BaseUSPTOClient[OARejectionsResponse]):
84
95
  post_body: Optional POST body dict for complex queries. When provided,
85
96
  all other parameters are ignored.
86
97
  patent_application_number_q: Filter by patent application number.
98
+ Any common form matches (``17/765,301``, ``17765301``). Write
99
+ each number in an ``OR`` or range expression as 8 digits.
87
100
  legacy_document_code_identifier_q: Filter by document code (e.g., ``"CTNF"``).
88
101
  group_art_unit_number_q: Filter by group art unit number.
89
102
  legal_section_code_q: Filter by legal section code.
@@ -130,31 +143,18 @@ class OARejectionsClient(BaseUSPTOClient[OARejectionsResponse]):
130
143
  # Build criteria from convenience parameters
131
144
  final_criteria = criteria
132
145
  if final_criteria is None:
133
- q_parts = []
134
- if patent_application_number_q:
135
- q_parts.append(f"patentApplicationNumber:{patent_application_number_q}")
136
- if legacy_document_code_identifier_q:
137
- q_parts.append(
138
- f"legacyDocumentCodeIdentifier:{legacy_document_code_identifier_q}"
139
- )
140
- if group_art_unit_number_q:
141
- q_parts.append(f"groupArtUnitNumber:{group_art_unit_number_q}")
142
- if legal_section_code_q:
143
- q_parts.append(f"legalSectionCode:{legal_section_code_q}")
144
- if action_type_category_q:
145
- q_parts.append(f"actionTypeCategory:{action_type_category_q}")
146
-
147
- if submission_date_from_q and submission_date_to_q:
148
- q_parts.append(
149
- f"submissionDate:[{submission_date_from_q} TO {submission_date_to_q}]"
150
- )
151
- elif submission_date_from_q:
152
- q_parts.append(f"submissionDate:>={submission_date_from_q}")
153
- elif submission_date_to_q:
154
- q_parts.append(f"submissionDate:<={submission_date_to_q}")
155
-
156
- if q_parts:
157
- final_criteria = " AND ".join(q_parts)
146
+ final_criteria = all_of(
147
+ application_number(
148
+ "patentApplicationNumber", patent_application_number_q
149
+ ),
150
+ term("legacyDocumentCodeIdentifier", legacy_document_code_identifier_q),
151
+ term("groupArtUnitNumber", group_art_unit_number_q),
152
+ term("legalSectionCode", legal_section_code_q),
153
+ term("actionTypeCategory", action_type_category_q),
154
+ date_range(
155
+ "submissionDate", submission_date_from_q, submission_date_to_q
156
+ ),
157
+ )
158
158
 
159
159
  if final_criteria is not None:
160
160
  body["criteria"] = final_criteria