@@ -109,15 +109,17 @@ def __init__(self, api_endpoint=API_ENDPOINT, username=USERNAME, password=PASSWO
109109 self .request_headers = {'Content-type' : 'application/json' , 'User-Agent' : self .USER_AGENT }
110110 self .list_request_headers = {'Content-type' : 'text/uri-list' , 'User-Agent' : self .USER_AGENT }
111111
112- def authenticate (self , retry = False ):
112+ def authenticate (self , user = None , password = None , retry = False ):
113113 """
114114 Authenticate with the DSpace REST API. As with other operations, perform XSRF refreshes when necessary.
115115 After POST, check /authn/status and log success if the authenticated json property is true
116116 @return: response object
117117 """
118+ user = user or self .USERNAME
119+ password = password or self .PASSWORD
118120 # Set headers for requests made during authentication
119121 # Get and update CSRF token
120- r = self .session .post (self .LOGIN_URL , data = {'user' : self . USERNAME , 'password' : self . PASSWORD },
122+ r = self .session .post (self .LOGIN_URL , data = {'user' : user , 'password' : password },
121123 headers = self .auth_request_headers )
122124 self .update_token (r )
123125
@@ -131,12 +133,12 @@ def authenticate(self, retry=False):
131133 return False
132134 else :
133135 logging .debug ("Retrying request with updated CSRF token" )
134- return self .authenticate (retry = True )
136+ return self .authenticate (user = user , password = password , retry = True )
135137
136138 if r .status_code == 401 :
137139 # 401 Unauthorized
138140 # If we get a 401, this means a general authentication failure
139- logging .error (f'Authentication failure: invalid credentials for user { self . USERNAME } ' )
141+ logging .error (f'Authentication failure: invalid credentials for user { user } ' )
140142 return False
141143
142144 # Update headers with new bearer token if present
@@ -148,7 +150,7 @@ def authenticate(self, retry=False):
148150 if r .status_code == 200 :
149151 r_json = parse_json (r )
150152 if 'authenticated' in r_json and r_json ['authenticated' ] is True :
151- logging .info (f'Authenticated successfully as { self . USERNAME } ' )
153+ logging .info (f'Authenticated successfully as { user } ' )
152154 return r_json ['authenticated' ]
153155
154156 # Default, return false
@@ -702,6 +704,7 @@ def create_bitstream(self, bundle=None, name=None, path=None, mime=None, metadat
702704 logging .error (f'Error creating bitstream: { r .status_code } : { r .text } ' )
703705 return None
704706
707+
705708 def download_bitstream (self , uuid = None ):
706709 """
707710 Download bitstream and return full response object including headers, and content
@@ -858,6 +861,24 @@ def get_item(self, uuid):
858861 logging .error (f'Invalid item UUID: { uuid } ' )
859862 return None
860863
864+ def get_items_by_handle (self , handle ):
865+ if handle is None :
866+ return None
867+ params = {
868+ "handle" : handle
869+ }
870+ url = f'{ self .API_ENDPOINT } /core/items/search/byHandle'
871+ try :
872+ r = self .api_get (url , params , None )
873+ r_json = parse_json (r )
874+ if '_embedded' in r_json :
875+ if 'items' in r_json ['_embedded' ]:
876+ return r_json ['_embedded' ]['items' ]
877+ return None
878+ except ValueError :
879+ logging .error (f'Invalid item handle: { handle } ' )
880+ return None
881+
861882 def get_items (self ):
862883 """
863884 Get all archived items for a logged-in administrator. Admin only! Usually you will want to
@@ -1128,3 +1149,149 @@ def update_resource_policy_group(self, policy_id, group_uuid):
11281149 body = f'{ self .API_ENDPOINT } /eperson/groups/{ group_uuid } '
11291150 r = self .api_put_uri (url , None , body , False )
11301151 return r
1152+
1153+ def get_clarinlruallowances (self ):
1154+ """
1155+ Fetch all clarinlruallowances.
1156+ """
1157+ url = f'{ self .API_ENDPOINT } /core/clarinlruallowances'
1158+ try :
1159+ response = self .api_get (url )
1160+ data = parse_json (response )
1161+ allowances = data .get ('_embedded' , {}).get ('clarinlruallowances' )
1162+ if allowances :
1163+ logging .info (f"Fetched { len (allowances )} CLARIN LRU allowances." )
1164+ return allowances
1165+ logging .warning ("No CLARIN LRU allowances found." )
1166+ except Exception as e :
1167+ logging .error (f"Error fetching CLARIN LRU allowances [{ url } ]: { e } " )
1168+ return None
1169+
1170+ def get_clarinlruallowances_by_bitstreama_and_user (self , bitstream_uuid , user_uuid ):
1171+ """
1172+ Fetch user allowances for a specific bitstream and user.
1173+ """
1174+ url = f'{ self .API_ENDPOINT } /core/clarinlruallowance/search/byBitstreamAndUser'
1175+ params = {'bitstreamUUID' : bitstream_uuid , 'userUUID' : user_uuid }
1176+ try :
1177+ response = self .api_get (url , params = params )
1178+ data = parse_json (response )
1179+ allowances = data .get ('_embedded' , {}).get ('clarinlruallowances' )
1180+ if allowances :
1181+ logging .info (f"Found { len (allowances )} user allowance(s)." )
1182+ return allowances
1183+ logging .warning (f"No user allowances found for user: { user_uuid } and bitstream: { bitstream_uuid } " )
1184+ except Exception as e :
1185+ logging .error (f"Error fetching user allowances: { e } " )
1186+ return None
1187+
1188+
1189+ def create_clarinlruallowances (self , bitstream_uuid ):
1190+ """
1191+ Create clarinlruallowances for a bitstream for logged user
1192+ by managing user metadata of bitstream.
1193+ """
1194+ url = f'{ self .API_ENDPOINT } /core/clarinusermetadata/manage'
1195+ params = {'bitstreamUUID' : bitstream_uuid }
1196+ metadata_payload = [
1197+ {"metadataKey" : "NAME" , "metadataValue" : "Test" }
1198+ ]
1199+ try :
1200+ response = self .api_post (url , json = metadata_payload , params = params )
1201+ if response .status_code == 200 :
1202+ logging .info (f"User metadata access managed for bitstream: { bitstream_uuid } " )
1203+ return True
1204+ logging .warning (f"Failed to manage user metadata: { response .status_code } " )
1205+ except Exception as e :
1206+ logging .error (f"Error managing user metadata: { e } " )
1207+ return False
1208+
1209+
1210+ def logout (self ):
1211+ """
1212+ Log out from the DSpace session.
1213+ """
1214+ try :
1215+ response = self .session .post (f'{ self .API_ENDPOINT } /authn/logout' , headers = self .request_headers )
1216+ if response .status_code == 204 :
1217+ self .session .cookies .clear ()
1218+ self .session .headers .pop ('Authorization' , None )
1219+ logging .info ("Logout successful." )
1220+ return True
1221+ else :
1222+ logging .error (f"Logout failed: { response .status_code } - { response .text } " )
1223+ except Exception as e :
1224+ logging .error (f"Logout error: { e } " )
1225+ return False
1226+
1227+ def get_http_status (self , url ):
1228+ """
1229+ Check the HTTP status code of a URL.
1230+ """
1231+ if not url :
1232+ logging .warning ("Provided URL is not defined." )
1233+ return None
1234+ try :
1235+ response = self .api_get (url )
1236+ logging .info (f"Checked URL status: { url } -> { response .status_code } " )
1237+ return response .status_code
1238+ except Exception as e :
1239+ logging .error (f"Error getting URL status for { url } : { e } " )
1240+ return None
1241+
1242+
1243+ def fetch_bundle (self , bundle_reference ):
1244+ """
1245+ Fetch a bundle either by UUID or URL.
1246+ """
1247+ if not bundle_reference :
1248+ logging .warning ("No bundle reference provided." )
1249+ return None
1250+
1251+ url = bundle_reference if "bundle" in bundle_reference else \
1252+ f'{ self .API_ENDPOINT } /core/{ bundle_reference } /bundle'
1253+ try :
1254+ response = self .api_get (url )
1255+ data = parse_json (response )
1256+ bundle = data .get ('_embedded' , {}).get ('bundles' , [{}])[0 ]
1257+ logging .info (f"Bundle retrieved: { bundle .get ('uuid' , 'unknown' )} " )
1258+ return bundle
1259+ except Exception as e :
1260+ logging .error (f"Error fetching bundle: { e } " )
1261+ return None
1262+
1263+ def fetch_bitstreams (self , bitstream_reference ):
1264+ """
1265+ Fetch bitstreams either by UUID or direct URL.
1266+ """
1267+ if not bitstream_reference :
1268+ logging .warning ("No bitstream reference provided." )
1269+ return None
1270+
1271+ url = bitstream_reference if "bitstream" in bitstream_reference else \
1272+ f'{ self .API_ENDPOINT } /core/{ bitstream_reference } /bitstream'
1273+ try :
1274+ response = self .api_get (url )
1275+ data = parse_json (response )
1276+ bitstreams = data .get ('_embedded' , {}).get ('bitstreams' , [])
1277+ logging .info (f"Fetched { len (bitstreams )} bitstream(s)." )
1278+ return bitstreams
1279+ except Exception as e :
1280+ logging .error (f"Error fetching bitstreams: { e } " )
1281+ return None
1282+
1283+
1284+ def get_user_by_email (self , email ):
1285+ """
1286+ Retrieve user details using their email address.
1287+ """
1288+ url = f'{ self .API_ENDPOINT } /eperson/epersons/search/byEmail'
1289+ params = {'email' : email }
1290+ try :
1291+ response = self .api_get (url , params = params )
1292+ user_data = parse_json (response )
1293+ logging .info (f"User fetched by email: { email } " )
1294+ return User (user_data )
1295+ except Exception as e :
1296+ logging .error (f"Error retrieving user by email { email } : { e } " )
1297+ return None
0 commit comments