{"id":16736,"date":"2017-03-29T12:15:14","date_gmt":"2017-03-29T09:15:14","guid":{"rendered":"https:\/\/www.webcodegeeks.com\/?p=16736"},"modified":"2017-03-28T12:27:35","modified_gmt":"2017-03-28T09:27:35","slug":"luigi-defining-dynamic-requirements-output-files","status":"publish","type":"post","link":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/","title":{"rendered":"Luigi: Defining dynamic requirements (on output files)"},"content":{"rendered":"<p>In <a href=\"https:\/\/www.webcodegeeks.com\/python\/luigi-externalprogramtask-example-converting-json-csv\/\">my last blog post<\/a> I showed how to convert a JSON document containing meetup groups into a CSV file using Luigi, the Python library for building data pipelines. As well as creating that CSV file I wanted to go back to the <a href=\"https:\/\/www.meetup.com\/meetup_api\/\">meetup.com API<\/a> and download all the members of those groups.<\/p>\n<p>This was a rough flow of what i wanted to do:<\/p>\n<ul>\n<li>Take JSON document containing all groups<\/li>\n<li>Parse that document and for each group:\n<ul>\n<li>Call the \/members endpoint<\/li>\n<li>Save each one of those files as a JSON file<\/li>\n<\/ul>\n<\/li>\n<li>Iterate over all those JSON files and create a members CSV file<\/li>\n<\/ul>\n<p>In the previous post we created the <cite>GroupsToJSON<\/cite> task which calls the \/groups endpoint on the meetup API and creates the file \/tmp\/groups.json.<\/p>\n<p>Our new task has that as its initial requirement:<\/p>\n<pre class=\"brush:py\">class MembersToCSV(luigi.Task):\r\n    key = luigi.Parameter()\r\n    lat = luigi.Parameter()\r\n    lon = luigi.Parameter()\r\n\u00a0\r\n    def requires(self):\r\n        yield GroupsToJSON(self.key, self.lat, self.lon)<\/pre>\n<p>But we also want to create a requirement on a task that will make those calls to the \/members endpoint and store the result in a JSON file.<\/p>\n<p>One of the patterns that Luigi imposes on us is that each task should only create one file so actually we have a requirement on a collection of tasks rather than just one. It took me a little while to get my head around that!<\/p>\n<p>We don\u2019t know the parameters of those tasks at compile time \u2013 we can only calculate them by parsing the JSON file produced by <cite>GroupsToJSON<\/cite>.<\/p>\n<p>In Luigi terminology what we want to create is a <a href=\"http:\/\/luigi.readthedocs.io\/en\/stable\/tasks.html#dynamic-dependencies\">dynamic requirement<\/a>. A dynamic requirement is defined inside the run method of a task and can rely on the output of any tasks specified in the requires method, which is exactly what we need.<\/p>\n<p>This code does the delegating part of the job:<\/p>\n<pre class=\"brush:py\">class MembersToCSV(luigi.Task):\r\n    key = luigi.Parameter()\r\n    lat = luigi.Parameter()\r\n    lon = luigi.Parameter()\r\n\u00a0\r\n\u00a0\r\n    def run(self):\r\n        outputs = []\r\n        for input in self.input():\r\n            with input.open('r') as group_file:\r\n                groups_json = json.load(group_file)\r\n                groups = [str(group['id']) for group in groups_json]\r\n\u00a0\r\n\u00a0\r\n                for group_id in groups:\r\n                    members = MembersToJSON(group_id, self.key)\r\n                    outputs.append(members.output().path)\r\n                    yield members\r\n\u00a0\r\n\u00a0\r\n    def requires(self):\r\n        yield GroupsToJSON(self.key, self.lat, self.lon)<\/pre>\n<p>Inside our run method we iterate over the output of GroupsToJSON (which is our input) and we yield to another task as well as collecting its outputs in the array outputs that we\u2019ll use later.<br \/>\n<cite>MembersToJSON<\/cite> looks like this:<\/p>\n<pre class=\"brush:py\">class MembersToJSON(luigi.Task):\r\n    group_id = luigi.IntParameter()\r\n    key = luigi.Parameter()\r\n\u00a0\r\n\u00a0\r\n    def run(self):\r\n        results = []\r\n        uri = \"https:\/\/api.meetup.com\/2\/members?&amp;group_id={0}&amp;key={1}\".format(self.group_id, self.key)\r\n        while True:\r\n            if uri is None:\r\n                break\r\n            r = requests.get(uri)\r\n            response = r.json()\r\n            for result in response[\"results\"]:\r\n                results.append(result)\r\n            uri = response[\"meta\"][\"next\"] if response[\"meta\"][\"next\"] else None\r\n\u00a0\r\n\u00a0\r\n        with self.output().open(\"w\") as output:\r\n            json.dump(results, output)\r\n\u00a0\r\n    def output(self):\r\n        return luigi.LocalTarget(\"\/tmp\/members\/{0}.json\".format(self.group_id))<\/pre>\n<p>This task generates one file per group containing a list of all the members of that group.<\/p>\n<p>We can now go back to <cite>MembersToCSV<\/cite> and convert those JSON files into a single CSV file:<\/p>\n<pre class=\"brush:py\">class MembersToCSV(luigi.Task):\r\n    out_path = \"\/tmp\/members.csv\"\r\n    key = luigi.Parameter()\r\n    lat = luigi.Parameter()\r\n    lon = luigi.Parameter()\r\n\u00a0\r\n\u00a0\r\n    def run(self):\r\n        outputs = []\r\n        for input in self.input():\r\n            with input.open('r') as group_file:\r\n                groups_json = json.load(group_file)\r\n                groups = [str(group['id']) for group in groups_json]\r\n\u00a0\r\n\u00a0\r\n                for group_id in groups:\r\n                    members = MembersToJSON(group_id, self.key)\r\n                    outputs.append(members.output().path)\r\n                    yield members\r\n\u00a0\r\n        with self.output().open(\"w\") as output:\r\n            writer = csv.writer(output, delimiter=\",\")\r\n            writer.writerow([\"id\", \"name\", \"joined\", \"topics\", \"groupId\"])\r\n\u00a0\r\n            for path in outputs:\r\n                group_id = path.split(\"\/\")[-1].replace(\".json\", \"\")\r\n                with open(path) as json_data:\r\n                    d = json.load(json_data)\r\n                    for member in d:\r\n                        topic_ids = \";\".join([str(topic[\"id\"]) for topic in member[\"topics\"]])\r\n                        if \"name\" in member:\r\n                            writer.writerow([member[\"id\"], member[\"name\"], member[\"joined\"], topic_ids, group_id])\r\n\u00a0\r\n    def output(self):\r\n        return luigi.LocalTarget(self.out_path)\r\n\u00a0\r\n    def requires(self):\r\n        yield GroupsToJSON(self.key, self.lat, self.lon)<\/pre>\n<p>We then just need to add our new task as a requirement of the wrapper task:<\/p>\n<p>And we\u2019re ready to roll:<\/p>\n<pre class=\"brush:bash\">$ PYTHONPATH=\".\" luigi --module blog --local-scheduler Meetup --workers 3<\/pre>\n<p>We\u2019ve defined the number of workers here as we can execute those calls to the \/members endpoint in parallel and there are ~ 600 calls to make.<\/p>\n<p>All the <a href=\"https:\/\/gist.github.com\/mneedham\/de3c67dd198e53303923cf40739fb74c\">code from both blog posts is available as a gist<\/a> if you want to play around with it.<\/p>\n<p>Any questions\/advice let me know in the comments or I\u2019m <a href=\"https:\/\/twitter.com\/markhneedham\">@markhneedham<\/a> on twitter.<\/p>\n<div class=\"attribution\">\n<table>\n<tbody>\n<tr>\n<td><span class=\"reference\">Reference: <\/span><\/td>\n<td><a href=\"http:\/\/www.markhneedham.com\/blog\/2017\/03\/28\/luigi-defining-dynamic-requirements-on-output-files\/\">Luigi: Defining dynamic requirements (on output files)<\/a> from our <a href=\"http:\/\/www.webcodegeeks.com\/join-us\/wcg\/\">WCG partner<\/a> Mark Needham at the <a href=\"http:\/\/www.markhneedham.com\/blog\/\">Mark Needham Blog<\/a> blog.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In my last blog post I showed how to convert a JSON document containing meetup groups into a CSV file using Luigi, the Python library for building data pipelines. As well as creating that CSV file I wanted to go back to the meetup.com API and download all the members of those groups. This was &hellip;<\/p>\n","protected":false},"author":48,"featured_media":1651,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[53],"tags":[459],"class_list":["post-16736","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python","tag-luigi"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Luigi: Defining dynamic requirements (on output files) - Web Code Geeks - 2026<\/title>\n<meta name=\"description\" content=\"In my last blog post I showed how to convert a JSON document containing meetup groups into a CSV file using Luigi, the Python library for building data\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Luigi: Defining dynamic requirements (on output files) - Web Code Geeks - 2026\" \/>\n<meta property=\"og:description\" content=\"In my last blog post I showed how to convert a JSON document containing meetup groups into a CSV file using Luigi, the Python library for building data\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/\" \/>\n<meta property=\"og:site_name\" content=\"Web Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/webcodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2017-03-29T09:15:14+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Mark Needham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Mark Needham\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/\"},\"author\":{\"name\":\"Mark Needham\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/848a54e2ee724e46069ce36c2e52e98e\"},\"headline\":\"Luigi: Defining dynamic requirements (on output files)\",\"datePublished\":\"2017-03-29T09:15:14+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/\"},\"wordCount\":473,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"keywords\":[\"Luigi\"],\"articleSection\":[\"Python\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/\",\"name\":\"Luigi: Defining dynamic requirements (on output files) - Web Code Geeks - 2026\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"datePublished\":\"2017-03-29T09:15:14+00:00\",\"description\":\"In my last blog post I showed how to convert a JSON document containing meetup groups into a CSV file using Luigi, the Python library for building data\",\"breadcrumb\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#primaryimage\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.webcodegeeks.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Python\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/python\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Luigi: Defining dynamic requirements (on output files)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"name\":\"Web Code Geeks\",\"description\":\"Web Developers Resource Center\",\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.webcodegeeks.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/webcodegeeks\",\"https:\/\/x.com\/webcodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/848a54e2ee724e46069ce36c2e52e98e\",\"name\":\"Mark Needham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/5489baed26ce2d932bf951ecfb47afe80bec45d3648c23521d87c83b8f1c3ea9?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/5489baed26ce2d932bf951ecfb47afe80bec45d3648c23521d87c83b8f1c3ea9?s=96&d=mm&r=g\",\"caption\":\"Mark Needham\"},\"sameAs\":[\"http:\/\/www.markhneedham.com\/blog\/\"],\"url\":\"https:\/\/www.webcodegeeks.com\/author\/mark-needham\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Luigi: Defining dynamic requirements (on output files) - Web Code Geeks - 2026","description":"In my last blog post I showed how to convert a JSON document containing meetup groups into a CSV file using Luigi, the Python library for building data","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/","og_locale":"en_US","og_type":"article","og_title":"Luigi: Defining dynamic requirements (on output files) - Web Code Geeks - 2026","og_description":"In my last blog post I showed how to convert a JSON document containing meetup groups into a CSV file using Luigi, the Python library for building data","og_url":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/","og_site_name":"Web Code Geeks","article_publisher":"https:\/\/www.facebook.com\/webcodegeeks","article_published_time":"2017-03-29T09:15:14+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","type":"image\/jpeg"}],"author":"Mark Needham","twitter_card":"summary_large_image","twitter_creator":"@webcodegeeks","twitter_site":"@webcodegeeks","twitter_misc":{"Written by":"Mark Needham","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#article","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/"},"author":{"name":"Mark Needham","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/848a54e2ee724e46069ce36c2e52e98e"},"headline":"Luigi: Defining dynamic requirements (on output files)","datePublished":"2017-03-29T09:15:14+00:00","mainEntityOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/"},"wordCount":473,"commentCount":0,"publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","keywords":["Luigi"],"articleSection":["Python"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/","url":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/","name":"Luigi: Defining dynamic requirements (on output files) - Web Code Geeks - 2026","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#primaryimage"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","datePublished":"2017-03-29T09:15:14+00:00","description":"In my last blog post I showed how to convert a JSON document containing meetup groups into a CSV file using Luigi, the Python library for building data","breadcrumb":{"@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#primaryimage","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/11\/python-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.webcodegeeks.com\/python\/luigi-defining-dynamic-requirements-output-files\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.webcodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Python","item":"https:\/\/www.webcodegeeks.com\/category\/python\/"},{"@type":"ListItem","position":3,"name":"Luigi: Defining dynamic requirements (on output files)"}]},{"@type":"WebSite","@id":"https:\/\/www.webcodegeeks.com\/#website","url":"https:\/\/www.webcodegeeks.com\/","name":"Web Code Geeks","description":"Web Developers Resource Center","publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.webcodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.webcodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.webcodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/webcodegeeks","https:\/\/x.com\/webcodegeeks"]},{"@type":"Person","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/848a54e2ee724e46069ce36c2e52e98e","name":"Mark Needham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/5489baed26ce2d932bf951ecfb47afe80bec45d3648c23521d87c83b8f1c3ea9?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/5489baed26ce2d932bf951ecfb47afe80bec45d3648c23521d87c83b8f1c3ea9?s=96&d=mm&r=g","caption":"Mark Needham"},"sameAs":["http:\/\/www.markhneedham.com\/blog\/"],"url":"https:\/\/www.webcodegeeks.com\/author\/mark-needham\/"}]}},"_links":{"self":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/16736","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/users\/48"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/comments?post=16736"}],"version-history":[{"count":0,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/16736\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media\/1651"}],"wp:attachment":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media?parent=16736"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/categories?post=16736"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/tags?post=16736"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}