Skip to content

Commit d600136

Browse files
committed
commit for 0.4.9:
preliminary support for PATCH functionality documented several library functions with extra information added support for various historical/legacy values to enable searching via pydantic validations. Signed-off-by: Neal Ensor <ensorn@osti.gov>
1 parent 1cec299 commit d600136

15 files changed

Lines changed: 253 additions & 19 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,11 @@
122122
## 0.4.8 - 6/5/2025
123123
- **bug** Fix issue with access limitation validation on new records
124124
- **bug** Fix typing of pams_publication_status field
125+
126+
## 0.4.9 - 6/17/2025
127+
- Add legacy access limitation values for validation
128+
- Modify PAMS publication status constants to reflect proper values (int to str)
129+
- Add "process_exceptions" to MediaFile data
130+
- Update to requests dependency (>=2.32.4) to address potential vulnerability
131+
- Support patch methods to update metadata through new endpoints
132+
- Added documentation and explanations to a number of library functions

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "elinkapi"
7-
version = "0.4.8"
7+
version = "0.4.9"
88
authors = [
99
{ name="Jacob Samar", email="samarj@osti.gov" },
1010
{ name="Neal Ensor", email="ensorn@osti.gov" }

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ charset-normalizer==3.3.2
44
idna==3.7
55
pydantic==2.8.2
66
pydantic-core==2.20.1
7-
requests==2.32.3
7+
requests>=2.32.4
88
requests-toolbelt==1.0.0
99
typing-extensions==4.12.2
1010
urllib3

src/elinkapi/auditlogs.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ class AuditLog(BaseModel):
66
"""
77
Descriptive audit logging of processing states this record has
88
undertaken during release.
9+
10+
Indicates back-end processing of a given Record, generally with
11+
a list of one or more informative messages, a status value of
12+
SUCCESS or FAIL for the operation, and the worker (type)
13+
performing this processing (DOI, RELEASER, etc.)
914
"""
1015
# List of one or more messages relevant to this event
1116
messages: List[str]

src/elinkapi/contribution.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
from enum import Enum
22

33
class Contribution(Enum):
4+
"""
5+
Defines the type of contribution for each CONTRIBUTING Person or Organization involved in the
6+
associated Record or product. Definitions of these types are available on the E-Link online
7+
API documentation at https://www.osti.gov/elink2api/#tag/person_model
8+
"""
49
Chair="Chair"
510
ContactPerson="ContactPerson"
611
DataCollector="DataCollector"

src/elinkapi/elinkapi.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,24 @@
1313
import mimetypes
1414

1515
class Elink:
16+
"""
17+
Defines a set of access points for E-Link API endpoints.
18+
19+
Construct an api access object, defining the desired API target and supplying the user-specific API key token.
20+
21+
Use this to access functions, including:
22+
23+
get_single_record -- obtain a particular Record by its OSTI ID
24+
query_records -- query E-Link records according to given parameters
25+
post_new_record -- create a new Record; returned with unique OSTI ID value
26+
update_record -- update the indicated record content by OSTI ID
27+
patch_record -- submit a partial JSON update to a given record by its OSTI ID
28+
patch_json -- submit a JSON-patch set of commands to update a given record by its OSTI ID
29+
get_media -- obtain all media sets associated with an OSTI ID
30+
post_media -- add a new media set to the OSTI ID
31+
put_media -- replace an existing media set with new content
32+
33+
"""
1634
def __init__(self, token=None, target=None):
1735
"""
1836
Set up the E-Link 2 OSTI API connector.
@@ -105,7 +123,8 @@ def query_records(self, **kwargs):
105123
the list of allowed query parameters.
106124
107125
Returns:
108-
List[Record] - A list of one or more matching metadata records, if found.
126+
a iterative Query object containing total_rows count matching the search, and data containing
127+
a page at a time of returned Record values as a List.
109128
"""
110129
query_params = ""
111130

@@ -162,6 +181,55 @@ def post_new_record(self, record, state="save"):
162181

163182
# returns array, so grab the first element
164183
return self._convert_response_to_records(response)[0]
184+
185+
def patch_record(self, osti_id, patch, state="save"):
186+
"""
187+
Update record via partial-patch-json method endpoint
188+
189+
Arguments:
190+
osti_id -- the OSTI ID of the record to patch
191+
patch -- JSON/dict containing the partial JSON to apply
192+
193+
Keyword arguments:
194+
state -- the desired workflow submission state ("save" or "submit") default: "save"
195+
196+
Returns:
197+
Record -- the metadata of the new record revision if successful
198+
"""
199+
response = requests.patch(f"{self.target}/records/{osti_id}/{state}",
200+
headers = {
201+
"Authorization" : f"Bearer {self.token}",
202+
"Content-Type": "application/json"
203+
},
204+
data=str(patch))
205+
206+
Validation.handle_response(response)
207+
208+
return self._convert_response_to_records(response)[0]
209+
210+
def patch_json(self, osti_id, jsonpatch, state="save"):
211+
"""
212+
Update record via a JSON-patch set of command operations.
213+
214+
:param osti_id: The OSTI ID of the record to patch
215+
:type osti_id: int
216+
217+
:param jsonpatch: The JSON or dict containing array of patch operations to perform
218+
:param state: The desired workflow state of the new revision ("save" or "submit") default: "save"
219+
220+
:return: a Record of the new revision if successful
221+
222+
"""
223+
response = requests.patch(f"{self.target}/records/{osti_id}/{state}",
224+
headers = {
225+
"Authorization" : f"Bearer {self.token}",
226+
"Content-Type": "application/json-patch+json"
227+
},
228+
data=str(jsonpatch))
229+
230+
Validation.handle_response(response)
231+
232+
return self._convert_response_to_records(response)[0]
165233

166234
def update_record(self, osti_id, record, state="save"):
167235
"""Update existing records at OSTI by unique OSTI ID

src/elinkapi/geolocation.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,28 @@
33
from typing import List
44

55
class Geolocation(BaseModel):
6+
"""
7+
Defines a particular geolocation point or area related to the associated record or product. It is made up of a
8+
List of Point values (latitude, longitude pairs) making up the geolocation construct.
9+
"""
610
model_config = ConfigDict(validate_assignment=True)
711

812
class Type(Enum):
13+
"""
14+
Indicates the TYPE of this particular geolocation construct.
15+
A POINT is a single set of latitude and longitude.
16+
A BOX indicates a NW and SE pair of latitude and longitude points making up the box area.
17+
A POLYGON should be any number of latitude and longitude pairs, starting and ending with the same value to complete
18+
the polygon construct.
19+
"""
920
POINT="Point"
1021
BOX="BOX"
1122
POLYGON="POLYGON"
1223

1324
class Point(BaseModel):
25+
"""
26+
Represents a single POINT, or pair of latitude and longitude values, that makes up part of the geolocation.
27+
"""
1428
model_config = ConfigDict(validate_assignment=True)
1529

1630
latitude: float

src/elinkapi/identifier.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22
from pydantic import BaseModel, ConfigDict, field_validator
33

44
class Identifier(BaseModel):
5+
"""
6+
Different types of identifying numbers, including DOE contract numbers, report numbers, ISSN, ISBN, or other identifying numbers
7+
associated with the product or record. Each element requires a "type" (enumerated by an Identifier.Type value) and the
8+
"value" of the identifier.
9+
"""
510
model_config = ConfigDict(validate_assignment=True)
611

712
class Type(Enum):
13+
"""
14+
Indicates the particular TYPE of this identifier.
15+
"""
816
AUTH_REVISION_NUMBER="AUTH_REV"
917
AWARD_DOI="AWARD_DOI"
1018
DOE_CONTRACT_NUMBER="CN_DOE"

src/elinkapi/media_file.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@
44
import datetime
55

66
class MediaFile(BaseModel):
7+
"""
8+
Represents a particular full text file or URL associated with a
9+
MediaSet.
10+
11+
Each media file or URL is uniquely identified by its media_file_id
12+
value. The url_type indicates whether it is "L" (local to OSTI) or
13+
"O" (offsite URL reference). The "url" indicates the file pathname or
14+
offsite URL value of this MediaFile record.
15+
16+
Many of the other fields are administrative, and supplied during
17+
media processing by the backend workers, as applicable to the
18+
media file type.
19+
20+
Any issues or failures during processing are usually detailed by the
21+
processing_exceptions value. The "status" is generally "DONE" for
22+
those that have completed processing successfully, but may be "OCR"
23+
indicating awaiting background OCR processing, or "FAIL" if the
24+
media file failed processing in some way.
25+
"""
726
model_config = ConfigDict(validate_assignment=True)
827

928
class UrlType(Enum):
@@ -15,6 +34,7 @@ class UrlType(Enum):
1534
revision: int = None
1635
parent_media_file_id: int = None
1736
status: str = None
37+
processing_exceptions: str = None
1838
media_type: str = None
1939
url_type: str = None
2040
url: str = None

src/elinkapi/media_info.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@
44
from .media_file import MediaFile
55

66
class MediaInfo(BaseModel):
7+
"""
8+
Information about an associated "media set" for this product. A media set is defined as one or more
9+
files or URLs to full text, along with any derived content (such as OCR versions, text extracted from
10+
full text, cached URL contents, etc.) generated during media processing.
11+
12+
Most fields are administrative, and set during processing. An entire set is uniquely identified by
13+
its media_id, and will be also associated with the osti_id of the record to which it is attached.
14+
15+
The status value generally will indicate its processing status; "C" indicates completed, "P" indicates
16+
in processing, "X" indicating a failure during media processing.
17+
18+
See MediaFile class for additional information on individual files or URLs making up this media set.
19+
"""
720
model_config = ConfigDict(validate_assignment=True)
821

922
media_id: int = None

0 commit comments

Comments
 (0)