dspace-api-toolkit 0.1.1__py3-none-any.whl
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.
- dspace_api/__init__.py +75 -0
- dspace_api/_base.py +407 -0
- dspace_api/_containers.py +217 -0
- dspace_api/_version.py +4 -0
- dspace_api/auth.py +467 -0
- dspace_api/authorizations.py +486 -0
- dspace_api/bundles.py +1193 -0
- dspace_api/collections.py +1069 -0
- dspace_api/communities.py +363 -0
- dspace_api/config.py +254 -0
- dspace_api/discovery.py +920 -0
- dspace_api/entity_types.py +127 -0
- dspace_api/epersons.py +647 -0
- dspace_api/exceptions.py +110 -0
- dspace_api/groups.py +599 -0
- dspace_api/items.py +549 -0
- dspace_api/metadata.py +742 -0
- dspace_api/permissions.py +1052 -0
- dspace_api/projection.py +292 -0
- dspace_api/relationships.py +694 -0
- dspace_api/session.py +538 -0
- dspace_api/site.py +98 -0
- dspace_api/utils.py +519 -0
- dspace_api/versions.py +292 -0
- dspace_api/vocabulary.py +365 -0
- dspace_api/workflow.py +893 -0
- dspace_api/workspaces.py +1404 -0
- dspace_api_toolkit-0.1.1.dist-info/METADATA +211 -0
- dspace_api_toolkit-0.1.1.dist-info/RECORD +32 -0
- dspace_api_toolkit-0.1.1.dist-info/WHEEL +5 -0
- dspace_api_toolkit-0.1.1.dist-info/licenses/LICENSE +28 -0
- dspace_api_toolkit-0.1.1.dist-info/top_level.txt +1 -0
dspace_api/utils.py
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Utility functions for DSpace API client
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import re
|
|
10
|
+
import math
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import Dict, List, Optional, Union
|
|
13
|
+
|
|
14
|
+
# Logger for the module
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
def format_metadata_value(value, language=None, authority=None, confidence=-1, place=0):
|
|
18
|
+
"""
|
|
19
|
+
Format a single metadata value for DSpace API
|
|
20
|
+
|
|
21
|
+
Parameters:
|
|
22
|
+
value: The metadata value
|
|
23
|
+
language: Language code (e.g., "en", "de")
|
|
24
|
+
authority: Authority value for controlled vocabularies
|
|
25
|
+
confidence: Confidence level for authority values
|
|
26
|
+
place: Position/order of the value
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
dict: Formatted metadata value
|
|
30
|
+
"""
|
|
31
|
+
metadata_value = {
|
|
32
|
+
"value": str(value),
|
|
33
|
+
"language": language,
|
|
34
|
+
"authority": authority,
|
|
35
|
+
"confidence": confidence,
|
|
36
|
+
"place": place
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Remove None values to keep JSON clean
|
|
40
|
+
return {k: v for k, v in metadata_value.items() if v is not None}
|
|
41
|
+
|
|
42
|
+
def format_metadata_field(values, default_language=None):
|
|
43
|
+
"""
|
|
44
|
+
Format metadata field values for DSpace API
|
|
45
|
+
|
|
46
|
+
Parameters:
|
|
47
|
+
values: String, list of strings, or list of dicts
|
|
48
|
+
default_language: Default language to use if not specified
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
list: Formatted metadata values
|
|
52
|
+
"""
|
|
53
|
+
if isinstance(values, str):
|
|
54
|
+
return [format_metadata_value(values, language=default_language)]
|
|
55
|
+
elif isinstance(values, list):
|
|
56
|
+
formatted = []
|
|
57
|
+
for i, value in enumerate(values):
|
|
58
|
+
if isinstance(value, str):
|
|
59
|
+
formatted.append(format_metadata_value(value, language=default_language, place=i))
|
|
60
|
+
elif isinstance(value, dict):
|
|
61
|
+
# Use provided dict, but ensure place is set if not provided
|
|
62
|
+
if "place" not in value:
|
|
63
|
+
value["place"] = i
|
|
64
|
+
formatted.append(value)
|
|
65
|
+
else:
|
|
66
|
+
formatted.append(format_metadata_value(str(value), language=default_language, place=i))
|
|
67
|
+
return formatted
|
|
68
|
+
else:
|
|
69
|
+
return [format_metadata_value(str(values), language=default_language)]
|
|
70
|
+
|
|
71
|
+
def extract_uuid_from_url(url):
|
|
72
|
+
"""
|
|
73
|
+
Extract UUID from DSpace URL
|
|
74
|
+
|
|
75
|
+
Parameters:
|
|
76
|
+
url: DSpace URL containing UUID
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
str: Extracted UUID or None
|
|
80
|
+
"""
|
|
81
|
+
if not url:
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
# Split by '/' and find the last part that looks like a UUID
|
|
85
|
+
parts = url.split('/')
|
|
86
|
+
for part in reversed(parts):
|
|
87
|
+
if len(part) == 36 and part.count('-') == 4: # UUID format
|
|
88
|
+
return part
|
|
89
|
+
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
def parse_handle(handle_or_url):
|
|
93
|
+
"""
|
|
94
|
+
Parse DSpace handle from various formats
|
|
95
|
+
|
|
96
|
+
Parameters:
|
|
97
|
+
handle_or_url: Handle string or URL
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
str: Clean handle (e.g., "123456789/123")
|
|
101
|
+
"""
|
|
102
|
+
if not handle_or_url:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
# Remove common URL prefixes
|
|
106
|
+
handle = handle_or_url.replace("http://hdl.handle.net/", "")
|
|
107
|
+
handle = handle.replace("https://hdl.handle.net/", "")
|
|
108
|
+
|
|
109
|
+
# Extract handle pattern (numbers/numbers)
|
|
110
|
+
match = re.search(r'(\d+/\d+)', handle)
|
|
111
|
+
return match.group(1) if match else handle
|
|
112
|
+
|
|
113
|
+
def validate_metadata_field(field_name, values):
|
|
114
|
+
"""
|
|
115
|
+
Validate metadata field format
|
|
116
|
+
|
|
117
|
+
Parameters:
|
|
118
|
+
field_name: Name of the metadata field
|
|
119
|
+
values: Field values to validate
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
bool: True if valid
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
ValueError: If validation fails
|
|
126
|
+
"""
|
|
127
|
+
if not field_name:
|
|
128
|
+
raise ValueError("Field name cannot be empty")
|
|
129
|
+
|
|
130
|
+
if not values:
|
|
131
|
+
raise ValueError(f"No values provided for field {field_name}")
|
|
132
|
+
|
|
133
|
+
# Check if field follows Dublin Core pattern - must have at least 2 parts after "dc."
|
|
134
|
+
if field_name.startswith("dc.") and field_name.count(".") < 2:
|
|
135
|
+
raise ValueError(f"Invalid Dublin Core field format: {field_name}")
|
|
136
|
+
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
def sanitize_filename(filename):
|
|
140
|
+
"""
|
|
141
|
+
Sanitize filename for safe file operations
|
|
142
|
+
|
|
143
|
+
Parameters:
|
|
144
|
+
filename: Original filename
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
str: Sanitized filename
|
|
148
|
+
"""
|
|
149
|
+
# Remove or replace invalid characters
|
|
150
|
+
sanitized = re.sub(r'[<>:"/\\|?*]', '_', filename)
|
|
151
|
+
# Remove leading/trailing whitespace and dots
|
|
152
|
+
sanitized = sanitized.strip(' .')
|
|
153
|
+
# Limit length
|
|
154
|
+
if len(sanitized) > 255:
|
|
155
|
+
name, ext = os.path.splitext(sanitized)
|
|
156
|
+
sanitized = name[:255-len(ext)] + ext
|
|
157
|
+
|
|
158
|
+
return sanitized
|
|
159
|
+
|
|
160
|
+
def create_patch_operation(operation, path, value=None, from_path=None):
|
|
161
|
+
"""
|
|
162
|
+
Create a JSON Patch operation
|
|
163
|
+
|
|
164
|
+
Parameters:
|
|
165
|
+
operation: Operation type ("add", "remove", "replace", "move", "copy", "test")
|
|
166
|
+
path: JSON Pointer path
|
|
167
|
+
value: Value for add/replace operations
|
|
168
|
+
from_path: Source path for move/copy operations
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
dict: JSON Patch operation
|
|
172
|
+
"""
|
|
173
|
+
patch_op = {"op": operation, "path": path}
|
|
174
|
+
|
|
175
|
+
if operation in ["add", "replace", "test"] and value is not None:
|
|
176
|
+
patch_op["value"] = value
|
|
177
|
+
elif operation in ["move", "copy"] and from_path:
|
|
178
|
+
patch_op["from"] = from_path
|
|
179
|
+
|
|
180
|
+
return patch_op
|
|
181
|
+
|
|
182
|
+
def batch_metadata_operations(metadata_updates):
|
|
183
|
+
"""
|
|
184
|
+
Create batch patch operations for metadata updates
|
|
185
|
+
|
|
186
|
+
Parameters:
|
|
187
|
+
metadata_updates: Dict of field -> values mappings
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
list: List of JSON Patch operations
|
|
191
|
+
"""
|
|
192
|
+
operations = []
|
|
193
|
+
|
|
194
|
+
for field, values in metadata_updates.items():
|
|
195
|
+
operations.append(create_patch_operation(
|
|
196
|
+
"replace",
|
|
197
|
+
f"/metadata/{field}",
|
|
198
|
+
format_metadata_field(values)
|
|
199
|
+
))
|
|
200
|
+
|
|
201
|
+
return operations
|
|
202
|
+
|
|
203
|
+
def log_api_call(method, url, status_code, response_time=None):
|
|
204
|
+
"""
|
|
205
|
+
Log API call details
|
|
206
|
+
|
|
207
|
+
Parameters:
|
|
208
|
+
method: HTTP method
|
|
209
|
+
url: Request URL
|
|
210
|
+
status_code: Response status code
|
|
211
|
+
response_time: Response time in seconds
|
|
212
|
+
"""
|
|
213
|
+
log_msg = f"{method} {url} -> {status_code}"
|
|
214
|
+
if response_time:
|
|
215
|
+
log_msg += f" ({response_time:.2f}s)"
|
|
216
|
+
|
|
217
|
+
if 200 <= status_code < 300:
|
|
218
|
+
logger.info(log_msg)
|
|
219
|
+
elif 400 <= status_code < 500:
|
|
220
|
+
logger.warning(log_msg)
|
|
221
|
+
else:
|
|
222
|
+
logger.error(log_msg)
|
|
223
|
+
|
|
224
|
+
def format_file_size(size_bytes):
|
|
225
|
+
"""
|
|
226
|
+
Format file size in human readable format
|
|
227
|
+
|
|
228
|
+
Parameters:
|
|
229
|
+
size_bytes: Size in bytes
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
str: Formatted size (e.g., "1.5 MB")
|
|
233
|
+
"""
|
|
234
|
+
if size_bytes == 0:
|
|
235
|
+
return "0 B"
|
|
236
|
+
|
|
237
|
+
size_names = ["B", "KB", "MB", "GB", "TB"]
|
|
238
|
+
i = int(math.floor(math.log(size_bytes, 1024)))
|
|
239
|
+
p = math.pow(1024, i)
|
|
240
|
+
s = round(size_bytes / p, 2)
|
|
241
|
+
return f"{s} {size_names[i]}"
|
|
242
|
+
|
|
243
|
+
def check_file_exists_and_readable(file_path):
|
|
244
|
+
"""
|
|
245
|
+
Check if file exists and is readable
|
|
246
|
+
|
|
247
|
+
Parameters:
|
|
248
|
+
file_path: Path to the file
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
bool: True if file exists and is readable
|
|
252
|
+
|
|
253
|
+
Raises:
|
|
254
|
+
FileNotFoundError: If file doesn't exist
|
|
255
|
+
PermissionError: If file is not readable
|
|
256
|
+
"""
|
|
257
|
+
if not os.path.exists(file_path):
|
|
258
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
259
|
+
|
|
260
|
+
if not os.access(file_path, os.R_OK):
|
|
261
|
+
raise PermissionError(f"File not readable: {file_path}")
|
|
262
|
+
|
|
263
|
+
return True
|
|
264
|
+
|
|
265
|
+
def get_file_info(file_path):
|
|
266
|
+
"""
|
|
267
|
+
Get file information
|
|
268
|
+
|
|
269
|
+
raise DSpaceError(f"Version {version.get('id')} has no workspace item (already in workflow?)")
|
|
270
|
+
|
|
271
|
+
Parameters:
|
|
272
|
+
metadata: Flat metadata dictionary
|
|
273
|
+
entity_type: Entity type for the item
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
dict: Metadata organized into sections
|
|
277
|
+
file_path: Path to the file
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
dict: File information
|
|
281
|
+
"""
|
|
282
|
+
check_file_exists_and_readable(file_path)
|
|
283
|
+
|
|
284
|
+
stat = os.stat(file_path)
|
|
285
|
+
return {
|
|
286
|
+
"path": file_path,
|
|
287
|
+
"name": os.path.basename(file_path),
|
|
288
|
+
"size": stat.st_size,
|
|
289
|
+
"size_formatted": format_file_size(stat.st_size),
|
|
290
|
+
"modified": datetime.fromtimestamp(stat.st_mtime),
|
|
291
|
+
"extension": os.path.splitext(file_path)[1].lower()
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
def create_workspace_sections_from_metadata(metadata, entity_type="Publication"):
|
|
295
|
+
"""
|
|
296
|
+
Convert flat metadata dictionary to workspace sections format
|
|
297
|
+
|
|
298
|
+
Parameters:
|
|
299
|
+
metadata: Flat metadata dictionary
|
|
300
|
+
entity_type: Entity type for the item
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
dict: Metadata organized into sections
|
|
304
|
+
"""
|
|
305
|
+
sections = {}
|
|
306
|
+
|
|
307
|
+
# Define section mappings
|
|
308
|
+
section_mappings = {
|
|
309
|
+
"publication": [
|
|
310
|
+
"dc.title", "dc.contributor.author", "dc.creator", "dc.date.issued",
|
|
311
|
+
"dc.publisher", "dc.relation.ispartof"
|
|
312
|
+
],
|
|
313
|
+
"publication_type": [
|
|
314
|
+
"dc.type"
|
|
315
|
+
],
|
|
316
|
+
"publication_indexing": [
|
|
317
|
+
"dc.description", "dc.subject", "dc.description.abstract"
|
|
318
|
+
],
|
|
319
|
+
"person": [
|
|
320
|
+
"person.familyName", "person.givenName", "person.identifier.gnd"
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
# Distribute metadata into sections
|
|
325
|
+
for section, fields in section_mappings.items():
|
|
326
|
+
section_metadata = {}
|
|
327
|
+
for field in fields:
|
|
328
|
+
if field in metadata:
|
|
329
|
+
section_metadata[field] = metadata[field]
|
|
330
|
+
if section_metadata:
|
|
331
|
+
sections[section] = section_metadata
|
|
332
|
+
|
|
333
|
+
# Add any remaining fields to a general section
|
|
334
|
+
used_fields = set()
|
|
335
|
+
for fields in section_mappings.values():
|
|
336
|
+
used_fields.update(fields)
|
|
337
|
+
|
|
338
|
+
remaining_fields = {k: v for k, v in metadata.items() if k not in used_fields}
|
|
339
|
+
if remaining_fields:
|
|
340
|
+
sections["additional"] = remaining_fields
|
|
341
|
+
|
|
342
|
+
return sections
|
|
343
|
+
|
|
344
|
+
def merge_metadata_sections(*section_dicts):
|
|
345
|
+
"""
|
|
346
|
+
Merge multiple metadata section dictionaries
|
|
347
|
+
|
|
348
|
+
Parameters:
|
|
349
|
+
*section_dicts: Multiple section dictionaries to merge
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
dict: Merged sections
|
|
353
|
+
"""
|
|
354
|
+
merged = {}
|
|
355
|
+
|
|
356
|
+
for section_dict in section_dicts:
|
|
357
|
+
for section_name, metadata in section_dict.items():
|
|
358
|
+
if section_name not in merged:
|
|
359
|
+
merged[section_name] = {}
|
|
360
|
+
merged[section_name].update(metadata)
|
|
361
|
+
|
|
362
|
+
return merged
|
|
363
|
+
|
|
364
|
+
def validate_uuid(uuid_string):
|
|
365
|
+
"""
|
|
366
|
+
Validate UUID format
|
|
367
|
+
|
|
368
|
+
Parameters:
|
|
369
|
+
uuid_string: String to validate
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
bool: True if valid UUID format
|
|
373
|
+
"""
|
|
374
|
+
uuid_pattern = re.compile(
|
|
375
|
+
r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
|
|
376
|
+
re.IGNORECASE
|
|
377
|
+
)
|
|
378
|
+
return bool(uuid_pattern.match(uuid_string)) if uuid_string else False
|
|
379
|
+
|
|
380
|
+
def convert_date_format(date_string, input_format=None, output_format="%Y"):
|
|
381
|
+
"""
|
|
382
|
+
Convert date between different formats
|
|
383
|
+
|
|
384
|
+
Parameters:
|
|
385
|
+
date_string: Date string to convert
|
|
386
|
+
input_format: Input format (auto-detected if None)
|
|
387
|
+
output_format: Desired output format
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
str: Converted date string
|
|
391
|
+
"""
|
|
392
|
+
if not date_string:
|
|
393
|
+
return None
|
|
394
|
+
|
|
395
|
+
common_formats = [
|
|
396
|
+
"%Y-%m-%d",
|
|
397
|
+
"%Y/%m/%d",
|
|
398
|
+
"%d.%m.%Y",
|
|
399
|
+
"%d/%m/%Y",
|
|
400
|
+
"%Y-%m-%dT%H:%M:%S",
|
|
401
|
+
"%Y-%m-%dT%H:%M:%SZ"
|
|
402
|
+
]
|
|
403
|
+
|
|
404
|
+
parsed_date = None
|
|
405
|
+
|
|
406
|
+
if input_format:
|
|
407
|
+
try:
|
|
408
|
+
parsed_date = datetime.strptime(date_string, input_format)
|
|
409
|
+
except ValueError:
|
|
410
|
+
pass
|
|
411
|
+
|
|
412
|
+
if not parsed_date:
|
|
413
|
+
for fmt in common_formats:
|
|
414
|
+
try:
|
|
415
|
+
parsed_date = datetime.strptime(date_string, fmt)
|
|
416
|
+
break
|
|
417
|
+
except ValueError:
|
|
418
|
+
continue
|
|
419
|
+
|
|
420
|
+
if parsed_date:
|
|
421
|
+
return parsed_date.strftime(output_format)
|
|
422
|
+
else:
|
|
423
|
+
# If all parsing fails, try to extract year
|
|
424
|
+
year_match = re.search(r'\b(19|20)\d{2}\b', date_string)
|
|
425
|
+
return year_match.group(0) if year_match else date_string
|
|
426
|
+
|
|
427
|
+
def create_dublin_core_metadata(title=None, creators=None, contributors=None,
|
|
428
|
+
description=None, subjects=None, date_issued=None,
|
|
429
|
+
type_value=None, language="en", **kwargs):
|
|
430
|
+
"""
|
|
431
|
+
Create Dublin Core metadata structure
|
|
432
|
+
|
|
433
|
+
Parameters:
|
|
434
|
+
title: Item title
|
|
435
|
+
creators: List of creators
|
|
436
|
+
contributors: List of contributors
|
|
437
|
+
description: Item description
|
|
438
|
+
subjects: List of subjects
|
|
439
|
+
date_issued: Date of issue (year or full date)
|
|
440
|
+
type_value: Resource type
|
|
441
|
+
language: Default language for metadata
|
|
442
|
+
**kwargs: Additional metadata fields
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
dict: Formatted Dublin Core metadata
|
|
446
|
+
"""
|
|
447
|
+
metadata = {}
|
|
448
|
+
|
|
449
|
+
if title:
|
|
450
|
+
metadata["dc.title"] = format_metadata_field(title, language)
|
|
451
|
+
|
|
452
|
+
if creators:
|
|
453
|
+
metadata["dc.creator"] = format_metadata_field(creators, language)
|
|
454
|
+
|
|
455
|
+
if contributors:
|
|
456
|
+
metadata["dc.contributor.author"] = format_metadata_field(contributors, language)
|
|
457
|
+
|
|
458
|
+
if description:
|
|
459
|
+
metadata["dc.description"] = format_metadata_field(description, language)
|
|
460
|
+
|
|
461
|
+
if subjects:
|
|
462
|
+
metadata["dc.subject"] = format_metadata_field(subjects, language)
|
|
463
|
+
|
|
464
|
+
if date_issued:
|
|
465
|
+
metadata["dc.date.issued"] = format_metadata_field(str(date_issued))
|
|
466
|
+
|
|
467
|
+
if type_value:
|
|
468
|
+
metadata["dc.type"] = format_metadata_field(type_value)
|
|
469
|
+
|
|
470
|
+
# Add any additional metadata fields
|
|
471
|
+
for field, value in kwargs.items():
|
|
472
|
+
if value:
|
|
473
|
+
metadata[field] = format_metadata_field(value, language)
|
|
474
|
+
|
|
475
|
+
return metadata
|
|
476
|
+
|
|
477
|
+
def create_publication_metadata(title, authors=None, date_issued=None,
|
|
478
|
+
pub_type="text", pub_type_authority=None,
|
|
479
|
+
description=None, language="en"):
|
|
480
|
+
"""
|
|
481
|
+
Create metadata for a publication item
|
|
482
|
+
|
|
483
|
+
Parameters:
|
|
484
|
+
title: Publication title
|
|
485
|
+
authors: List of authors
|
|
486
|
+
date_issued: Publication date
|
|
487
|
+
pub_type: Publication type
|
|
488
|
+
pub_type_authority: Authority URI for publication type
|
|
489
|
+
description: Publication description
|
|
490
|
+
language: Default language
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
dict: Publication metadata sections
|
|
494
|
+
"""
|
|
495
|
+
metadata_sections = {}
|
|
496
|
+
|
|
497
|
+
# Publication section
|
|
498
|
+
publication_metadata = {"dc.title": format_metadata_field(title, language)}
|
|
499
|
+
|
|
500
|
+
if authors:
|
|
501
|
+
publication_metadata["dc.contributor.author"] = format_metadata_field(authors, language)
|
|
502
|
+
|
|
503
|
+
if date_issued:
|
|
504
|
+
publication_metadata["dc.date.issued"] = format_metadata_field(str(date_issued))
|
|
505
|
+
|
|
506
|
+
metadata_sections["publication"] = publication_metadata
|
|
507
|
+
|
|
508
|
+
# Publication type section
|
|
509
|
+
if pub_type:
|
|
510
|
+
type_value = format_metadata_value(pub_type, authority=pub_type_authority, confidence=600)
|
|
511
|
+
metadata_sections["publication_type"] = {"dc.type": [type_value]}
|
|
512
|
+
|
|
513
|
+
# Description section
|
|
514
|
+
if description:
|
|
515
|
+
metadata_sections["publication_indexing"] = {
|
|
516
|
+
"dc.description": format_metadata_field(description, language)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return metadata_sections
|