1
2
3
4 """
5 This module provides the 'Connection' class, a SWORD2 client.
6
7 #BETASWORD2URL
8 See http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD for information
9 about the SWORD2 AtomPub profile.
10
11 """
12 from sword2_logging import logging
13 conn_l = logging.getLogger(__name__)
14
15 from utils import Timer, NS, get_md5, create_multipart_related
16
17 from transaction_history import Transaction_History
18 from service_document import ServiceDocument
19 from deposit_receipt import Deposit_Receipt
20 from error_document import Error_Document
21 from collection import Sword_Statement
22 from exceptions import *
23
24 from compatible_libs import etree
25
26 import httplib2
27
29 """
30 `Connection` - SWORD2 client
31
32 This connection is predicated on having a Service Document (SD), preferably by an instance being constructed with
33 the Service Document IRI (SD-IRI) which can dereference to the XML document itself.
34
35
36 Contructor parameters:
37
38 There are a number of flags that can be set when getting an instance of this class that affect the behaviour
39 of the client. See the help for `self.__init__` for more details.
40
41 Example usage:
42
43 >>> from sword2 import Connection
44 >>> conn = Connection("http://example.org/service-doc") # An SD-IRI is required.
45
46
47
48
49 # Get, validate and parse the document at the SD_IRI:
50 >>> conn.get_service_document()
51
52 # Load a Service Document from a string:
53 >>> conn.load_service_document(xml_service_doc)
54 2011-05-30 01:06:13,251 - sword2.service_document - INFO - Initial SWORD2 validation checks on service document - Valid document? True
55
56 # View transaction history (if enabled)
57 >>> print conn.history.to_pretty_json()
58 [
59 {
60 "sd_iri": "http://example.org/service-doc",
61 "timestamp": "2011-05-30T01:05:54.071042",
62 "on_behalf_of": null,
63 "type": "init",
64 "user_name": null
65 },
66 {
67 "IRI": "http://example.org/service-doc",
68 "valid": true,
69 "sword_version": "2.0",
70 "duration": 0.0029349327087402344,
71 "timestamp": "2011-05-30T01:06:13.253907",
72 "workspaces_found": [
73 "Main Site",
74 "Sub-site"
75 ],
76 "type": "SD Parse",
77 "maxUploadSize": 16777216
78 }
79 ]
80
81 # Start a connection and do not maintain a transaction history
82 # Useful for bulk-testing where the history might grow exponentially
83 >>> conn = Connection(...... , keep_history=False, ....)
84
85 # Initialise a connection and get the document at the SD IRI:
86 # (Uses the Simple Sword Server as an endpoint - sss.py
87
88 >>> from sword2 import Connection
89 >>> c = Connection("http://localhost:8080/sd-uri", download_service_document=True)
90 2011-05-30 02:04:24,179 - sword2.connection - INFO - keep_history=True--> This instance will keep a JSON-compatible transaction log of all (SWORD/APP) activities in 'self.history'
91 2011-05-30 02:04:24,215 - sword2.connection - INFO - Received a document for http://localhost:8080/sd-uri
92 2011-05-30 02:04:24,216 - sword2.service_document - INFO - Initial SWORD2 validation checks on service document - Valid document? True
93 >>> print c.history
94 --------------------
95 Type: 'init' [2011-05-30T02:04:24.180182]
96 Data:
97 user_name: None
98 on_behalf_of: None
99 sd_iri: http://localhost:8080/sd-uri
100 --------------------
101 Type: 'SD_IRI GET' [2011-05-30T02:04:24.215661]
102 Data:
103 sd_iri: http://localhost:8080/sd-uri
104 response: {'status': '200', 'content-location': 'http://localhost:8080/sd-uri', 'transfer-encoding': 'chunked', 'server': 'CherryPy/3.1.2 WSGI Server', 'date': 'Mon, 30 May 2011 01:04:24 GMT', 'content-type': 'text/xml'}
105 process_duration: 0.0354170799255
106 --------------------
107 Type: 'SD Parse' [2011-05-30T02:04:24.220798]
108 Data:
109 maxUploadSize: 16777216
110 sd_iri: http://localhost:8080/sd-uri
111 valid: True
112 sword_version: 2.0
113 workspaces_found: ['Main Site']
114 process_duration: 0.00482511520386
115
116 Please see the testsuite for this class for more examples of the sorts of transactions that can be done. (tests/test_connection*.py)
117 """
118
119 - def __init__(self, service_document_iri,
120 user_name=None,
121 user_pass=None,
122 on_behalf_of=None,
123 download_service_document = False,
124 keep_history=True,
125 cache_deposit_receipts=True,
126 honour_receipts=True,
127 error_response_raises_exceptions=True):
128 """
129 Creates a new Connection object.
130
131 Parameters:
132
133 Connection(service_document_iri, <--- REQUIRED - use a dummy string here if the SD is local only.
134
135 # OPTIONAL parameters (default values are shown below)
136
137 # Authentication parameters: (can use any method that `httplib2` provides)
138
139 user_name=None,
140 user_pass=None,
141
142 # Set the SWORD2 On Behalf Of value here, for it to be included as part of every transaction
143 # Can be passed to every transaction method (update resource, delete deposit, etc) otherwise
144
145 on_behalf_of=None,
146
147 ## Behaviour Flags
148 # Try to GET the service document from the provided SD-IRI in `service_document_iri` if True
149
150 download_service_document = False, # Don't automagically GET the SD_IRI by default
151
152 # Keep a history of all transactions made with the SWORD2 Server
153 # Records details like the response headers, sent headers, times taken and so forth
154 # Kept in a `sword2.transaction_history:Transaction_History` object but can be treated like an ordinary `list`
155 keep_history=True,
156
157 # Keep a cache of all deposit receipt responses from the server and provide an 'index' to these `sword2.Deposit_Receipt` objects
158 # by Edit-IRI, Content-IRI and Sword-Edit-IRI. (ie given an Edit-IRI, find the deposit receipt for the last received response containing
159 # that IRI.
160 # If the following flag, `honour_receipts` is set to True, packaging checks and other limits set in these receipts will be
161 # honoured.
162 # For example, a request for an item with an invalid packaging type will never reach the server, but throw an exception.
163
164 cache_deposit_receipts=True,
165
166 # Make sure to behave as required by the SWORD2 server - not sending too large a file, not asking for invalid packaging types and so on.
167
168 honour_receipts=True,
169
170 # Two means of handling server error responses:
171 # If set to True - An exception will be thrown from `sword2.exceptions` (caused by any server error response w/
172 # HTTP code greater than or equal to 400)
173 # OR
174 # If set to False - A `sword2.error_document:Error_Document` object will be returned.
175
176 error_response_raises_exceptions=True
177 )
178
179 If a `Connection` is created with the parameter `download_service_document` set to `False`, then no attempt
180 to dereference the `service_document_iri` (SD-IRI) will be made at this stage.
181
182 To cause it to get or refresh the service document from this IRI, call `self.get_service_document()`
183
184 Loading in a locally held Service Document:
185
186 >>> conn = Connection(....)
187
188 >>> with open("service_doc.xml", "r") as f:
189 ... conn.load_service_document(f.read())
190
191
192 """
193 self.sd_iri = service_document_iri
194 self.sd = None
195
196
197
198
199 self.honour_receipts = honour_receipts
200
201
202
203
204
205
206
207 self.raise_except = error_response_raises_exceptions
208
209 self.keep_cache = cache_deposit_receipts
210 self.h = httplib2.Http(".cache", timeout=30.0)
211 self.user_name = user_name
212 self.on_behalf_of = on_behalf_of
213
214
215 self.edit_iris = {}
216 self.cont_iris = {}
217 self.se_iris = {}
218 self.cached_at = {}
219
220
221 self.history = None
222 self._t = Timer()
223 self.keep_history = keep_history
224 if keep_history:
225 conn_l.info("keep_history=True--> This instance will keep a JSON-compatible transaction log of all (SWORD/APP) activities in 'self.history'")
226 self.reset_transaction_history()
227 self.history.log('init',
228 sd_iri = self.sd_iri,
229 user_name = self.user_name,
230 on_behalf_of = self.on_behalf_of )
231
232 if user_name:
233 conn_l.info("Adding username/password credentials for the client to use.")
234 self.h.add_credentials(user_name, user_pass)
235
236 if self.sd_iri and download_service_document:
237 self._t.start("get_service_document")
238 self.get_service_document()
239 conn_l.debug("Getting service document and dealing with the response: %s s" % self._t.time_since_start("get_service_document")[1])
240
242 """Internal method for reporting errors, behaving as the `self.raise_except` flag requires.
243
244 `self.raise_except` can be altered at any time to affect this methods behaviour."""
245 if self.raise_except:
246 raise cls(resp)
247 else:
248 if resp['content-type'] in ['text/xml', 'application/xml']:
249 conn_l.info("Returning an error document, due to HTTP response code %s" % resp.status)
250 e = Error_Document(content, code=resp.status, resp = resp)
251 return e
252 else:
253 conn_l.info("Returning due to HTTP response code %s" % resp.status)
254 e = Error_Document(code=resp.status, resp = resp)
255 return e
256
258 """Catch a number of general HTTP error responses from the server, based on HTTP code
259
260 401 - Unauthorised.
261 Will throw a `sword2.exceptions.NotAuthorised` exception, if exceptions are set to be on.
262 Otherwise will return a `sword2.Error_Document` (likewise for the rest of these)
263
264 403 - Forbidden.
265 Will throw a `sword2.exceptions.Forbidden` exception
266
267 404 - Not Found.
268 Will throw a `sword2.exceptions.NotFound` exception
269
270 408 - Request Timeout
271 Will throw a `sword2.exceptions.RequestTimeOut` exception
272
273 500-599 errors:
274 Will throw a general `sword2.exceptions.ServerError` exception
275
276 4XX not listed:
277 Will throw a general `sword2.exceptions.HTTPResponseError` exception
278 """
279 if resp['status'] == "401":
280 conn_l.error("You are unauthorised (401) to access this document on the server. Check your username/password credentials and your 'On Behalf Of'")
281 self._return_error_or_exception(NotAuthorised, resp, content)
282 elif resp['status'] == "403":
283 conn_l.error("You are Forbidden (401) to POST to '%s'. Check your username/password credentials and your 'On Behalf Of'")
284 self._return_error_or_exception(Forbidden, resp, content)
285 elif resp['status'] == "408":
286 conn_l.error("Request Timeout (408) - error uploading.")
287 self._return_error_or_exception(RequestTimeOut, resp, content)
288 elif int(resp['status']) > 499:
289 conn_l.error("Server error occured. Response headers from the server:\n%s" % resp)
290 self._return_error_or_exception(ServerError, resp, content)
291 else:
292 conn_l.error("Unknown error occured. Response headers from the server:\n%s" % resp)
293 self._return_error_or_exception(HTTPResponseError, resp, content)
294
296 """Method for storing the deposit receipts, and also for providing lookup dictionaries that
297 reference these objects.
298
299 (only provides cache if `self.keep_cache` is `True` [via the `cache_deposit_receipts` init parameter flag])
300
301 Provides and maintains:
302 self.edit_iris -- a `dict`, keys: Edit-IRI hrefs, values: `sword2.Deposit_Receipt` objects they appear in
303
304 self.cont_iris -- a `dict`, keys: Content-IRI hrefs, values: `sword2.Deposit_Receipt` objects they appear in
305
306 self.se_iris -- a `dict`, keys: Sword-Edit-IRI hrefs, values: `sword2.Deposit_Receipt` objects they appear in
307
308 self.cached_at -- a `dict`, keys: Edit-IRIs, values: timestamp when receipt was last cached.
309 """
310 if self.keep_cache:
311 timestamp = self._t.get_timestamp()
312 conn_l.debug("Caching document (Edit-IRI:%s) - at %s" % (d.edit, timestamp))
313 self.edit_iris[d.edit] = d
314 if d.cont_iri:
315 self.cont_iris[d.cont_iri] = d
316 if d.se_iri:
317
318
319 self.se_iris[d.se_iri] = d
320 self.cached_at[d.edit] = self._t.get_timestamp()
321 else:
322 conn_l.debug("Caching request denied - deposit receipt caching is set to 'False'")
323
325 """Load the Service Document XML from bytestring, `xml_document`
326
327 Useful if SD-IRI is non-existant or invalid.
328
329 Will set the following convenience attributes:
330
331 `self.sd` -- the `sword2.ServiceDocument` instance
332
333 `self.workspaces` -- a `list` of workspace tuples, of the form:
334 ('Workspace atom:title', [<`sword2.Collection` object>, ....]),
335
336 `self.maxUploadSize` -- the maximum filesize for a deposit, if given in the service document
337 """
338 self._t.start("SD Parse")
339 self.sd = ServiceDocument(xml_document)
340 _, took_time = self._t.time_since_start("SD Parse")
341
342 self.workspaces = self.sd.workspaces
343 self.maxUploadSize = self.sd.maxUploadSize
344
345 if self.history:
346 if self.sd.valid:
347 self.history.log('SD Parse',
348 sd_iri = self.sd_iri,
349 valid = self.sd.valid,
350 workspaces_found = [k for k,v in self.sd.workspaces],
351 sword_version = self.sd.version,
352 maxUploadSize = self.sd.maxUploadSize,
353 process_duration = took_time)
354 else:
355 self.history.log('SD Parse',
356 sd_iri = self.sd_iri,
357 valid = self.sd.valid,
358 process_duration = took_time)
359
361 """Perform an HTTP GET on the Service Document IRI (SD-IRI) and attempt to parse the result as
362 a SWORD2 Service Document (using `self.load_service_document`)
363 """
364 headers = {}
365 if self.on_behalf_of:
366 headers['on-behalf-of'] = self.on_behalf_of
367 self._t.start("SD_URI request")
368 resp, content = self.h.request(self.sd_iri, "GET", headers=headers)
369 _, took_time = self._t.time_since_start("SD_URI request")
370 if self.history:
371 self.history.log('SD_IRI GET',
372 sd_iri = self.sd_iri,
373 response = resp,
374 process_duration = took_time)
375 if resp['status'] == "200":
376 conn_l.info("Received a document for %s" % self.sd_iri)
377 self.load_service_document(content)
378 elif resp['status'] == "401":
379 conn_l.error("You are unauthorised (401) to access this document on the server. Check your username/password credentials")
380
382 """ Clear the transaction history - `self.history`"""
383 del self.history
384 self.history = Transaction_History()
385
386 - def _make_request(self,
387 target_iri,
388 payload=None,
389 mimetype=None,
390 filename=None,
391 packaging=None,
392
393 metadata_entry=None,
394
395
396
397
398 suggested_identifier=None,
399 in_progress=True,
400 on_behalf_of=None,
401 metadata_relevant=False,
402
403
404 empty = None,
405
406 method = "POST",
407 request_type=""
408 ):
409 """Performs an HTTP request, as defined by the parameters. This is an internally used method and it is best that it
410 is not called directly.
411
412 target_iri -- IRI that will be the target of the HTTP call
413
414 # File upload parameters:
415 payload - the payload to send. Can be either a bytestring or a File-like object that supports `payload.read()`
416 mimetype - MIMEType of the payload
417 filename - filename. Most SWORD2 uploads have this as being mandatory.
418 packaging - the SWORD2 packaging type of the payload.
419 eg packaging = 'http://purl.org/net/sword/package/Binary'
420
421 # NB to work around a possible bug in httplib2 0.6.0, the file-like object is read into memory rather than streamed
422 # from disc, so is not as efficient as it should be. That said, it is recommended that file handles are passed to
423 # the _make_request method, as this is hoped to be a temporary situation.
424
425 metadata_entry - a `sword2.Entry` to be uploaded with metadata fields set as desired.
426
427 # If there is both a payload and a metadata_entry, then the request will be made as a Multipart-related request
428 # Otherwise, it will be a normal request for whicever type of upload.
429
430 empty - a flag to specify that an empty request should be made. A blank body and a 'Content-Length:0' header will be explicitly added
431 and any payload or metadata_entry passed in will be ignored.
432
433
434 # Header flags:
435 suggested_identifier -- set the 'Slug' header
436 in_progress -- 'In-Progress'
437 on_behalf_of -- 'On-Behalf-Of'
438 metadata_relevant -- 'Metadata-Relevant'
439
440 # HTTP settings:
441 method -- "GET", "POST", etc
442 request_type -- A label to be used in the transaction history for this particular operation.
443
444 Response:
445
446 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
447 not a Deposit Response, then only a few attributes will be populated:
448
449 `code` -- HTTP code of the response
450 `response_headers` -- `dict` of the reponse headers
451 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
452
453 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
454 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
455 response_headers, etc)
456 """
457 if payload:
458 md5sum, f_size = get_md5(payload)
459
460
461 headers = {}
462 headers['In-Progress'] = str(in_progress).lower()
463 if on_behalf_of:
464 headers['On-Behalf-Of'] = self.on_behalf_of
465 elif self.on_behalf_of:
466 headers['On-Behalf-Of'] = self.on_behalf_of
467
468 if suggested_identifier:
469 headers['Slug'] = str(suggested_identifier)
470
471 if suggested_identifier:
472 headers['Slug'] = str(suggested_identifier)
473
474 if metadata_relevant:
475 headers['Metadata-Relevant'] = str(metadata_relevant).lower()
476
477 if hasattr(payload, 'read'):
478
479
480
481 payload = payload.read()
482
483 self._t.start(request_type)
484 if empty:
485
486 headers['Content-Length'] = "0"
487 resp, content = self.h.request(target_iri, method, headers=headers)
488 _, took_time = self._t.time_since_start(request_type)
489 if self.history:
490 self.history.log(request_type + ": Empty request",
491 sd_iri = self.sd_iri,
492 target_iri = target_iri,
493 method = method,
494 response = resp,
495 headers = headers,
496 process_duration = took_time)
497 elif method == "DELETE":
498 resp, content = self.h.request(target_iri, method, headers=headers)
499 _, took_time = self._t.time_since_start(request_type)
500 if self.history:
501 self.history.log(request_type + ": DELETE request",
502 sd_iri = self.sd_iri,
503 target_iri = target_iri,
504 method = method,
505 response = resp,
506 headers = headers,
507 process_duration = took_time)
508
509 elif metadata_entry and not (filename and payload):
510
511 headers['Content-Type'] = "application/atom+xml;type=entry"
512 data = str(metadata_entry)
513 headers['Content-Length'] = str(len(data))
514
515 resp, content = self.h.request(target_iri, method, headers=headers, body = data)
516 _, took_time = self._t.time_since_start(request_type)
517 if self.history:
518 self.history.log(request_type + ": Metadata-only resource request",
519 sd_iri = self.sd_iri,
520 target_iri = target_iri,
521 method = method,
522 response = resp,
523 headers = headers,
524 process_duration = took_time)
525
526 elif metadata_entry and filename and payload:
527
528 multicontent_type, payload_data = create_multipart_related([{'key':'atom',
529 'type':'application/atom+xml; charset="utf-8"',
530 'data':str(metadata_entry),
531 },
532 {'key':'payload',
533 'type':str(mimetype),
534 'filename':filename,
535 'data':payload,
536 'headers':{'Content-MD5':str(md5sum),
537 'Packaging':str(packaging),
538 }
539 }
540 ])
541
542 headers['Content-Type'] = multicontent_type + '; type="application/atom+xml"'
543 headers['Content-Length'] = str(len(payload_data))
544 resp, content = self.h.request(target_iri, method, headers=headers, body = payload_data)
545 _, took_time = self._t.time_since_start(request_type)
546 if self.history:
547 self.history.log(request_type + ": Multipart resource request",
548 sd_iri = self.sd_iri,
549 target_iri = target_iri,
550 response = resp,
551 headers = headers,
552 method = method,
553 multipart = [{'key':'atom',
554 'type':'application/atom+xml; charset="utf-8"'
555 },
556 {'key':'payload',
557 'type':str(mimetype),
558 'filename':filename,
559 'headers':{'Content-MD5':str(md5sum),
560 'Packaging':str(packaging),
561 }
562 }],
563 process_duration = took_time)
564 elif filename and payload:
565 headers['Content-Type'] = str(mimetype)
566 headers['Content-MD5'] = str(md5sum)
567 headers['Content-Length'] = str(f_size)
568 headers['Content-Disposition'] = "attachment; filename=%s" % filename
569 headers['Packaging'] = str(packaging)
570
571 resp, content = self.h.request(target_iri, method, headers=headers, body = payload)
572 _, took_time = self._t.time_since_start(request_type)
573 if self.history:
574 self.history.log(request_type + ": simple resource request",
575 sd_iri = self.sd_iri,
576 target_iri = target_iri,
577 method = method,
578 response = resp,
579 headers = headers,
580 process_duration = took_time)
581 else:
582 conn_l.error("Parameters were not complete: requires a metadata_entry, or a payload/filename/packaging or both")
583 raise Exception("Parameters were not complete: requires a metadata_entry, or a payload/filename/packaging or both")
584
585 if resp['status'] == "201":
586
587 conn_l.info("Received a Resource Created (201) response.")
588
589 location = resp.get('location', None)
590 if len(content) > 0:
591
592 d = Deposit_Receipt(xml_deposit_receipt = content)
593 if d.parsed:
594 conn_l.info("Server response included a Deposit Receipt. Caching a copy in .resources['%s']" % d.edit)
595 d.response_headers = dict(resp)
596 d.location = location
597 d.code = 201
598 self._cache_deposit_receipt(d)
599 return d
600 else:
601
602 d = Deposit_Receipt()
603 conn_l.info("Server response dir not include a Deposit Receipt.")
604 d.response_headers = dict(resp)
605 d.code = 201
606 d.location = location
607 return d
608 elif resp['status'] == "204":
609
610 conn_l.info("Received a valid 'No Content' (204) response.")
611 location = resp.get('location', None)
612
613 return Deposit_Receipt(response_headers = dict(resp), location=location, code=204)
614 elif resp['status'] == "200":
615
616 conn_l.info("Received a valid (200) OK response.")
617 content_type = resp.get('content-type')
618 location = resp.get('location', None)
619 if content_type == "application/atom+xml;type=entry" and len(content) > 0:
620 d = Deposit_Receipt(content)
621 if d.parsed:
622 conn_l.info("Server response included a Deposit Receipt. Caching a copy in .resources['%s']" % d.edit)
623 d.response_headers = dict(resp)
624 d.location = location
625 d.code = 200
626 self._cache_deposit_receipt(d)
627 return d
628 else:
629
630 d = Deposit_Receipt()
631 conn_l.info("Server response dir not include a Deposit Receipt Entry.")
632 d.response_headers = dict(resp)
633 d.location = location
634 d.content = content
635 return d
636 else:
637 return self._handle_error_response(resp, content)
638
639
640
641 - def create(self,
642 workspace=None,
643 collection=None,
644 col_iri=None,
645
646 payload=None,
647 mimetype=None,
648 filename=None,
649 packaging=None,
650
651 metadata_entry=None,
652
653
654
655
656
657 suggested_identifier=None,
658 in_progress=True,
659 on_behalf_of=None,
660 ):
661 """
662 Creating a Resource
663 ===================
664
665 #BETASWORD2URL
666 See 6.3 Creating a Resource http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD#protocoloperations_creatingresource
667
668 Basic parameters:
669
670 This method can create a new resource in a Collection on a SWORD2 server, given suitable authentication to do so.
671
672 Select a collection to send a request to by either:
673
674 setting the param `col_iri` to its Collection-IRI or Col-IRI
675
676 or
677
678 setting 'workspace' and 'collection' to the labels for the desired workspace and collection.
679
680 SWORD2 request parameters:
681
682 `suggested_identifier` -- the suggested identifier of this resource (HTTP header of 'Slug'),
683
684 `in_progress` (`True` or `False`) -- whether or not the deposit should be considered by the
685 server to be in progress ('In-Progress')
686 `on_behalf_of` -- if this is a mediated deposit ('On-Behalf-Of')
687 (the client-wide setting `self.on_behalf_of will be used otherwise)
688
689
690 1. "Binary File Deposit in a given Collection"
691 ----------------------------------------------
692
693 Set the following parameters in addition to the basic parameters:
694
695 `payload` - the payload to send. Can be either a bytestring or a File-like object that supports `payload.read()`
696 `mimetype` - MIMEType of the payload
697 `filename` - filename. Most SWORD2 uploads have this as being mandatory.
698 `packaging` - the SWORD2 packaging type of the payload.
699 eg packaging = 'http://purl.org/net/sword/package/Binary'
700
701 Response:
702
703 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
704 not a Deposit Response, then only a few attributes will be populated:
705
706 `code` -- HTTP code of the response
707 `response_headers` -- `dict` of the reponse headers
708 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
709
710 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
711 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
712 response_headers, etc)
713
714 2. "Creating a Resource with an Atom Entry"
715 -------------------------------------------
716
717 create a container within a SWORD server and optionally provide it with metadata without adding any binary content to it.
718
719 Set the following parameters in addition to the basic parameters:
720
721 `metadata_entry` - An instance of `sword2.Entry`, set with the metadata required.
722
723 for example:
724 # conn = `sword2.Connection`, collection_iri = Collection-IRI
725 >>> from sword2 import Entry
726 >>> entry = Entry(title = "My new deposit",
727 ... id = "foo:id",
728 ... dcterms_abstract = "My Thesis",
729 ... dcterms_author = "Me",
730 ... dcterms_issued = "2009")
731
732 >>> conn.create(col_iri = collection_iri,
733 ... metadata_entry = entry,
734 ... in_progress = True)
735 # likely to want to add the thesis files later for example but get the identifier for the deposit now
736
737 Response:
738
739 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
740 not a Deposit Response, then only a few attributes will be populated:
741
742 `code` -- HTTP code of the response
743 `response_headers` -- `dict` of the reponse headers
744 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
745
746 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
747 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
748 response_headers, etc)
749
750 3. "Creating a Resource with a Multipart Deposit"
751 -------------------------------------------------
752
753 Create a resource in a given collection by uploading a file AND the metadata about this resource.
754
755 To make this sort of request, just set the parameters as shown for both the binary upload and the metadata upload.
756
757 eg:
758
759 >>> conn.create(col_iri = collection_iri,
760 ... metadata_entry = entry,
761 ... payload = open("foo.zip", "r"),
762 ... mimetype =
763 .... and so on
764
765 Response:
766
767 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
768 not a Deposit Response, then only a few attributes will be populated:
769
770 `code` -- HTTP code of the response
771 `response_headers` -- `dict` of the reponse headers
772 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
773
774 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
775 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
776 response_headers, etc)
777
778 (under the hood, this request uses Atom Multipart-related)
779
780 From the spec:
781
782 "In order to ensure that all SWORD clients and servers can exchange a full range of file content and metadata, the use of Atom Multipart [AtomMultipart] is permitted to combine a package (possibly a simple ZIP) with a set of Dublin Core metadata terms [DublinCore] embedded in an Atom Entry.
783
784 The SWORD server is not required to support packaging formats, but this profile RECOMMENDS that the server be able to accept a ZIP file as the Media Part of an Atom Multipart request (See Section 5: IRIs and Section 7: Packaging for more details)."
785 """
786 conn_l.debug("Create Resource")
787 if not col_iri:
788 for w, collections in self.workspaces:
789 if w == workspace:
790 for c in collections:
791 if c.title == collection:
792 conn_l.debug("Matched: Workspace='%s', Collection='%s' ==> Col-IRI='%s'" % (workspace,
793 collection,
794 c.href))
795 col_iri = c.href
796 break
797
798 if not col_iri:
799 conn_l.error("No suitable Col-IRI was found, with the given parameters.")
800 return
801
802 return self._make_request(target_iri = col_iri,
803 payload=payload,
804 mimetype=mimetype,
805 filename=filename,
806 packaging=packaging,
807 metadata_entry=metadata_entry,
808 suggested_identifier=suggested_identifier,
809 in_progress=in_progress,
810 on_behalf_of=on_behalf_of,
811 method="POST",
812 request_type='Col_IRI POST')
813
814 - def update(self, metadata_entry = None,
815 payload = None,
816 filename = None,
817 mimetype=None,
818 packaging=None,
819
820 dr = None,
821
822 edit_iri = None,
823 edit_media_iri = None,
824
825 metadata_relevant=False,
826 in_progress=False,
827 on_behalf_of=None,
828 ):
829 """
830 Replacing the Metadata and/or Files of a Resource
831
832 #BETASWORD2URL
833 See http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD#protocoloperations_editingcontent_multipart
834
835 Replace the metadata and/or files of a resource.
836
837 This wraps a number of methods and relies on being passed the Deposit Receipt, as the target IRI changes depending
838 on whether the metadata, the files or both are to be updated by the request.
839
840 This method has the same functionality as the following methods:
841 update_files_for_resource
842 update_metadata_for_resource
843 update_metadata_and_files_for_resource
844
845 Usage:
846 ------
847
848 Set the target for this request:
849 --------------------------------
850
851 You MUST pass back the `sword2.Deposit_Receipt` object you got from a previous transaction as the `dr` parameter,
852 and the correct IRI will automatically be chosen based on what combination of files you want to upload.
853
854 Then, add in the metadata and/or file information as desired:
855 -------------------------------------------------------------
856
857 File information requires:
858
859 `payload` - the payload to send. Can be either a bytestring or a File-like object that supports `payload.read()`
860 `mimetype` - MIMEType of the payload
861 `filename` - filename. Most SWORD2 uploads have this as being mandatory.
862 `packaging` - the SWORD2 packaging type of the payload.
863 eg packaging = 'http://purl.org/net/sword/package/Binary'
864
865 `metadata_relevant` - This should be set to `True` if the server should consider the file a potential source of metadata extraction,
866 or `False` if the server should not attempt to extract any metadata from the deposi
867
868 Metadata information requires:
869
870 `metadata_entry` - An instance of `sword2.Entry`, set with the metadata required.
871
872 for example, to create a metadata entry
873 >>> from sword2 import Entry
874 >>> entry = Entry(title = "My new deposit",
875 ... id = "new:id", # atom:id
876 ... dcterms_abstract = "My Thesis",
877 ... dcterms_author = "Ben",
878 ... dcterms_issued = "2010")
879
880 Response:
881
882 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
883 not a Deposit Response, then only a few attributes will be populated:
884
885 `code` -- HTTP code of the response
886 `response_headers` -- `dict` of the reponse headers
887 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
888
889 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
890 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
891 response_headers, etc)
892 """
893 target_iri = None
894 request_type = "Update PUT"
895 if metadata_entry != None:
896
897 conn_l.info("Using the Edit-IRI - Metadata or Metadata + file multipart-related uses a PUT request to the Edit-IRI")
898 if payload != None and filename != None:
899 request_type = "Update Multipart PUT"
900 else:
901 request_type = "Update Metadata PUT"
902 if dr != None and dr.edit != None:
903 conn_l.info("Using the deposit receipt to get the Edit-IRI: %s" % dr.edit)
904 target_iri = dr.edit
905 elif edit_iri != None:
906 conn_l.info("Using the %s receipt as the Edit-IRI" % edit_iri)
907 target_iri = edit_iri
908 else:
909 conn_l.error("Metadata or Metadata + file multipart-related update: Cannot find the Edit-IRI from the parameters supplied.")
910 elif payload != None and filename != None:
911
912 conn_l.info("Using the Edit-Media-IRI - File update uses a PUT request to the Edit-Media-IRI")
913 request_type = "Update File PUT"
914 if dr != None and dr.edit_media != None:
915 conn_l.info("Using the deposit receipt to get the Edit-Media-IRI: %s" % dr.edit_media)
916 target_iri = dr.edit_media
917 elif edit_media_iri != None:
918 conn_l.info("Using the %s receipt as the Edit-Media-IRI" % edit_media_iri)
919 target_iri = edit_media_iri
920 else:
921 conn_l.error("File update: Cannot find the Edit-Media-IRI from the parameters supplied.")
922
923 if target_iri == None:
924 raise Exception("No suitable IRI was found for the request needed.")
925
926 return self._make_request(target_iri = target_iri,
927 metadata_entry=metadata_entry,
928 payload=payload,
929 mimetype=mimetype,
930 filename=filename,
931 packaging=packaging,
932 on_behalf_of=on_behalf_of,
933 in_progress=in_progress,
934 metadata_relevant=str(metadata_relevant),
935 method="PUT",
936 request_type=request_type)
937
938
939
940 - def add_file_to_resource(self,
941 edit_media_iri,
942 payload,
943 filename,
944
945 mimetype=None,
946
947
948 on_behalf_of=None,
949 in_progress=False,
950 metadata_relevant=False
951 ):
952 """
953 Adding Files to the Media Resource
954
955 From the spec, paraphrased:
956
957 "This feature is for use when clients wish to send individual files to the server and to receive back the IRI for the created resource. [Adding new items to the deposit container] will not give back the location of the deposited resources, so in cases where the server does not provide the (optional) Deposit Receipt, it is not possible for the client to ascertain the location of the file actually deposited - the Location header in that operation is the Edit-IRI. By POSTing to the EM-IRI, the Location header will return the IRI of the deposited file itself, rather than that of the container.
958
959 As the EM-IRI represents the Media Resource itself, rather than the Container, this operation will not formally support metadata handling, and therefore also offers no explicit support for packaging either since packages may be both content and metadata. Nonetheless, for files which may contain extractable metadata, there is a Metadata-Relevant header which can be defined to indicate whether the deposit can be used to augment the metadata of the container."
960
961 #BETASWORD2URL
962 See http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD#protocoloperations_addingcontent_mediaresource
963
964
965 Set the following parameters in addition to the basic parameters:
966
967 `edit_media_iri` - The Edit-Media-IRI
968
969 `payload` - the payload to send. Can be either a bytestring or a File-like object that supports `payload.read()`
970 `mimetype` - MIMEType of the payload
971 `filename` - filename. Most SWORD2 uploads have this as being mandatory.
972 `packaging` - the SWORD2 packaging type of the payload.
973 eg packaging = 'http://purl.org/net/sword/package/Binary'
974
975 Response:
976
977 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
978 not a Deposit Response, then only a few attributes will be populated:
979
980 `code` -- HTTP code of the response
981 `response_headers` -- `dict` of the reponse headers
982 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
983
984 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
985 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
986 response_headers, etc)
987
988 """
989 conn_l.info("Appending file to a deposit via Edit-Media-IRI %s" % edit_media_iri)
990 return self._make_request(target_iri = edit_media_iri,
991 payload=payload,
992 mimetype=mimetype,
993 filename=filename,
994 on_behalf_of=on_behalf_of,
995 in_progress=in_progress,
996 method="POST",
997 metadata_relevant=metadata_relevant,
998 request_type='EM_IRI POST (APPEND)')
999
1000 - def append(self,
1001 se_iri = None,
1002
1003 payload = None,
1004 filename = None,
1005
1006 mimetype = None,
1007 packaging = None,
1008 on_behalf_of = None,
1009 metadata_entry = None,
1010 metadata_relevant = False,
1011 in_progress = False,
1012 dr = None
1013 ):
1014 """
1015 Adding Content to a Resource
1016
1017 #BETASWORD2URL
1018 See http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD#protocoloperations_addingcontent
1019
1020 Usage:
1021 ------
1022
1023 Set the target for this request:
1024 --------------------------------
1025
1026 Set `se_iri` to be the SWORD2-Edit-IRI for a given deposit. (This can be found in `sword2.Deposit_Receipt.se_iri`)
1027
1028
1029 OR
1030
1031 you can pass back the `sword2.Deposit_Receipt` object you got from a previous transaction as the `dr` parameter,
1032 and the correct IRI will automatically be chosen.
1033
1034 Then:
1035 -----
1036
1037 1. "Adding New Packages or Files to a Container"
1038 ------------------------------------------------
1039
1040 Set the following parameters in addition to the basic parameters:
1041
1042 `payload` - the payload to send. Can be either a bytestring or a File-like object that supports `payload.read()`
1043 `mimetype` - MIMEType of the payload
1044 `filename` - filename. Most SWORD2 uploads have this as being mandatory.
1045 `packaging` - the SWORD2 packaging type of the payload.
1046 eg packaging = 'http://purl.org/net/sword/package/Binary'
1047
1048 Response:
1049
1050 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
1051 not a Deposit Response, then only a few attributes will be populated:
1052
1053 `code` -- HTTP code of the response
1054 `response_headers` -- `dict` of the reponse headers
1055 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
1056
1057 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
1058 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
1059 response_headers, etc)
1060
1061
1062 2. "Adding New Metadata to a Container"
1063 ---------------------------------------
1064
1065 NB SWORD2 does not instruct the server on the best way to handle metadata, only that metadata SHOULD be
1066 added and not overwritten; in certain circumstances this may not produce the desired behaviour.
1067
1068 Set the following parameters in addition to the basic parameters:
1069
1070 `metadata_entry` - An instance of `sword2.Entry`, set with the metadata required.
1071
1072 for example:
1073 # conn = `sword2.Connection`, se_iri = SWORD2-Edit-IRI
1074 >>> from sword2 import Entry
1075 >>> entry = Entry(dcterms:identifier = "doi://......")
1076 >>> conn.add_new_item_to_container(se_iri = se_iri,
1077 ... metadata_entry = entry)
1078
1079 Response:
1080
1081 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
1082 not a Deposit Response, then only a few attributes will be populated:
1083
1084 `code` -- HTTP code of the response
1085 `response_headers` -- `dict` of the reponse headers
1086 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
1087
1088 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
1089 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
1090 response_headers, etc)
1091
1092 3. "Adding New Metadata and Packages or Files to a Container with Multipart"
1093 ----------------------------------------------------------------------------
1094
1095 Create a resource in a given collection by uploading a file AND the metadata about this resource.
1096
1097 To make this sort of request, just set the parameters as shown for both the binary upload and the metadata upload.
1098
1099 eg:
1100
1101 >>> conn.add_new_item_to_container(se_iri = se_iri,
1102 ... metadata_entry = entry,
1103 ... payload = open("foo.zip", "r"),
1104 ... mimetype =
1105 .... and so on
1106
1107 Response:
1108
1109 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
1110 not a Deposit Response, then only a few attributes will be populated:
1111
1112 `code` -- HTTP code of the response
1113 `response_headers` -- `dict` of the reponse headers
1114 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
1115
1116 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
1117 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
1118 response_headers, etc)
1119
1120 """
1121
1122 if not se_iri:
1123 if dr != None:
1124 conn_l.info("Using the deposit receipt to get the SWORD2-Edit-IRI")
1125 se_iri = dr.se_iri
1126 if se_iri:
1127 conn_l.info("Update Resource via SWORD2-Edit-IRI %s" % se_iri)
1128 else:
1129 raise Exception("No SWORD2-Edit-IRI was given and no suitable IRI was found in the deposit receipt.")
1130 else:
1131 raise Exception("No SWORD2-Edit-IRI was given")
1132 else:
1133 conn_l.info("Update Resource via SWORD2-Edit-IRI %s" % se_iri)
1134
1135 conn_l.info("Adding new file, metadata or both to a SWORD deposit via SWORD-Edit-IRI %s" % se_iri)
1136 return self._make_request(target_iri = se_iri,
1137 payload=payload,
1138 mimetype=mimetype,
1139 packaging=packaging,
1140 filename=filename,
1141 metadata_entry=metadata_entry,
1142 on_behalf_of=on_behalf_of,
1143 in_progress=in_progress,
1144 method="POST",
1145 metadata_relevant=metadata_relevant,
1146 request_type='SE_IRI POST (APPEND PKG)')
1147
1148
1149 - def delete(self,
1150 resource_iri,
1151 on_behalf_of=None):
1152 """
1153 Delete resource
1154
1155 Generic method to send an HTTP DELETE request to a given IRI.
1156
1157 Can be given the optional parameter of `on_behalf_of`.
1158 """
1159 conn_l.info("Deleting resource %s" % resource_iri)
1160 return self._make_request(target_iri = resource_iri,
1161 on_behalf_of=on_behalf_of,
1162 method="DELETE",
1163 request_type='IRI DELETE')
1164
1165 - def delete_content_of_resource(self, edit_media_iri = None,
1166 on_behalf_of = None,
1167 dr = None):
1168
1169 """
1170 Deleting the Content of a Resource
1171
1172 Remove all the content of a resource without removing the resource itself
1173
1174 #BETASWORD2URL
1175 See http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD#protocoloperations_deletingcontent
1176
1177 Usage:
1178 ------
1179
1180 Set the target for this request:
1181 --------------------------------
1182
1183 Set `edit_media_iri` to be the Edit-Media-IRI for a given resource.
1184
1185
1186 OR
1187
1188 you can pass back the `sword2.Deposit_Receipt` object you got from a previous transaction as the `dr` parameter,
1189 and the correct IRI will automatically be chosen.
1190 """
1191 if not edit_media_iri:
1192 if dr != None:
1193 conn_l.info("Using the deposit receipt to get the Edit-Media-IRI")
1194 edit_media_iri = dr.edit_media
1195 if edit_media_iri:
1196 conn_l.info("Deleting Resource via Edit-Media-IRI %s" % edit_media_iri)
1197 else:
1198 raise Exception("No Edit-Media-IRI was given and no suitable IRI was found in the deposit receipt.")
1199 else:
1200 raise Exception("No Edit-Media-IRI was given")
1201 else:
1202 conn_l.info("Deleting Resource via Edit-Media-IRI %s" % edit_media_iri)
1203
1204 return self.delete_resource(edit_media_iri,
1205 on_behalf_of = on_behalf_of)
1206
1207
1208
1209
1210
1211 - def delete_container(self, edit_iri = None,
1212 on_behalf_of = None,
1213 dr = None):
1214
1215 """
1216 Deleting the Container
1217
1218 Delete the entire object on the server, effectively removing the deposit entirely.
1219
1220 #BETASWORD2URL
1221 See http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD#protocoloperations_deleteconteiner
1222
1223 Usage:
1224 ------
1225
1226 Set the target for this request:
1227 --------------------------------
1228
1229 Set `edit_iri` to be the Edit-IRI for a given resource.
1230
1231
1232 OR
1233
1234 you can pass back the `sword2.Deposit_Receipt` object you got from a previous transaction as the `dr` parameter,
1235 and the correct IRI will automatically be chosen.
1236
1237 """
1238 if not edit_iri:
1239 if dr != None:
1240 conn_l.info("Using the deposit receipt to get the Edit-IRI")
1241 edit_iri = dr.edit
1242 if edit_iri:
1243 conn_l.info("Deleting Container via Edit-IRI %s" % edit_iri)
1244 else:
1245 raise Exception("No Edit-IRI was given and no suitable IRI was found in the deposit receipt.")
1246 else:
1247 raise Exception("No Edit-IRI was given")
1248 else:
1249 conn_l.info("Deleting Container via Edit-IRI %s" % edit_iri)
1250
1251 return self.delete_resource(edit_iri,
1252 on_behalf_of = on_behalf_of)
1253
1254 - def complete_deposit(self,
1255 se_iri = None,
1256 on_behalf_of=None,
1257 dr = None):
1258 """
1259 Completing a Previously Incomplete Deposit
1260
1261 Use this method to indicate to a server that a deposit which was 'in progress' is now complete. In other words, complete a deposit
1262 which had the 'In-Progress' flag set to True.
1263
1264 #BETASWORD2URL
1265 http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD#continueddeposit_complete
1266
1267 Usage:
1268 ------
1269
1270 Set the target for this request:
1271 --------------------------------
1272
1273 Set `se_iri` to be the SWORD2-Edit-IRI for a given resource.
1274
1275
1276 OR
1277
1278 you can pass back the `sword2.Deposit_Receipt` object you got from a previous transaction as the `dr` parameter,
1279 and the correct IRI will automatically be chosen.
1280 """
1281
1282 if not se_iri:
1283 if dr != None:
1284 conn_l.info("Using the deposit receipt to get the SWORD2-Edit-IRI")
1285 se_iri = dr.se_iri
1286 if se_iri:
1287 conn_l.info("Complete deposit using the SWORD2-Edit-IRI %s" % se_iri)
1288 else:
1289 raise Exception("No SWORD2-Edit-IRI was given and no suitable IRI was found in the deposit receipt.")
1290 else:
1291 raise Exception("No SWORD2-Edit-IRI was given")
1292 else:
1293 conn_l.info("Complete deposit using the SWORD2-Edit-IRI %s" % se_iri)
1294
1295 return self._make_request(target_iri = se_iri,
1296 on_behalf_of=on_behalf_of,
1297 in_progress='false',
1298 method="POST",
1299 empty=True,
1300 request_type='SE_IRI Complete Deposit')
1301
1302 - def update_files_for_resource(self,
1303 payload,
1304 filename,
1305
1306 mimetype=None,
1307 packaging=None,
1308
1309 edit_media_iri = None,
1310
1311 on_behalf_of=None,
1312 in_progress=False,
1313 metadata_relevant=False,
1314
1315 dr = None
1316 ):
1317 """
1318 Replacing the File Content of a Resource
1319
1320 #BETASWORD2URL
1321 See http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD#protocoloperations_editingcontent_binary
1322
1323 The `Connection` can replace the file content of a resource, given the Edit-Media-IRI for this resource. This can be found
1324 from the `sword2.Deposit_Receipt.edit_media` attribute of a previous deposit, or directly from the deposit receipt XML response.
1325
1326 Usage:
1327 ------
1328
1329 Set the target for this request:
1330 --------------------------------
1331
1332 Set the `edit_media_iri` parameter to the Edit-Media-IRI.
1333
1334 OR
1335
1336 you can pass back the `sword2.Deposit_Receipt` object you got from a previous transaction as the `dr` parameter,
1337 and the correct IRI will automatically be chosen.
1338
1339 Then, add in the payload:
1340 -------------------------
1341
1342 Set the following parameters in addition to the basic parameters (see `self.create_resource`):
1343
1344 `payload` - the payload to send. Can be either a bytestring or a File-like object that supports `payload.read()`
1345 `mimetype` - MIMEType of the payload
1346 `filename` - filename. Most SWORD2 uploads have this as being mandatory.
1347 `packaging` - the SWORD2 packaging type of the payload.
1348 eg packaging = 'http://purl.org/net/sword/package/Binary'
1349
1350 `metadata_relevant` - This should be set to `True` if the server should consider the file a potential source of metadata extraction,
1351 or `False` if the server should not attempt to extract any metadata from the deposi
1352
1353 Response:
1354
1355 A `sword2.Deposit_Receipt` object containing the deposit receipt data. If the response was blank or
1356 not a Deposit Response, then only a few attributes will be populated:
1357
1358 `code` -- HTTP code of the response
1359 `response_headers` -- `dict` of the reponse headers
1360 `content` -- (Optional) in case the response body is not empty but the response is not a Deposit Receipt
1361
1362 If exception-throwing is turned off (`error_response_raises_exceptions = False` or `self.raise_except = False`)
1363 then the response will be a `sword2.Error_Document`, but will still have the aforementioned attributes set, (code,
1364 response_headers, etc)
1365 """
1366 if not edit_media_iri:
1367 if dr != None:
1368 conn_l.info("Using the deposit receipt to get the Edit-Media-IRI")
1369 edit_media_iri = dr.edit_media
1370 if edit_media_iri:
1371 conn_l.info("Update Resource via Edit-Media-IRI %s" % edit_media_iri)
1372 else:
1373 raise Exception("No Edit-Media-IRI was given and no suitable IRI was found in the deposit receipt.")
1374 else:
1375 raise Exception("No Edit-Media-IRI was given")
1376 else:
1377 conn_l.info("Update Resource via Edit-Media-IRI %s" % edit_media_iri)
1378
1379 return self._make_request(target_iri = edit_media_iri,
1380 payload=payload,
1381 mimetype=mimetype,
1382 filename=filename,
1383 in_progress=in_progress,
1384 packaging=packaging,
1385 on_behalf_of=on_behalf_of,
1386 method="PUT",
1387 metadata_relevant=str(metadata_relevant),
1388 request_type='EM_IRI PUT')
1389
1472
1572
1573
1575 """
1576 Getting the Sword Statement.
1577
1578 IN PROGRESS - USE AT OWN RISK.... see `sword2.Sword_Statement`.
1579 """
1580
1581 conn_l.debug("Trying to GET the ATOM Sword Statement at %s." % sword_statement_iri)
1582 response = self.get_resource(sword_statement_iri, headers = {'Accept':'application/atom+xml;type=feed'})
1583 if response.code == 200:
1584
1585 if True:
1586 conn_l.debug("Attempting to parse the response as a ATOM Sword Statement")
1587 s = Sword_Statement(response.content)
1588 conn_l.debug("Parsed SWORD2 Statement, returning")
1589 return s
1590
1591
1592
1593
1594 - def get_resource(self, content_iri = None,
1595 packaging=None,
1596 on_behalf_of=None,
1597 headers = {},
1598 dr = None):
1599 """
1600 Retrieving the content
1601
1602 Get the file or package from the SWORD2 server.
1603
1604 From the specification:
1605 "The Deposit Receipt contains two IRIs which can be used to retrieve content from the server: Cont-IRI and EM-IRI. These are provided in the atom:content@src element and the atom:link@rel="edit-media" elements respectively. Their only functional difference is that the client MUST NOT carry out any HTTP operations other than GET on the Cont-IRI, while all operations are permitted on the EM-IRI. It is acceptable, but not required, that both IRIs to be the same, and in this section we refer only to the EM-IRI but in all cases it can be substituted for the Cont-IRI."
1606
1607 #BETASWORD2URL
1608 See http://sword-app.svn.sourceforge.net/viewvc/sword-app/spec/trunk/SWORDProfile.html?revision=HEAD#protocoloperations_retrievingcontent
1609
1610 Usage:
1611 ------
1612
1613 Set the target for this request:
1614 --------------------------------
1615
1616 Set `content_iri` to be the Content-IRI for a given resource (or to the IRI of any resource you wish to HTTP GET)
1617
1618
1619 OR
1620
1621 you can pass back the `sword2.Deposit_Receipt` object you got from a previous transaction as the `dr` parameter,
1622 and the correct IRI will automatically be chosen.
1623
1624 Response:
1625
1626 A `ContentWrapper` -
1627 `ContentWrapper.response_headers` -- response headers
1628 `ContentWrapper.content` -- body of response from server (the file or package)
1629 `ContentWrapper.code` -- status code ('200' on success.)
1630
1631 """
1632
1633 if not content_iri:
1634 if dr != None:
1635 conn_l.info("Using the deposit receipt to get the SWORD2-Edit-IRI")
1636 content_iri = dr.cont_iri
1637 if content_iri:
1638 conn_l.info("Getting the resource at Content-IRI %s" % content_iri)
1639 else:
1640 raise Exception("No Content-IRI was given and no suitable IRI was found in the deposit receipt.")
1641 else:
1642 raise Exception("No Content-IRI was given")
1643 else:
1644 conn_l.info("Getting the resource at Content-IRI %s" % content_iri)
1645
1646
1647 if self.honour_receipts and packaging:
1648
1649 conn_l.debug("Checking that the packaging format '%s' is available." % content_iri)
1650 conn_l.debug("Cached Cont-IRI Receipts: %s" % self.cont_iris.keys())
1651 if content_iri in self.cont_iris.keys():
1652 if not (packaging in self.cont_iris[content_iri].packaging):
1653 conn_l.error("Desired packaging format '%' not available from the server, according to the deposit receipt. Change the client parameter 'honour_receipts' to False to avoid this check.")
1654 return self._return_error_or_exception(PackagingFormatNotAvailable, {}, "")
1655 if on_behalf_of:
1656 headers['On-Behalf-Of'] = self.on_behalf_of
1657 elif self.on_behalf_of:
1658 headers['On-Behalf-Of'] = self.on_behalf_of
1659 if packaging:
1660 headers['Accept-Packaging'] = packaging
1661
1662 self._t.start("IRI GET resource")
1663 if packaging:
1664 conn_l.info("IRI GET resource '%s' with Accept-Packaging:%s" % (content_iri, packaging))
1665 else:
1666 conn_l.info("IRI GET resource '%s'" % content_iri)
1667 resp, content = self.h.request(content_iri, "GET", headers=headers)
1668 _, took_time = self._t.time_since_start("IRI GET resource")
1669 if self.history:
1670 self.history.log('Cont_IRI GET resource',
1671 sd_iri = self.sd_iri,
1672 content_iri = content_iri,
1673 packaging = packaging,
1674 on_behalf_of = self.on_behalf_of,
1675 response = resp,
1676 headers = headers,
1677 process_duration = took_time)
1678 conn_l.info("Server response: %s" % resp['status'])
1679 conn_l.debug(resp)
1680 if resp['status'] == '200':
1681 conn_l.debug("Cont_IRI GET resource successful - got %s bytes from %s" % (len(content), content_iri))
1682 class ContentWrapper(object):
1683 def __init__(self, resp, content):
1684 self.response_headers = dict(resp)
1685 self.content = content
1686 self.code = resp.status
1687 return ContentWrapper(resp, content)
1688 elif resp['status'] == '408':
1689 conn_l.error("Desired packaging format '%' not available from the server.")
1690 return self._return_error_or_exception(PackagingFormatNotAvailable, resp, content)
1691 else:
1692 return self._handle_error_response(resp, content)
1693