{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# An Introduction to python-fmrest (dotfmp demo)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "python-fmrest is a wrapper around the FileMaker Data API.\n", "\n", "No need to worry about manually requesting access tokens, setting the right http headers, parsing responses, ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use cases" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some things you may use the python-fmrest library for:\n", "\n", "- Build a backend for a web app that works with FileMaker data\n", "- Use python-fmrest together with a rest framework to build your own data API as middleware \n", " (so that you don't expose the whole FM data API to a third party, but only allowed endpoints/actions)\n", "- Explore your FileMaker data with data analysis tools from the Python ecosystem\n", "- Anything else you could do in the past with the CWP/XML API" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Installation (get you up and running quickly)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you haven't worked with Python and Virtualenvs before:\n", "\n", "- `brew install python3`\n", " - No brew? `/usr/bin/ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"`)\n", "- `pip3 install virtualenv`\n", "\n", "If you have worked with Python and Virtualenvs before, or after executing the steps above:\n", "\n", "- ```virtualenv venv --python=`which python3` ```\n", "- `source venv/bin/activate`\n", "- `pip install python-fmrest`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Link to this notebook: https://github.com/davidhamann/python-fmrest/tree/master/examples/conf_dotfmp_2018.ipynb" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The demo setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- FileMaker Server 17, with Data API enabled, in a VM\n", "- Hosted Database called \"Planets\"\n", " - incl. account with `fmrest` extended privilege\n", "- Example code running in a Jupyter Notebook with a Python 3.6 kernel\n", " - Not necessarily needed, but nice for exploration and presentation (mixing code and annotations)\n", "- Installed python-fmrest library" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Import module" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import fmrest" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fmrest.__version__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create server instance" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fms = fmrest.Server(\n", " 'https://dotfmp-demo.davidhamann.de',\n", " user='admin',\n", " password='admin',\n", " database='planets',\n", " layout='Planets',\n", " # if you are testing without cert/domain you may need the parameter verify_ssl=False here.\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This gives you a server instance which provides all further methods to interact with the Data API." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fms" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Login" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Obtain a token from FMS:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fms.login()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get records and access field and portal data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "List all records from the Planets table" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "planets = fms.get_records()\n", "for planet in planets:\n", " print(f'{planet.id}, {planet.record_id}, {planet.name}')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "type(planets), planets" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Look at (some of) the moons of Jupiter (list records of a portal)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "record = fms.get_record(5, portals=[{'name': 'moons', 'limit': 5}])\n", "\n", "portal = record['portal_moons']\n", "record, portal" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fetching a record always gives you a Record instance. The portal rows, however, are returned as a Foundset." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for row in portal:\n", " print(row['Moons::name'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can inspect what fields are available:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "record.keys()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "record.values()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "record.to_dict()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And access the value by attribute or key:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "record.name, record['atmosphere']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far we have seen Server, Foundset, Record. These are the main classes you need to be aware of when working with the library." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Find records" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "find_request = [{'name': 'Earth'}, {'name': 'Jupiter'}]\n", "foundset = fms.find(query=find_request)\n", "\n", "earth = foundset[0]\n", "earth" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Edit a record" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "earth.name = 'Blue Dot'\n", "earth" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fms.edit(earth)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Handle outdated record values:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# change back\n", "earth.name = 'Earth'\n", "fms.edit(earth, validate_mod_id=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create a record" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pluto = fms.create_record({'name': 'Pluto', 'id': 9})\n", "pluto" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Delete a record" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fms.delete_record(pluto)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Performing scripts (new in v17)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fms.get_record(\n", " 1,\n", " scripts={\n", " 'after': ['say_hello', 'dotfmp']\n", " }\n", ")\n", "fms.last_script_result" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "fms.last_script_result['after'][1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Uploading container data (new in v17)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open('../scratch/dotfmp_logo.png', 'rb') as image:\n", " result = fms.upload_container(3, 'image', image) # upload dotfmp logo into field with name \"image\" of record 3\n", "result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now retrieve the image again:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "earth = fms.get_record(3)\n", "earth.image" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "name, type_, length, response = fms.fetch_file(earth.image)\n", "name, type_, length" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import Image\n", "Image(response.content)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exceptions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "find_request = [{'name': 'something that doesn\\'t exist'}]\n", "foundset = fms.find(query=find_request)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Foundset into DataFrame" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Turn Foundset into a Pandas DataFrame to do statistical analyses on your dataset, work with missing data, reshape/pivot, perform joins/merges, plot with matplotlib, export, etc." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foundset = fms.get_records()\n", "df = foundset.to_df()\n", "df.loc[:, df.columns != 'image']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df[['name', 'atmosphere', 'rings', 'confirmed_moons', 'mass']].set_index('name').T" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.describe()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... or plot some data with matplotlib" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [ "%matplotlib notebook\n", "df.plot(x='name', y='confirmed_moons')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... or export the data in a different format" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "path = 'data.csv'\n", "df.to_csv(path, sep=\";\", index=False)\n", "from IPython.display import FileLink\n", "FileLink(path)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Read about Pandas here: https://pandas.pydata.org" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More on python-fmrest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- **More examples**: https://github.com/davidhamann/python-fmrest/tree/master/examples\n", "- GitHub: https://github.com/davidhamann/python-fmrest (MIT License)\n", "- v16 to v17 switch: https://davidhamann.de/2018/05/15/python-fmrest-and-filemaker-17-data-api/\n", "- Support and issues: https://github.com/davidhamann/python-fmrest/issues" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More on me" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- https://davidhamann.de/conference/\n", "- https://twitter.com/d_hamann" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Any questions? Ask now or later." ] } ], "metadata": { "kernelspec": { "display_name": "fmrest venv", "language": "python", "name": "venv" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.2" } }, "nbformat": 4, "nbformat_minor": 2 }