ds-caselaw-marklogic-api-client 17.3.0__py3-none-any.whl → 18.0.0__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.
caselawclient/Client.py CHANGED
@@ -951,17 +951,3 @@ class MarklogicApiClient:
951
951
  )
952
952
 
953
953
  return results
954
-
955
-
956
- api_client = MarklogicApiClient(
957
- host=env("MARKLOGIC_HOST", default=None),
958
- username=env("MARKLOGIC_USER", default=None),
959
- password=env("MARKLOGIC_PASSWORD", default=None),
960
- use_https=env("MARKLOGIC_USE_HTTPS", default=False),
961
- )
962
- """
963
- An instance of the API client which is automatically initialised on importing the library.
964
-
965
- .. deprecated:: 13.0.1
966
- You should instead initialise your own instance of `MarklogicApiClient`
967
- """
caselawclient/__init__.py CHANGED
@@ -37,24 +37,4 @@ client = MarklogicApiClient(
37
37
 
38
38
  ```
39
39
 
40
- ## (Deprecated) Use in-library client instance
41
-
42
- This library will automatically initialise an instance of the client. This functionality is deprecated, and will be
43
- removed.
44
-
45
- The client expects the following environment variables to be set or defined in a `.env` file:
46
-
47
- ```bash
48
- MARKLOGIC_HOST
49
- MARKLOGIC_USER
50
- MARKLOGIC_PASSWORD
51
- MARKLOGIC_USE_HTTPS # Optional, defaults to False
52
- ```
53
-
54
- Then import `api_client` from `caselawclient.Client`:
55
-
56
- ```python
57
- from caselawclient.Client import api_client
58
- ```
59
-
60
40
  """
@@ -1,3 +1,5 @@
1
+ from lxml import etree
2
+
1
3
  from caselawclient.Client import MarklogicApiClient
2
4
  from caselawclient.responses.search_response import SearchResponse
3
5
  from caselawclient.search_parameters import SearchParameters
@@ -14,8 +16,11 @@ def search_judgments_and_parse_response(
14
16
 
15
17
  :return: The parsed search response as a SearchResponse object
16
18
  """
17
- return SearchResponse.from_response_string(
18
- api_client.search_judgments_and_decode_response(search_parameters)
19
+ return SearchResponse(
20
+ etree.fromstring(
21
+ api_client.search_judgments_and_decode_response(search_parameters)
22
+ ),
23
+ api_client,
19
24
  )
20
25
 
21
26
 
@@ -30,6 +35,7 @@ def search_and_parse_response(
30
35
 
31
36
  :return: The parsed search response as a SearchResponse object
32
37
  """
33
- return SearchResponse.from_response_string(
34
- api_client.search_and_decode_response(search_parameters)
38
+ return SearchResponse(
39
+ etree.fromstring(api_client.search_and_decode_response(search_parameters)),
40
+ api_client,
35
41
  )
@@ -18,7 +18,7 @@ from ..errors import (
18
18
  OnlySupportedOnVersion,
19
19
  )
20
20
  from ..xml_helpers import get_xpath_match_string, get_xpath_match_strings
21
- from .utilities import VersionsDict, get_judgment_root, render_versions
21
+ from .utilities import VersionsDict, render_versions
22
22
  from .utilities.aws import (
23
23
  ParserInstructionsDict,
24
24
  announce_document_event,
@@ -76,6 +76,12 @@ class DocumentNotSafeForDeletion(Exception):
76
76
  pass
77
77
 
78
78
 
79
+ class NonXMLDocumentError(Exception):
80
+ """A document cannot be parsed as XML."""
81
+
82
+ pass
83
+
84
+
79
85
  class Document:
80
86
  """
81
87
  A base class from which all other document types are extensions. This class includes the essential methods for
@@ -142,6 +148,12 @@ class Document:
142
148
  if not self.document_exists():
143
149
  raise DocumentNotFoundError(f"Document {self.uri} does not exist")
144
150
 
151
+ self.xml = self.XML(
152
+ xml_bytestring=self.api_client.get_judgment_xml_bytestring(
153
+ self.uri, show_unpublished=True
154
+ )
155
+ )
156
+
145
157
  def document_exists(self) -> bool:
146
158
  """Helper method to verify the existence of a document within MarkLogic.
147
159
 
@@ -167,14 +179,14 @@ class Document:
167
179
 
168
180
  @cached_property
169
181
  def name(self) -> str:
170
- return self._get_xpath_match_string(
182
+ return self.xml.get_xpath_match_string(
171
183
  "/akn:akomaNtoso/akn:*/akn:meta/akn:identification/akn:FRBRWork/akn:FRBRname/@value",
172
184
  {"akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0"},
173
185
  )
174
186
 
175
187
  @cached_property
176
188
  def court(self) -> str:
177
- return self._get_xpath_match_string(
189
+ return self.xml.get_xpath_match_string(
178
190
  "/akn:akomaNtoso/akn:*/akn:meta/akn:proprietary/uk:court/text()",
179
191
  {
180
192
  "uk": "https://caselaw.nationalarchives.gov.uk/akn",
@@ -184,7 +196,7 @@ class Document:
184
196
 
185
197
  @cached_property
186
198
  def document_date_as_string(self) -> str:
187
- return self._get_xpath_match_string(
199
+ return self.xml.get_xpath_match_string(
188
200
  "/akn:akomaNtoso/akn:*/akn:meta/akn:identification/akn:FRBRWork/akn:FRBRdate/@date",
189
201
  {"akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0"},
190
202
  )
@@ -208,7 +220,7 @@ class Document:
208
220
  self, name: Optional[str] = None
209
221
  ) -> list[datetime.datetime]:
210
222
  name_filter = f"[@name='{name}']" if name else ""
211
- iso_datetimes = self._get_xpath_match_strings(
223
+ iso_datetimes = self.xml.get_xpath_match_strings(
212
224
  "/akn:akomaNtoso/akn:*/akn:meta/akn:identification/akn:FRBRManifestation"
213
225
  f"/akn:FRBRdate{name_filter}/@date",
214
226
  {"akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0"},
@@ -250,6 +262,14 @@ class Document:
250
262
  def is_held(self) -> bool:
251
263
  return self.api_client.get_property(self.uri, "editor-hold") == "true"
252
264
 
265
+ @cached_property
266
+ def is_locked(self) -> bool:
267
+ return self.checkout_message is not None
268
+
269
+ @cached_property
270
+ def checkout_message(self) -> Optional[str]:
271
+ return self.api_client.get_judgment_checkout_status_message(self.uri)
272
+
253
273
  @cached_property
254
274
  def source_name(self) -> str:
255
275
  return self.api_client.get_property(self.uri, "source-name")
@@ -324,17 +344,7 @@ class Document:
324
344
 
325
345
  @cached_property
326
346
  def content_as_xml(self) -> str:
327
- return self.api_client.get_judgment_xml(self.uri, show_unpublished=True)
328
-
329
- @cached_property
330
- def content_as_xml_bytestring(self) -> bytes:
331
- return self.api_client.get_judgment_xml_bytestring(
332
- self.uri, show_unpublished=True
333
- )
334
-
335
- @cached_property
336
- def content_as_xml_tree(self) -> Any:
337
- return etree.fromstring(self.content_as_xml_bytestring)
347
+ return self.xml.xml_as_string
338
348
 
339
349
  def content_as_html(
340
350
  self,
@@ -393,13 +403,10 @@ class Document:
393
403
 
394
404
  :return: `True` if there was a complete parser failure, otherwise `False`
395
405
  """
396
- if "error" in self._get_root():
406
+ if "error" in self.xml.root_element:
397
407
  return True
398
408
  return False
399
409
 
400
- def _get_root(self) -> str:
401
- return get_judgment_root(self.content_as_xml_bytestring)
402
-
403
410
  @cached_property
404
411
  def has_name(self) -> bool:
405
412
  if not self.name:
@@ -511,14 +518,6 @@ class Document:
511
518
  else:
512
519
  raise DocumentNotSafeForDeletion()
513
520
 
514
- def _get_xpath_match_string(self, xpath: str, namespaces: Dict[str, str]) -> str:
515
- return get_xpath_match_string(self.content_as_xml_tree, xpath, namespaces)
516
-
517
- def _get_xpath_match_strings(
518
- self, xpath: str, namespaces: Dict[str, str]
519
- ) -> list[str]:
520
- return get_xpath_match_strings(self.content_as_xml_tree, xpath, namespaces)
521
-
522
521
  def overwrite(self, new_citation: str) -> None:
523
522
  self.api_client.overwrite_document(self.uri, new_citation)
524
523
 
@@ -556,3 +555,36 @@ class Document:
556
555
  reference=self.consignment_reference,
557
556
  parser_instructions=parser_instructions,
558
557
  )
558
+
559
+ class XML:
560
+ """
561
+ Represents the XML of a document, and should contain all methods for interacting with it.
562
+ """
563
+
564
+ def __init__(self, xml_bytestring: bytes):
565
+ """
566
+ :raises NonXMLDocumentError: This document is not valid XML
567
+ """
568
+ try:
569
+ self.xml_as_tree: etree.Element = etree.fromstring(xml_bytestring)
570
+ except etree.XMLSyntaxError:
571
+ raise NonXMLDocumentError
572
+
573
+ @property
574
+ def xml_as_string(self) -> str:
575
+ """
576
+ :return: A string representation of this document's XML tree.
577
+ """
578
+ return str(etree.tostring(self.xml_as_tree).decode(encoding="utf-8"))
579
+
580
+ @property
581
+ def root_element(self) -> str:
582
+ return str(self.xml_as_tree.tag)
583
+
584
+ def get_xpath_match_string(self, xpath: str, namespaces: Dict[str, str]) -> str:
585
+ return get_xpath_match_string(self.xml_as_tree, xpath, namespaces)
586
+
587
+ def get_xpath_match_strings(
588
+ self, xpath: str, namespaces: Dict[str, str]
589
+ ) -> list[str]:
590
+ return get_xpath_match_strings(self.xml_as_tree, xpath, namespaces)
@@ -21,7 +21,7 @@ class Judgment(NeutralCitationMixin, Document):
21
21
  @cached_property
22
22
  def neutral_citation(self) -> str:
23
23
  return get_xpath_match_string(
24
- self.content_as_xml_tree,
24
+ self.xml.xml_as_tree,
25
25
  "/akn:akomaNtoso/akn:*/akn:meta/akn:proprietary/uk:cite/text()",
26
26
  {
27
27
  "uk": "https://caselaw.nationalarchives.gov.uk/akn",
@@ -21,7 +21,7 @@ class PressSummary(NeutralCitationMixin, Document):
21
21
  @cached_property
22
22
  def neutral_citation(self) -> str:
23
23
  return get_xpath_match_string(
24
- self.content_as_xml_tree,
24
+ self.xml.xml_as_tree,
25
25
  "/akn:akomaNtoso/akn:doc/akn:preface/akn:p/akn:neutralCitation/text()",
26
26
  {
27
27
  "akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0",
@@ -1,5 +1,4 @@
1
1
  import re
2
- import xml.etree.ElementTree as ET
3
2
  from typing import TypedDict
4
3
 
5
4
  from requests_toolbelt.multipart.decoder import BodyPart
@@ -12,14 +11,6 @@ akn_namespace = {"akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0"}
12
11
  uk_namespace = {"uk": "https://caselaw.nationalarchives.gov.uk/akn"}
13
12
 
14
13
 
15
- def get_judgment_root(judgment_xml: bytes) -> str:
16
- try:
17
- parsed_xml = ET.XML(judgment_xml)
18
- return parsed_xml.tag
19
- except ET.ParseError:
20
- return "error"
21
-
22
-
23
14
  class VersionsDict(TypedDict):
24
15
  uri: str
25
16
  version: int
@@ -2,6 +2,7 @@ from typing import List
2
2
 
3
3
  from lxml import etree
4
4
 
5
+ from caselawclient.Client import MarklogicApiClient
5
6
  from caselawclient.responses.search_result import SearchResult
6
7
 
7
8
 
@@ -13,22 +14,14 @@ class SearchResponse:
13
14
  NAMESPACES = {"search": "http://marklogic.com/appservices/search"}
14
15
  """ Namespaces used in XPath expressions."""
15
16
 
16
- def __init__(self, node: etree._Element) -> None:
17
+ def __init__(self, node: etree._Element, client: MarklogicApiClient) -> None:
17
18
  """
18
19
  Initializes a SearchResponse instance from an xml node.
19
20
 
20
21
  :param node: The XML data as an etree element
21
22
  """
22
23
  self.node = node
23
-
24
- @staticmethod
25
- def from_response_string(xml: str) -> "SearchResponse":
26
- """
27
- Constructs a SearchResponse instance from an xml response string.
28
-
29
- :param xml: The XML data as a string
30
- """
31
- return SearchResponse(etree.fromstring(xml))
24
+ self.client = client
32
25
 
33
26
  @property
34
27
  def total(self) -> str:
@@ -51,9 +44,4 @@ class SearchResponse:
51
44
  results = self.node.xpath(
52
45
  "//search:response/search:result", namespaces=self.NAMESPACES
53
46
  )
54
- return [
55
- SearchResult(
56
- result,
57
- )
58
- for result in results
59
- ]
47
+ return [SearchResult(result, self.client) for result in results]
@@ -2,6 +2,7 @@ import logging
2
2
  import os
3
3
  from datetime import datetime
4
4
  from enum import Enum
5
+ from functools import cached_property
5
6
  from typing import Dict, Optional
6
7
 
7
8
  from dateutil import parser as dateparser
@@ -9,7 +10,7 @@ from dateutil.parser import ParserError
9
10
  from ds_caselaw_utils.courts import Court, CourtNotFoundException, courts
10
11
  from lxml import etree
11
12
 
12
- from caselawclient.Client import api_client
13
+ from caselawclient.Client import MarklogicApiClient
13
14
  from caselawclient.models.documents import DocumentURIString
14
15
  from caselawclient.xml_helpers import get_xpath_match_string
15
16
 
@@ -44,20 +45,6 @@ class SearchResultMetadata:
44
45
  self.node = node
45
46
  self.last_modified = last_modified
46
47
 
47
- @staticmethod
48
- def create_from_uri(uri: DocumentURIString) -> "SearchResultMetadata":
49
- """
50
- Create a SearchResultMetadata instance from a search result URI.
51
-
52
- :param uri: The URI of the search result
53
-
54
- :return: The created SearchResultMetadata instance
55
- """
56
- response_text = api_client.get_properties_for_search_results([uri])
57
- last_modified = api_client.get_last_modified(uri)
58
- root = etree.fromstring(response_text)
59
- return SearchResultMetadata(root, last_modified)
60
-
61
48
  @property
62
49
  def author(self) -> str:
63
50
  """
@@ -162,12 +149,13 @@ class SearchResult:
162
149
  }
163
150
  """ Namespace mappings used in XPath expressions. """
164
151
 
165
- def __init__(self, node: etree._Element):
152
+ def __init__(self, node: etree._Element, client: MarklogicApiClient):
166
153
  """
167
154
  :param node: The XML element representing the search result
168
155
  """
169
156
 
170
157
  self.node = node
158
+ self.client = client
171
159
 
172
160
  @property
173
161
  def uri(self) -> DocumentURIString:
@@ -259,15 +247,15 @@ class SearchResult:
259
247
  xslt_transform = etree.XSLT(etree.parse(file_path))
260
248
  return str(xslt_transform(self.node))
261
249
 
262
- @property
250
+ @cached_property
263
251
  def metadata(self) -> SearchResultMetadata:
264
252
  """
265
- :return: The metadata of the search result
253
+ :return: A `SearchResultMetadata` instance representing the metadata of this result
266
254
  """
267
-
268
- return SearchResultMetadata.create_from_uri(
269
- self.uri,
270
- )
255
+ response_text = self.client.get_properties_for_search_results([self.uri])
256
+ last_modified = self.client.get_last_modified(self.uri)
257
+ root = etree.fromstring(response_text)
258
+ return SearchResultMetadata(root, last_modified)
271
259
 
272
260
  def _get_xpath_match_string(self, path: str) -> str:
273
261
  return get_xpath_match_string(self.node, path, namespaces=self.NAMESPACES)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ds-caselaw-marklogic-api-client
3
- Version: 17.3.0
3
+ Version: 18.0.0
4
4
  Summary: An API client for interacting with the underlying data in Find Caselaw.
5
5
  Home-page: https://github.com/nationalarchives/ds-caselaw-custom-api-client
6
6
  Keywords: national archives,caselaw
@@ -14,7 +14,7 @@ Requires-Dist: boto3 (>=1.26.112,<2.0.0)
14
14
  Requires-Dist: certifi (>=2022.12.7,<2024.0.0)
15
15
  Requires-Dist: charset-normalizer (>=3.0.0,<4.0.0)
16
16
  Requires-Dist: django-environ (>=0.11.0,<0.12.0)
17
- Requires-Dist: ds-caselaw-utils (>=1.3.0,<2.0.0)
17
+ Requires-Dist: ds-caselaw-utils (>=1.3.3,<2.0.0)
18
18
  Requires-Dist: idna (>=3.4,<4.0)
19
19
  Requires-Dist: lxml (>=4.9.2,<5.0.0)
20
20
  Requires-Dist: memoization (>=0.4.0,<0.5.0)
@@ -1,21 +1,21 @@
1
- caselawclient/Client.py,sha256=tObD5UsxIavhKv_MlKLiE8ZDKOq4seim0e-5tUL6L-g,35031
2
- caselawclient/__init__.py,sha256=Tz651uskPQCtnjmUcQ8_TB5biTytJOMbcMHGkWSBUrw,1107
1
+ caselawclient/Client.py,sha256=c8DvbUAhBGpraD5oj1sVOtkco-j6Vq1zG5zshAOiuHI,34589
2
+ caselawclient/__init__.py,sha256=DY-caubLDQWWingSdsBWgovDNXh8KcnkI6kwz08eIFk,612
3
3
  caselawclient/client_helpers/__init__.py,sha256=6vUjIwi777iaNDBUYwWmpzgAXeFHeXnmmMBniVmjUP8,3830
4
- caselawclient/client_helpers/search_helpers.py,sha256=ImrH4K8-NKD8qNuBL4z0IQq8Fnkr2eFSYqUy9wDSu5U,1432
4
+ caselawclient/client_helpers/search_helpers.py,sha256=DYgUltPq8fFI2KkLRqH1-8zpbb8_swBFyBvvgBbinig,1514
5
5
  caselawclient/content_hash.py,sha256=DF7ujrQPNf1bTSbK0mIIaC5qx6CmF5I0xlQ7uIG0zYI,2236
6
6
  caselawclient/errors.py,sha256=3rsbOQ11hIhm7-UABcHNMcs9XgcrIzytAP0koyZBLWM,3195
7
7
  caselawclient/models/__init__.py,sha256=kd23EUpvaC7aLHdgk8farqKAQEx3lf7RvNT2jEatvlg,68
8
- caselawclient/models/documents.py,sha256=7hxbbYIe_3drgigMxmJa--Cq9n82qzOavD6NXK5kXDc,18548
9
- caselawclient/models/judgments.py,sha256=wewBm-VRQ3waNKTbSlYktfD0hxxX7GFdRSdt2anFtgQ,1024
8
+ caselawclient/models/documents.py,sha256=qiIb2sKBL_sbrAOwRI62MWM6V0GRXndzxWbfx4XbQbA,19447
9
+ caselawclient/models/judgments.py,sha256=TcAsn27K--QQAfaaUZ8biybB9OeVS__91FRlwaG16HY,1020
10
10
  caselawclient/models/neutral_citation_mixin.py,sha256=qqB1K4IHVy0XvdY40sfVywZ6VGaZ9ojHcVOQRyi0Vhc,1752
11
- caselawclient/models/press_summaries.py,sha256=8jcRc_ts-bPzMay9bGTzTgogY7PSOs1Oqy2CqhwDU50,998
12
- caselawclient/models/utilities/__init__.py,sha256=ctwgNq_TynEI9O_f3VoUpUWN5oVeOIJaTwt_uXmY1_E,1318
11
+ caselawclient/models/press_summaries.py,sha256=5c1jpVhVtmIMN8AeHMywGXvz4H55kKAIUaaaVims6Tw,994
12
+ caselawclient/models/utilities/__init__.py,sha256=aL1a2nDacPxninETeaVZKwOxZemgvm73IcpWgMNXoGc,1100
13
13
  caselawclient/models/utilities/aws.py,sha256=HxmcoDXPpkDfxDH-tm3HtxnMCfOC2qIuy6PZs46OveY,7645
14
14
  caselawclient/models/utilities/move.py,sha256=_SKzO1UVXHFIVvWfT4nuCwdov7acp8tYzzEg-vVfUyg,5372
15
15
  caselawclient/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  caselawclient/responses/__init__.py,sha256=2-5NJn_PXPTje_W4dHeHYaNRN6vXK4UcB9eLLNUAKa4,67
17
- caselawclient/responses/search_response.py,sha256=BIWiG1es_k38hc14X880np3AJMeX8A0p96SoomguLLI,1554
18
- caselawclient/responses/search_result.py,sha256=LeiTQ-docnNdzMz59MsXAJQNlkcfL8VsNgU-rc276lI,7422
17
+ caselawclient/responses/search_response.py,sha256=OlzXOEnCg-4HdfOYfeIMdk-UfROOI_Nz-etfznFffok,1335
18
+ caselawclient/responses/search_result.py,sha256=M6BSzkGRn0TY2Qs17bplv1RbhBHECAypUjnXnoslltc,7198
19
19
  caselawclient/responses/xsl/search_match.xsl,sha256=g_CYe8bOQ-Hnzt1YaobbdEP5B71XtEJhJG8jrfc9rG0,918
20
20
  caselawclient/search_parameters.py,sha256=cT2pJueQNNNbbzcwpEoTz_9yHVv9ZD6GsfycXKDtgCQ,3075
21
21
  caselawclient/xml_helpers.py,sha256=Ihy6OxdEHm7LFePX0XF6uzpyp6yo7U13zyP0nhpBguU,582
@@ -53,7 +53,7 @@ caselawclient/xquery/validate_all_documents.xqy,sha256=z_0YEXmRcZ-FaJM0ouKiTjdI4
53
53
  caselawclient/xquery/xslt.xqy,sha256=w57wNijH3dkwHkpKeAxqjlghVflQwo8cq6jS_sm-erM,199
54
54
  caselawclient/xquery/xslt_transform.xqy,sha256=smyFFxqmtkuOzBd2l7uw6K2oAsYctudrP8omdv_XNAM,2463
55
55
  caselawclient/xquery_type_dicts.py,sha256=MZwjEURV_s-USIeX_qr_5VXcfswwoBgFQdw1ITVmtwQ,4726
56
- ds_caselaw_marklogic_api_client-17.3.0.dist-info/LICENSE.md,sha256=fGMzyyLuQW-IAXUeDSCrRdsYW536aEWThdbpCjo6ZKg,1108
57
- ds_caselaw_marklogic_api_client-17.3.0.dist-info/METADATA,sha256=KY3bZt-Of5llGDsCRn2nrmjWsKRlXLruLrAuOu_GDLM,4006
58
- ds_caselaw_marklogic_api_client-17.3.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
59
- ds_caselaw_marklogic_api_client-17.3.0.dist-info/RECORD,,
56
+ ds_caselaw_marklogic_api_client-18.0.0.dist-info/LICENSE.md,sha256=fGMzyyLuQW-IAXUeDSCrRdsYW536aEWThdbpCjo6ZKg,1108
57
+ ds_caselaw_marklogic_api_client-18.0.0.dist-info/METADATA,sha256=mXQ4jemxpauxmDZMzRDXYgN0_Qc-dyWfxWCZl7bVY-A,4006
58
+ ds_caselaw_marklogic_api_client-18.0.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
59
+ ds_caselaw_marklogic_api_client-18.0.0.dist-info/RECORD,,