Skip to content

Commit

Permalink
Merge pull request #63 from LeJeko/dateformats
Browse files Browse the repository at this point in the history
Full layout metadata, date formats, value lists
  • Loading branch information
davidhamann committed Jan 18, 2024
2 parents 756cbe8 + 9cd9b90 commit 62ba449
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 8 deletions.
1 change: 1 addition & 0 deletions fmrest/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
TIMEOUT = int(os.environ.get('fmrest_timeout', 10))

API_VERSIONS = ('v1', 'v2', 'vLatest')
API_DATE_FORMATS = [('us', '0'), ('file', '1'), ('iso-8601', '2')]
API_PATH_PREFIX = '/fmi/data/{version}'
API_PATH: Dict[str, Any] = {
'meta': {
Expand Down
86 changes: 78 additions & 8 deletions fmrest/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import requests
from .utils import (request, build_portal_params, build_script_params,
filename_from_url, PlaceholderDict)
from .const import (PORTAL_PREFIX, FMSErrorCode, API_VERSIONS, API_PATH_PREFIX,
from .const import (PORTAL_PREFIX, FMSErrorCode, API_VERSIONS, API_DATE_FORMATS, API_PATH_PREFIX,
API_PATH)
from .exceptions import BadJSON, FileMakerError, RecordError
from .record import Record
Expand Down Expand Up @@ -155,6 +155,9 @@ def _get_api_path(self, resource: str,
path.format_map(PlaceholderDict(database=self.database,
layout=request_layout)))

def _date_format_for_keyword(self, keyword: str) -> Optional[str]:
return next((format[1] for format in API_DATE_FORMATS if format[0] == keyword), None)

def _with_auto_relogin(f):
@wraps(f)
def wrapper(self, *args, **kwargs):
Expand Down Expand Up @@ -371,7 +374,8 @@ def get_record(self, record_id: int, portals: Optional[List[Dict]] = None,
scripts: Optional[Dict[str, List]] = None,
layout: Optional[str] = None,
request_layout: Optional[str] = None,
response_layout: Optional[str] = None) -> Record:
response_layout: Optional[str] = None,
date_format: Optional[str] = None) -> Record:
"""Fetches record with given ID and returns Record instance
Parameters
Expand Down Expand Up @@ -403,6 +407,13 @@ def get_record(self, record_id: int, portals: Optional[List[Dict]] = None,
Set the response layout. This is helpful, for example, if you
want to limit the number of fields/portals being returned and have
a dedicated response layout.
date_format : str, optional
The date format. Choices are:
'us' for US format MM/DD/YYYY,
'file' for file locale format,
'iso-8601' for ISO format YYYY-MM-DD.
If not specified, the default value is 'us'.
Note that dates should always be sent in the US format when creating or editing a record.
"""
if layout is not None:
warnings.warn('layout parameter is deprecated and will be removed '
Expand All @@ -413,6 +424,8 @@ def get_record(self, record_id: int, portals: Optional[List[Dict]] = None,

params = build_portal_params(portals, True) if portals else {}

params['dateformats'] = self._date_format_for_keyword(date_format)

# set response layout; layout param is only handled for backward-
# compatibility
params['layout.response'] = layout if layout else response_layout
Expand Down Expand Up @@ -496,7 +509,8 @@ def get_records(self, offset: int = 1, limit: int = 100,
scripts: Optional[Dict[str, List]] = None,
layout: Optional[str] = None,
request_layout: Optional[str] = None,
response_layout: Optional[str] = None) -> Foundset:
response_layout: Optional[str] = None,
date_format: Optional[str] = None) -> Foundset:
"""Requests all records with given offset and limit and returns result as
(sorted) Foundset instance.
Expand Down Expand Up @@ -528,6 +542,13 @@ def get_records(self, offset: int = 1, limit: int = 100,
Set the response layout. This is helpful, for example, if you
want to limit the number of fields/portals being returned and have
a dedicated response layout.
date_format : str, optional
The date format. Choices are:
'us' for US format MM/DD/YYYY,
'file' for file locale format,
'iso-8601' for ISO format YYYY-MM-DD.
If not specified, the default value is 'us'.
Note that dates should always be sent in the US format when creating or editing a record.
"""
if layout is not None:
warnings.warn('layout parameter is deprecated and will be removed '
Expand All @@ -539,6 +560,8 @@ def get_records(self, offset: int = 1, limit: int = 100,
params['_offset'] = offset
params['_limit'] = limit

params['dateformats'] = self._date_format_for_keyword(date_format)

# set response layout; layout param is only handled for backward-
# compatibility
params['layout.response'] = layout if layout else response_layout
Expand All @@ -564,7 +587,8 @@ def find(self, query: List[Dict[str, Any]],
scripts: Optional[Dict[str, List]] = None,
layout: Optional[str] = None,
request_layout: Optional[str] = None,
response_layout: Optional[str] = None) -> Foundset:
response_layout: Optional[str] = None,
date_format: Optional[str] = None) -> Foundset:
"""Finds all records matching query and returns result as a Foundset instance.
Parameters
Expand Down Expand Up @@ -605,6 +629,13 @@ def find(self, query: List[Dict[str, Any]],
Set the response layout. This is helpful, for example, if you
want to limit the number of fields/portals being returned and have
a dedicated response layout.
date_format : str, optional
The date format. Choices are:
'us' for US format MM/DD/YYYY,
'file' for file locale format,
'iso-8601' for ISO format YYYY-MM-DD.
If not specified, the default value is 'us'.
Note that dates should always be sent in the US format when creating or editing a record.
"""
if layout is not None:
warnings.warn('layout parameter is deprecated and will be removed '
Expand All @@ -617,6 +648,7 @@ def find(self, query: List[Dict[str, Any]],
'sort': sort,
'limit': str(limit),
'offset': str(offset),
'dateformats': self._date_format_for_keyword(date_format),
# "layout" param is only handled for backwards-compatibility
'layout.response': layout if layout else response_layout
}
Expand Down Expand Up @@ -796,19 +828,57 @@ def get_scripts(self) -> Dict:
return response.get('scripts', None)

@_with_auto_relogin
def get_layout(self, layout: Optional[str] = None) -> Dict:
"""Fetches layout metadata and returns Dict instance
def get_layout(self, layout: Optional[str] = None, metadata: Optional[str] = None) -> Dict:
"""Fetches layout metadata and returns Dict instance of metadata parameter
Parameters
-----------
none
layout : str, optional
Sets the layout name for this request. This takes precedence over
the value stored in the Server instance's layout attribute
metadata : str, optional
Options to get all or specifics metadatas.
Choices are 'fields', 'portals', 'value_lists' or 'all'.
Default is 'fields'
"""
target_layout = layout if layout else self.layout
path = self._get_api_path('meta.layouts') + f'/{target_layout}'

response = self._call_filemaker('GET', path)

return response.get('fieldMetaData', None)
if metadata == 'all':
return response
elif metadata == 'portals':
return response.get('portalMetaData', None)
elif metadata == 'value_lists':
return response.get('valueLists', None)
else:
return response.get('fieldMetaData', None)

@_with_auto_relogin
def get_value_list_values(self, name: str, layout: Optional[str] = None) -> List[Tuple[str, str]]:
"""Retrieves layout metadata and returns a list of tuple of (value, display value) of a named FileMaker value list
in the format [('a','A'), ('b','B'), ('c','C')]
Parameters
-----------
name : str
The list name to retreive values
layout : str, optional
Sets the layout name for this request. This takes precedence over
the value stored in the Server instance's layout attribute
"""
target_layout = layout if layout else self.layout
value_lists = self.get_layout(layout=target_layout, metadata='value_lists')

values = []

for vlist in value_lists:
if vlist['name'] == name:
values += [(v['value'], v['displayValue']) for v in vlist['values']]
break

return values

def _call_filemaker(self, method: str, path: str,
data: Optional[Dict] = None,
Expand Down

0 comments on commit 62ba449

Please sign in to comment.