<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.tessercat.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=DRW</id>
	<title>WPRDC Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.tessercat.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=DRW"/>
	<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/wiki/Special:Contributions/DRW"/>
	<updated>2026-04-08T01:07:50Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.36.2</generator>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14400</id>
		<title>Data Guides</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14400"/>
		<updated>2024-02-01T14:44:17Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* WPRDC Data Guides */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Links to detailed document about data that includes social contextual information.&lt;br /&gt;
&lt;br /&gt;
== WPRDC Data Guides ==&lt;br /&gt;
[[Data Across Sectors for Health (DASH) Data Guide]] - A guide for [https://data.wprdc.org/dataset/?q=dash&amp;amp;sort=score+desc%2C+metadata_modified+desc&amp;amp;tags=ACHD+DASH these 29 health-related datasets].&lt;br /&gt;
&lt;br /&gt;
[[Guide to Property and Housing Data]]&lt;br /&gt;
&lt;br /&gt;
[[Allegheny County Property Assessment Data User Guide]] | ([https://data.wprdc.org/dataset/2b3df818-601e-4f06-b150-643557229491/resource/cc4bafd2-25b6-41d7-83aa-d16bc211b020/download/allegheny-county-property-assessment-data-user-guide.pdf PDF version])&lt;br /&gt;
&lt;br /&gt;
[[Crime, Courts, and Corrections in the City of Pittsburgh]]&lt;br /&gt;
&lt;br /&gt;
[[City of Pittsburgh 311 Data User Guide]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14399</id>
		<title>Data Guides</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14399"/>
		<updated>2024-02-01T14:43:25Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* WPRDC Data Guides */ Clarify nature of DASH data guide&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Links to detailed document about data that includes social contextual information.&lt;br /&gt;
&lt;br /&gt;
== WPRDC Data Guides ==&lt;br /&gt;
[[Data Across Sectors for Health (DASH) Data Guide]] - A guide for [these 29 health-related datasets](https://data.wprdc.org/dataset/?q=dash&amp;amp;sort=score+desc%2C+metadata_modified+desc&amp;amp;tags=ACHD+DASH).&lt;br /&gt;
&lt;br /&gt;
[[Guide to Property and Housing Data]]&lt;br /&gt;
&lt;br /&gt;
[[Allegheny County Property Assessment Data User Guide]] | ([https://data.wprdc.org/dataset/2b3df818-601e-4f06-b150-643557229491/resource/cc4bafd2-25b6-41d7-83aa-d16bc211b020/download/allegheny-county-property-assessment-data-user-guide.pdf PDF version])&lt;br /&gt;
&lt;br /&gt;
[[Crime, Courts, and Corrections in the City of Pittsburgh]]&lt;br /&gt;
&lt;br /&gt;
[[City of Pittsburgh 311 Data User Guide]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14398</id>
		<title>Data Guides</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14398"/>
		<updated>2024-02-01T14:40:56Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* WPRDC Data Guides */ Reorder guides&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Links to detailed document about data that includes social contextual information.&lt;br /&gt;
&lt;br /&gt;
== WPRDC Data Guides ==&lt;br /&gt;
[[DASH Data Guide]]&lt;br /&gt;
&lt;br /&gt;
[[Guide to Property and Housing Data]]&lt;br /&gt;
&lt;br /&gt;
[[Allegheny County Property Assessment Data User Guide]] | ([https://data.wprdc.org/dataset/2b3df818-601e-4f06-b150-643557229491/resource/cc4bafd2-25b6-41d7-83aa-d16bc211b020/download/allegheny-county-property-assessment-data-user-guide.pdf PDF version])&lt;br /&gt;
&lt;br /&gt;
[[Crime, Courts, and Corrections in the City of Pittsburgh]]&lt;br /&gt;
&lt;br /&gt;
[[City of Pittsburgh 311 Data User Guide]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14397</id>
		<title>Data Guides</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14397"/>
		<updated>2024-02-01T14:36:29Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* WPRDC Data Guides */ Indicate that one link is just a PDF version of another data guide&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Links to detailed document about data that includes social contextual information.&lt;br /&gt;
&lt;br /&gt;
== WPRDC Data Guides ==&lt;br /&gt;
[[DASH Data Guide]]&lt;br /&gt;
&lt;br /&gt;
[[Guide to Property and Housing Data]]&lt;br /&gt;
&lt;br /&gt;
[[Crime, Courts, and Corrections in the City of Pittsburgh]]&lt;br /&gt;
&lt;br /&gt;
[[City of Pittsburgh 311 Data User Guide]]&lt;br /&gt;
&lt;br /&gt;
[[Allegheny County Property Assessment Data User Guide]] | ([https://data.wprdc.org/dataset/2b3df818-601e-4f06-b150-643557229491/resource/cc4bafd2-25b6-41d7-83aa-d16bc211b020/download/allegheny-county-property-assessment-data-user-guide.pdf PDF version])&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Main_Page&amp;diff=14396</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Main_Page&amp;diff=14396"/>
		<updated>2024-01-24T14:40:01Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* Learning Resources */ Add Tutorials link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;strong&amp;gt;Welcome to the [https://www.wprdc.org WPRDC] wiki!&amp;lt;/strong&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Learning Resources ==&lt;br /&gt;
&lt;br /&gt;
=== Topics ===&lt;br /&gt;
[[Topic:GIS|Mapping/GIS]]&lt;br /&gt;
&lt;br /&gt;
=== 👋 Getting started with open data ===&lt;br /&gt;
[[Data Guides]]&lt;br /&gt;
&lt;br /&gt;
=== 📕Grimoires ===&lt;br /&gt;
[[List of Scripts|Scripts]]&lt;br /&gt;
&lt;br /&gt;
[[Useful Parcel Queries]]&lt;br /&gt;
&lt;br /&gt;
[[Working with certificates]]&lt;br /&gt;
&lt;br /&gt;
=== Tutorials ===&lt;br /&gt;
[[Tutorials|Tutorials on using data, SQL, and computers]]&lt;br /&gt;
&lt;br /&gt;
== About the WPRDC ==&lt;br /&gt;
In short, we'll put some sort of short description here.&lt;br /&gt;
&lt;br /&gt;
[[The WPRDC|(See the project's page for more details)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Maintainer Information ==&lt;br /&gt;
Consult the [[mediawikiwiki:Special:MyLanguage/Help:Contents|User's Guide]] for information on using the wiki software.&lt;br /&gt;
*[[mediawikiwiki:Special:MyLanguage/Manual:Configuration_settings|Configuration settings list]]&lt;br /&gt;
* [[mediawikiwiki:Special:MyLanguage/Manual:FAQ|MediaWiki FAQ]]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ MediaWiki release mailing list]&lt;br /&gt;
* [[mediawikiwiki:Special:MyLanguage/Localisation#Translation_resources|Localise MediaWiki for your language]]&lt;br /&gt;
* [[mediawikiwiki:Special:MyLanguage/Manual:Combating_spam|Learn how to combat spam on your wiki]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14395</id>
		<title>Tutorials</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14395"/>
		<updated>2024-01-24T14:38:27Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add a link to a tutorial and another to a discussion of tutorials&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* [https://kbroman.org/dataorg/ Organizing Data in Spreadsheets] - Tips on how to store data in a spreadsheet in a way that will make it easiest to sustainably work with. (Highly recommended if you ever want to publish your data.)&lt;br /&gt;
* [https://www.youtube.com/watch?v=7Ma8WIDinDc Data Cleaning Principles] - A video of a talk from csv,conf6 on how to approach and think about cleaning data. The slides, annotated with notes, are [https://kbroman.org/Talk_DataCleaning/data_cleaning_notes.pdf here] and the corresponding GitHub repo is [https://github.com/kbroman/dataorg here].&lt;br /&gt;
* [https://www.youtube.com/watch?v=Ssso_5X1UPs Creating Effective Figures and Tables] - A video of a talk about how to communicate data clearly with graphs and tables. [https://www.biostat.wisc.edu/~kbroman/presentations/graphs2018.pdf A PDF of the slides] and [https://github.com/kbroman/Talk_Graphs the GitHub repo] are also available.&lt;br /&gt;
&lt;br /&gt;
* [https://missing.csail.mit.edu/ The Missing Semester] - A series of MIT class videos teaching a lot of practical things that developers need to know.&lt;br /&gt;
* [https://mptc.io/ Modern Plain Text Computing] - A short seminar taught at Duke, aimed at students and researchers, covering how to think about and use computers, file systems, the shell, text editors, version control, and build systems. The philosophy is that these is essential knowledge for doing lots of research and making that research reproducible.&lt;br /&gt;
** [https://mastodon.social/@kjhealy/111791054059558470 A Mastodon thread discussing similar classes and resources]&lt;br /&gt;
* [https://gitlab.com/slackermedia/bashcrawl bashcrawl] - A text adventure to teach shell skills.&lt;br /&gt;
* [https://mystery.knightlab.com/ SQL Murder Mystery] - &amp;quot;The SQL Murder Mystery is designed to be both a self-directed lesson to learn SQL concepts and commands and a fun game for experienced SQL users to solve an intriguing crime.&amp;quot;&lt;br /&gt;
   - &amp;quot;... If you really want to learn a lot about SQL, you may prefer a complete tutorial like [https://selectstarsql.com/ Select Star SQL].&amp;quot;&lt;br /&gt;
* [https://jsvine.github.io/intro-to-visidata/index.html An Introduction to VisiData] - One way to learn the best power tool for exploring and analyzing CSV files, SQLite databases, Excel files, and many other file formats. VisiData is also handy for editing, manipulating, and joining CSV files. Click [https://www.visidata.org/install/ here] to install it.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14394</id>
		<title>CKAN Administration</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14394"/>
		<updated>2024-01-04T17:07:20Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add URL for fetching CKAN version and installed extensions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;More information on deploying/administering CKAN (like adding/removing extensions and editing the theme), may be found in [https://github.com/WPRDC/ckan-docker/wiki the wiki of the GitHub repo for our fork of the CKAN Docker repo].&lt;br /&gt;
&lt;br /&gt;
== Checking CKAN configuration ==&lt;br /&gt;
&lt;br /&gt;
* Check CKAN version and installed extensions: [https://data.wprdc.org/api/3/action/status_show https://data.wprdc.org/api/3/action/status_show]&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the frontend ==&lt;br /&gt;
There's a lot of documentation on publishing data on our CKAN portal [https://github.com/WPRDC/data-guide/tree/master/docs here].&lt;br /&gt;
&lt;br /&gt;
A few samples (to eventually migrate over):&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/PublishingCKAN.md Our documentation for publishers on publishing data on the WPRDC]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/data_dictionaries.md How to create data dictionaries] ==&amp;gt; [[Data Dictionaries]]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/metadata_extras.md Some of our standard extra metadata fields]&lt;br /&gt;
&lt;br /&gt;
=== Canonical Views ===&lt;br /&gt;
If you want to set a map or data table to be on the dataset landing page, you create a corresponding &amp;quot;view&amp;quot; under one of the resources in the dataset and then click the &amp;quot;Canonical View&amp;quot; button for that view. The catch is that CKAN does not enforce that only one view may be canonical, so if multiple views have their &amp;quot;Canonical View&amp;quot; button depressed, one of them will be chosen by CKAN to be the displayed one, and you will have to unclick others in order to get the one you want to display on the dataset landing page.&lt;br /&gt;
&lt;br /&gt;
=== Writing dataset descriptions ===&lt;br /&gt;
The description field supports some limited markup, which appears to be a subset of Markdown.&lt;br /&gt;
&lt;br /&gt;
* Starting a line with a single pound sign (#) indicates that the line should be in bigger, title text, but two pound signs (##) do not give a different font size, as they do in standard Markdown.&lt;br /&gt;
&lt;br /&gt;
* Dashes can be used to denote elements in an unordered list (though I haven't been able to get nested lists to work).&lt;br /&gt;
&lt;br /&gt;
* Use backticks to indicate that a sans serif font should be use to represent code, like `this`.&lt;br /&gt;
&lt;br /&gt;
Images, links, bold and italic text all work.&lt;br /&gt;
&lt;br /&gt;
It seems like it's limited to the original [https://www.markdownguide.org/cheat-sheet/#basic-syntax](very basic specification of Markdown).&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the backend ==&lt;br /&gt;
=== Configuring the CKAN server ===&lt;br /&gt;
(The contents of this section were initially taken from the &amp;lt;code&amp;gt;ORIENTATION&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/home/ubuntu&amp;lt;/code&amp;gt; on the CKAN production server.)&lt;br /&gt;
&lt;br /&gt;
* The main CKAN config file is at &amp;lt;code&amp;gt;/etc/ckan/default/production.ini&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To monitor HTTP requests in real-time: &amp;lt;code&amp;gt;&amp;gt; tail -f /var/log/nginx/access.log&amp;lt;/code&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
* Service-worker activity (like the Express Loader uploading files to the datastore and background geocoding) can be found in: &amp;lt;code&amp;gt;/var/log/ckan-worker.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Edit templates here (changes to templates should show up when reloading the relevant web pages): &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/ckanext/wprdc/templates&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;templates/terms.html&amp;lt;/code&amp;gt; is the source for the pop-up version of the Terms of Use. There appears to be no template linked to the &amp;quot;Terms&amp;quot; hyperlink.&lt;br /&gt;
&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;templates/foo.html&amp;lt;/code&amp;gt; and then run &amp;lt;code&amp;gt;&amp;gt; sudo service supervisor restart&amp;lt;/code&amp;gt; and THEN load &amp;lt;code&amp;gt;data.wprdc.org/foo.html&amp;lt;/code&amp;gt; in your browser, and the page will be there.&lt;br /&gt;
&lt;br /&gt;
* Presumably &amp;lt;code&amp;gt;data.wprdc.org/foo/&amp;lt;/code&amp;gt; can be populated by creating a file at &amp;lt;code&amp;gt;templates/foo/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Managing the CKAN server ===&lt;br /&gt;
* To restart the Express Loader: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl restart ckan-worker:*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To edit the background worker configuration (including increasing the number of background workers), &lt;br /&gt;
*# Edit the config file: &amp;lt;code&amp;gt;&amp;gt; vi /etc/supervisor/conf.d/supervisor-ckan-worker.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Tell Supervisor to use the new configuration: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl reread&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Update the deployed configuration to start the desired number of workers: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Activate the virtual environment that lets you run &amp;lt;code&amp;gt;paster&amp;lt;/code&amp;gt; commands: &amp;lt;code&amp;gt;&amp;gt; . /usr/lib/ckan/default/bin/activate&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Managing the Docker containers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt; &amp;gt; cd docker-ckan&lt;br /&gt;
&lt;br /&gt;
&amp;gt; docker ps&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
should return a list of running containers, which should include the following container names: &amp;lt;code&amp;gt;datapusher-plus&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;solr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;redis&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Use&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; sudo docker-compose logs --tail=100 ckan&amp;lt;/code&amp;gt;&lt;br /&gt;
to show the last 100 lines of the log for the &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt; Docker instance.&lt;br /&gt;
&lt;br /&gt;
=== Adding/changing departments of publishers ===&lt;br /&gt;
To add or change the departments belonging to a particular publisher organization edit the &amp;lt;code&amp;gt;dataset_schema.json&amp;lt;/code&amp;gt; file: &amp;lt;code&amp;gt;&amp;gt; vi /usr/lib/ckan/default/src/ckanext-scheming/ckanext/scheming/dataset_schema.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then run &amp;lt;code&amp;gt;&amp;gt; sudo service apache2 reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The extra tricky part about this one is that [https://github.com/WPRDC/ckanext-wprdctheme our GitHub repository that includes this JSON file] is installed in a different directory: &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/&amp;lt;/code&amp;gt; but changes to the files in that directory (and subdirectories) do nothing.&lt;br /&gt;
&lt;br /&gt;
=== Dealing with inadequate disk space ===&lt;br /&gt;
Once, we were seeing OSError: write error in the CKAN Docker logs and had to increase disk space to make CKAN function again.&lt;br /&gt;
&lt;br /&gt;
Steve on increasing volume size on AWS:&lt;br /&gt;
&amp;gt; if you ever have to increase a volume, here’s what i followed to make the filesystem use the new space: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html&lt;br /&gt;
that server was a &amp;lt;code&amp;gt;Xen&amp;lt;/code&amp;gt; instance&lt;br /&gt;
&lt;br /&gt;
== Other changes ==&lt;br /&gt;
=== Using CKAN metadata instead of local caches ===&lt;br /&gt;
To avoid keeping local databases about datasets (for instance, when writing code to track some aspect of datasets), store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are cataloged on the [[CKAN Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
=== Hacky workaround for adding new users to publishers ===&lt;br /&gt;
In additional to adding the users to the organizations through the CKAN front-end, you also have to add them to groups, using this URL: [http://wprdc.org/group-adder/]&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]] [[Category:CKAN]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14393</id>
		<title>CKAN Administration</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14393"/>
		<updated>2023-11-21T18:27:30Z</updated>

		<summary type="html">&lt;p&gt;DRW: Fix markup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;More information on deploying/administering CKAN (like adding/removing extensions and editing the theme), may be found in [https://github.com/WPRDC/ckan-docker/wiki the wiki of the GitHub repo for our fork of the CKAN Docker repo].&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the frontend ==&lt;br /&gt;
There's a lot of documentation on publishing data on our CKAN portal [https://github.com/WPRDC/data-guide/tree/master/docs here].&lt;br /&gt;
&lt;br /&gt;
A few samples (to eventually migrate over):&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/PublishingCKAN.md Our documentation for publishers on publishing data on the WPRDC]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/data_dictionaries.md How to create data dictionaries] ==&amp;gt; [[Data Dictionaries]]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/metadata_extras.md Some of our standard extra metadata fields]&lt;br /&gt;
&lt;br /&gt;
=== Canonical Views ===&lt;br /&gt;
If you want to set a map or data table to be on the dataset landing page, you create a corresponding &amp;quot;view&amp;quot; under one of the resources in the dataset and then click the &amp;quot;Canonical View&amp;quot; button for that view. The catch is that CKAN does not enforce that only one view may be canonical, so if multiple views have their &amp;quot;Canonical View&amp;quot; button depressed, one of them will be chosen by CKAN to be the displayed one, and you will have to unclick others in order to get the one you want to display on the dataset landing page.&lt;br /&gt;
&lt;br /&gt;
=== Writing dataset descriptions ===&lt;br /&gt;
The description field supports some limited markup, which appears to be a subset of Markdown.&lt;br /&gt;
&lt;br /&gt;
* Starting a line with a single pound sign (#) indicates that the line should be in bigger, title text, but two pound signs (##) do not give a different font size, as they do in standard Markdown.&lt;br /&gt;
&lt;br /&gt;
* Dashes can be used to denote elements in an unordered list (though I haven't been able to get nested lists to work).&lt;br /&gt;
&lt;br /&gt;
* Use backticks to indicate that a sans serif font should be use to represent code, like `this`.&lt;br /&gt;
&lt;br /&gt;
Images, links, bold and italic text all work.&lt;br /&gt;
&lt;br /&gt;
It seems like it's limited to the original [https://www.markdownguide.org/cheat-sheet/#basic-syntax](very basic specification of Markdown).&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the backend ==&lt;br /&gt;
=== Configuring the CKAN server ===&lt;br /&gt;
(The contents of this section were initially taken from the &amp;lt;code&amp;gt;ORIENTATION&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/home/ubuntu&amp;lt;/code&amp;gt; on the CKAN production server.)&lt;br /&gt;
&lt;br /&gt;
* The main CKAN config file is at &amp;lt;code&amp;gt;/etc/ckan/default/production.ini&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To monitor HTTP requests in real-time: &amp;lt;code&amp;gt;&amp;gt; tail -f /var/log/nginx/access.log&amp;lt;/code&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
* Service-worker activity (like the Express Loader uploading files to the datastore and background geocoding) can be found in: &amp;lt;code&amp;gt;/var/log/ckan-worker.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Edit templates here (changes to templates should show up when reloading the relevant web pages): &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/ckanext/wprdc/templates&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;templates/terms.html&amp;lt;/code&amp;gt; is the source for the pop-up version of the Terms of Use. There appears to be no template linked to the &amp;quot;Terms&amp;quot; hyperlink.&lt;br /&gt;
&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;templates/foo.html&amp;lt;/code&amp;gt; and then run &amp;lt;code&amp;gt;&amp;gt; sudo service supervisor restart&amp;lt;/code&amp;gt; and THEN load &amp;lt;code&amp;gt;data.wprdc.org/foo.html&amp;lt;/code&amp;gt; in your browser, and the page will be there.&lt;br /&gt;
&lt;br /&gt;
* Presumably &amp;lt;code&amp;gt;data.wprdc.org/foo/&amp;lt;/code&amp;gt; can be populated by creating a file at &amp;lt;code&amp;gt;templates/foo/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Managing the CKAN server ===&lt;br /&gt;
* To restart the Express Loader: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl restart ckan-worker:*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To edit the background worker configuration (including increasing the number of background workers), &lt;br /&gt;
*# Edit the config file: &amp;lt;code&amp;gt;&amp;gt; vi /etc/supervisor/conf.d/supervisor-ckan-worker.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Tell Supervisor to use the new configuration: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl reread&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Update the deployed configuration to start the desired number of workers: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Activate the virtual environment that lets you run &amp;lt;code&amp;gt;paster&amp;lt;/code&amp;gt; commands: &amp;lt;code&amp;gt;&amp;gt; . /usr/lib/ckan/default/bin/activate&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Managing the Docker containers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt; &amp;gt; cd docker-ckan&lt;br /&gt;
&lt;br /&gt;
&amp;gt; docker ps&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
should return a list of running containers, which should include the following container names: &amp;lt;code&amp;gt;datapusher-plus&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;solr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;redis&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Use&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; sudo docker-compose logs --tail=100 ckan&amp;lt;/code&amp;gt;&lt;br /&gt;
to show the last 100 lines of the log for the &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt; Docker instance.&lt;br /&gt;
&lt;br /&gt;
=== Adding/changing departments of publishers ===&lt;br /&gt;
To add or change the departments belonging to a particular publisher organization edit the &amp;lt;code&amp;gt;dataset_schema.json&amp;lt;/code&amp;gt; file: &amp;lt;code&amp;gt;&amp;gt; vi /usr/lib/ckan/default/src/ckanext-scheming/ckanext/scheming/dataset_schema.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then run &amp;lt;code&amp;gt;&amp;gt; sudo service apache2 reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The extra tricky part about this one is that [https://github.com/WPRDC/ckanext-wprdctheme our GitHub repository that includes this JSON file] is installed in a different directory: &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/&amp;lt;/code&amp;gt; but changes to the files in that directory (and subdirectories) do nothing.&lt;br /&gt;
&lt;br /&gt;
=== Dealing with inadequate disk space ===&lt;br /&gt;
Once, we were seeing OSError: write error in the CKAN Docker logs and had to increase disk space to make CKAN function again.&lt;br /&gt;
&lt;br /&gt;
Steve on increasing volume size on AWS:&lt;br /&gt;
&amp;gt; if you ever have to increase a volume, here’s what i followed to make the filesystem use the new space: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html&lt;br /&gt;
that server was a &amp;lt;code&amp;gt;Xen&amp;lt;/code&amp;gt; instance&lt;br /&gt;
&lt;br /&gt;
== Other changes ==&lt;br /&gt;
=== Using CKAN metadata instead of local caches ===&lt;br /&gt;
To avoid keeping local databases about datasets (for instance, when writing code to track some aspect of datasets), store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are cataloged on the [[CKAN Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
=== Hacky workaround for adding new users to publishers ===&lt;br /&gt;
In additional to adding the users to the organizations through the CKAN front-end, you also have to add them to groups, using this URL: [http://wprdc.org/group-adder/]&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]] [[Category:CKAN]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14392</id>
		<title>CKAN Administration</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14392"/>
		<updated>2023-11-21T18:26:55Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add link to WPRDC wiki on CKAN Docker deployment/administration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;More information on deploying/administering CKAN (like adding/removing extensions and editing the theme), may be found in [https://github.com/WPRDC/ckan-docker/wiki](the wiki of the GitHub repo for our fork of the CKAN Docker repo).&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the frontend ==&lt;br /&gt;
There's a lot of documentation on publishing data on our CKAN portal [https://github.com/WPRDC/data-guide/tree/master/docs here].&lt;br /&gt;
&lt;br /&gt;
A few samples (to eventually migrate over):&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/PublishingCKAN.md Our documentation for publishers on publishing data on the WPRDC]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/data_dictionaries.md How to create data dictionaries] ==&amp;gt; [[Data Dictionaries]]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/metadata_extras.md Some of our standard extra metadata fields]&lt;br /&gt;
&lt;br /&gt;
=== Canonical Views ===&lt;br /&gt;
If you want to set a map or data table to be on the dataset landing page, you create a corresponding &amp;quot;view&amp;quot; under one of the resources in the dataset and then click the &amp;quot;Canonical View&amp;quot; button for that view. The catch is that CKAN does not enforce that only one view may be canonical, so if multiple views have their &amp;quot;Canonical View&amp;quot; button depressed, one of them will be chosen by CKAN to be the displayed one, and you will have to unclick others in order to get the one you want to display on the dataset landing page.&lt;br /&gt;
&lt;br /&gt;
=== Writing dataset descriptions ===&lt;br /&gt;
The description field supports some limited markup, which appears to be a subset of Markdown.&lt;br /&gt;
&lt;br /&gt;
* Starting a line with a single pound sign (#) indicates that the line should be in bigger, title text, but two pound signs (##) do not give a different font size, as they do in standard Markdown.&lt;br /&gt;
&lt;br /&gt;
* Dashes can be used to denote elements in an unordered list (though I haven't been able to get nested lists to work).&lt;br /&gt;
&lt;br /&gt;
* Use backticks to indicate that a sans serif font should be use to represent code, like `this`.&lt;br /&gt;
&lt;br /&gt;
Images, links, bold and italic text all work.&lt;br /&gt;
&lt;br /&gt;
It seems like it's limited to the original [https://www.markdownguide.org/cheat-sheet/#basic-syntax](very basic specification of Markdown).&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the backend ==&lt;br /&gt;
=== Configuring the CKAN server ===&lt;br /&gt;
(The contents of this section were initially taken from the &amp;lt;code&amp;gt;ORIENTATION&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/home/ubuntu&amp;lt;/code&amp;gt; on the CKAN production server.)&lt;br /&gt;
&lt;br /&gt;
* The main CKAN config file is at &amp;lt;code&amp;gt;/etc/ckan/default/production.ini&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To monitor HTTP requests in real-time: &amp;lt;code&amp;gt;&amp;gt; tail -f /var/log/nginx/access.log&amp;lt;/code&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
* Service-worker activity (like the Express Loader uploading files to the datastore and background geocoding) can be found in: &amp;lt;code&amp;gt;/var/log/ckan-worker.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Edit templates here (changes to templates should show up when reloading the relevant web pages): &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/ckanext/wprdc/templates&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;templates/terms.html&amp;lt;/code&amp;gt; is the source for the pop-up version of the Terms of Use. There appears to be no template linked to the &amp;quot;Terms&amp;quot; hyperlink.&lt;br /&gt;
&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;templates/foo.html&amp;lt;/code&amp;gt; and then run &amp;lt;code&amp;gt;&amp;gt; sudo service supervisor restart&amp;lt;/code&amp;gt; and THEN load &amp;lt;code&amp;gt;data.wprdc.org/foo.html&amp;lt;/code&amp;gt; in your browser, and the page will be there.&lt;br /&gt;
&lt;br /&gt;
* Presumably &amp;lt;code&amp;gt;data.wprdc.org/foo/&amp;lt;/code&amp;gt; can be populated by creating a file at &amp;lt;code&amp;gt;templates/foo/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Managing the CKAN server ===&lt;br /&gt;
* To restart the Express Loader: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl restart ckan-worker:*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To edit the background worker configuration (including increasing the number of background workers), &lt;br /&gt;
*# Edit the config file: &amp;lt;code&amp;gt;&amp;gt; vi /etc/supervisor/conf.d/supervisor-ckan-worker.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Tell Supervisor to use the new configuration: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl reread&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Update the deployed configuration to start the desired number of workers: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Activate the virtual environment that lets you run &amp;lt;code&amp;gt;paster&amp;lt;/code&amp;gt; commands: &amp;lt;code&amp;gt;&amp;gt; . /usr/lib/ckan/default/bin/activate&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Managing the Docker containers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt; &amp;gt; cd docker-ckan&lt;br /&gt;
&lt;br /&gt;
&amp;gt; docker ps&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
should return a list of running containers, which should include the following container names: &amp;lt;code&amp;gt;datapusher-plus&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;solr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;redis&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Use&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; sudo docker-compose logs --tail=100 ckan&amp;lt;/code&amp;gt;&lt;br /&gt;
to show the last 100 lines of the log for the &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt; Docker instance.&lt;br /&gt;
&lt;br /&gt;
=== Adding/changing departments of publishers ===&lt;br /&gt;
To add or change the departments belonging to a particular publisher organization edit the &amp;lt;code&amp;gt;dataset_schema.json&amp;lt;/code&amp;gt; file: &amp;lt;code&amp;gt;&amp;gt; vi /usr/lib/ckan/default/src/ckanext-scheming/ckanext/scheming/dataset_schema.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then run &amp;lt;code&amp;gt;&amp;gt; sudo service apache2 reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The extra tricky part about this one is that [https://github.com/WPRDC/ckanext-wprdctheme our GitHub repository that includes this JSON file] is installed in a different directory: &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/&amp;lt;/code&amp;gt; but changes to the files in that directory (and subdirectories) do nothing.&lt;br /&gt;
&lt;br /&gt;
=== Dealing with inadequate disk space ===&lt;br /&gt;
Once, we were seeing OSError: write error in the CKAN Docker logs and had to increase disk space to make CKAN function again.&lt;br /&gt;
&lt;br /&gt;
Steve on increasing volume size on AWS:&lt;br /&gt;
&amp;gt; if you ever have to increase a volume, here’s what i followed to make the filesystem use the new space: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html&lt;br /&gt;
that server was a &amp;lt;code&amp;gt;Xen&amp;lt;/code&amp;gt; instance&lt;br /&gt;
&lt;br /&gt;
== Other changes ==&lt;br /&gt;
=== Using CKAN metadata instead of local caches ===&lt;br /&gt;
To avoid keeping local databases about datasets (for instance, when writing code to track some aspect of datasets), store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are cataloged on the [[CKAN Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
=== Hacky workaround for adding new users to publishers ===&lt;br /&gt;
In additional to adding the users to the organizations through the CKAN front-end, you also have to add them to groups, using this URL: [http://wprdc.org/group-adder/]&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]] [[Category:CKAN]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Metadata&amp;diff=14391</id>
		<title>CKAN Metadata</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Metadata&amp;diff=14391"/>
		<updated>2023-11-21T18:22:58Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add &amp;quot;CKAN&amp;quot; as an additional category for this page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Metadata overview ==&lt;br /&gt;
Metadata is a structured framework for documenting data. Some people like to say it's data about data. It's essential if anyone hopes to find and use your data.&lt;br /&gt;
&lt;br /&gt;
The metadata standard we're using is adapted from those used by the City of San Francisco and data.gov (the U.S. Federal government's open data repository). &lt;br /&gt;
&lt;br /&gt;
Each CKAN dataset has default metadata fields. Some of these are filled in automatically by CKAN when a dataset is created or updated, while others are set by the publisher when the dataset is created and can be updated by the publisher or WPRDC staff (or in some cases, programmatically, such as the updating of the Temporal Coverage metadata field by [https://github.com/WPRDC/pocket-watch our watchdog utility].&lt;br /&gt;
&lt;br /&gt;
== WPRDC custom metadata ==&lt;br /&gt;
After upgrading our data portal to CKAN 2.7, it became possible to easily create new metadata subfields within the 'extras' metadata field for any dataset. This can be done through API calls or through the CKAN web interface (by editing the dataset package).&lt;br /&gt;
&lt;br /&gt;
Below are partial lists of 'extras' metadata fields in use on https://data.wprdc.org:&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+ Caption text&lt;br /&gt;
|-&lt;br /&gt;
! field name !! Use !! Used by&lt;br /&gt;
|-&lt;br /&gt;
| last_etl_update || Indicates when the ETL job last finished. || rocket-etl&lt;br /&gt;
|-&lt;br /&gt;
| time_field || Dict specifying the field name in a table that stores each record's timestamp (used for determining dataset freshness). || pocket-watch&lt;br /&gt;
|-&lt;br /&gt;
| no_updates_on || List of days (e.g., &amp;quot;weekends&amp;quot;) coding for when a table is not expected to update. || pocket-watch&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]] [[Category:CKAN]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Tricks&amp;diff=14390</id>
		<title>CKAN Tricks</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Tricks&amp;diff=14390"/>
		<updated>2023-11-21T18:22:26Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add &amp;quot;CKAN&amp;quot; as an additional category for this page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Manage datasets ==&lt;br /&gt;
=== Undelete deleted datasets ===&lt;br /&gt;
If you know the URL of the deleted dataset AND you are logged in as an administrator, you can go to that URL in your web browser and see the deleted dataset. &amp;quot;[Deleted]&amp;quot; will have been appended to the title of the dataset, and the value of the metadata field &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt; will be equal to &amp;quot;deleted&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To undelete such a dataset, just use the CKAN API or use the &lt;br /&gt;
[https://github.com/WPRDC/utility-belt/blob/master/gadgets.py#L767 set_package_parameters_to_values() function] to set the package's &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt; metadata value to &amp;quot;active&amp;quot;. For the latter option, invoke the function like this:&lt;br /&gt;
&amp;lt;code&amp;gt;set_package_parameters_to_values(&amp;quot;https://data.wprdc.org&amp;quot;, deleted_package_id, ['state'], ['active'], API_key)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Queries ==&lt;br /&gt;
=== Make queries faster ===&lt;br /&gt;
[https://ckan.org/2017/08/10/faster-datastore-in-ckan-2-7/ Speed up datastore_search queries] by including the &amp;lt;code&amp;gt;include_total=False&amp;lt;/code&amp;gt; parameter to skip calculation of the total number of rows (which can reduce response time by a factor of 2).  The [https://docs.ckan.org/en/ckan-2.7.3/maintaining/datastore.html#ckanext.datastore.logic.action.datastore_search datastore_search API call] lets you search a given datastore by column values and return subsets of the records. There's more on benchmarking CKAN performance [http://urbanopus.net/benchmarking-the-ckan-datastore-api/ here].&lt;br /&gt;
&lt;br /&gt;
Another way to speed up datastore search queries is to index fields used in the filtering. Note that (at least when the primary key is a combination of fields), if you don't list each primary key field as a separate field to index, those fields don't get indexed and queries take way longer.&lt;br /&gt;
&lt;br /&gt;
=== Avoiding stale query caches ===&lt;br /&gt;
Queries/API responses can be cached based upon nginx settings. If you find that your SQL query is getting a stale response, try changing your query slightly. For instance, instead of `SELECT MAX(some_field) AS biggest FROM &amp;lt;resource-id&amp;gt;`, you could change the assigned variable name (`SELECT MAX(some_field) AS biggest0413 FROM &amp;lt;resource-id&amp;gt;`) or add another field that you ignore (`SELECT MAX(some_field) AS biggest, MAX(some_field) AS whatever FROM &amp;lt;resource-id&amp;gt;`).&lt;br /&gt;
&lt;br /&gt;
== Scripts that interact with CKAN through the API ==&lt;br /&gt;
=== Run those CKAN-monitoring/modifying scripts from multiple servers by centralizing data ===&lt;br /&gt;
To avoid keeping local databases about datasets, store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are currently cataloged on the [[CKAN_Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]] [[Category:CKAN]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14389</id>
		<title>CKAN Administration</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14389"/>
		<updated>2023-11-21T18:22:02Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add &amp;quot;CKAN&amp;quot; as an additional category for this page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Changes that can be made through the frontend ==&lt;br /&gt;
There's a lot of documentation on publishing data on our CKAN portal [https://github.com/WPRDC/data-guide/tree/master/docs here].&lt;br /&gt;
&lt;br /&gt;
A few samples (to eventually migrate over):&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/PublishingCKAN.md Our documentation for publishers on publishing data on the WPRDC]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/data_dictionaries.md How to create data dictionaries] ==&amp;gt; [[Data Dictionaries]]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/metadata_extras.md Some of our standard extra metadata fields]&lt;br /&gt;
&lt;br /&gt;
=== Canonical Views ===&lt;br /&gt;
If you want to set a map or data table to be on the dataset landing page, you create a corresponding &amp;quot;view&amp;quot; under one of the resources in the dataset and then click the &amp;quot;Canonical View&amp;quot; button for that view. The catch is that CKAN does not enforce that only one view may be canonical, so if multiple views have their &amp;quot;Canonical View&amp;quot; button depressed, one of them will be chosen by CKAN to be the displayed one, and you will have to unclick others in order to get the one you want to display on the dataset landing page.&lt;br /&gt;
&lt;br /&gt;
=== Writing dataset descriptions ===&lt;br /&gt;
The description field supports some limited markup, which appears to be a subset of Markdown.&lt;br /&gt;
&lt;br /&gt;
* Starting a line with a single pound sign (#) indicates that the line should be in bigger, title text, but two pound signs (##) do not give a different font size, as they do in standard Markdown.&lt;br /&gt;
&lt;br /&gt;
* Dashes can be used to denote elements in an unordered list (though I haven't been able to get nested lists to work).&lt;br /&gt;
&lt;br /&gt;
* Use backticks to indicate that a sans serif font should be use to represent code, like `this`.&lt;br /&gt;
&lt;br /&gt;
Images, links, bold and italic text all work.&lt;br /&gt;
&lt;br /&gt;
It seems like it's limited to the original [https://www.markdownguide.org/cheat-sheet/#basic-syntax](very basic specification of Markdown).&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the backend ==&lt;br /&gt;
=== Configuring the CKAN server ===&lt;br /&gt;
(The contents of this section were initially taken from the &amp;lt;code&amp;gt;ORIENTATION&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/home/ubuntu&amp;lt;/code&amp;gt; on the CKAN production server.)&lt;br /&gt;
&lt;br /&gt;
* The main CKAN config file is at &amp;lt;code&amp;gt;/etc/ckan/default/production.ini&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To monitor HTTP requests in real-time: &amp;lt;code&amp;gt;&amp;gt; tail -f /var/log/nginx/access.log&amp;lt;/code&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
* Service-worker activity (like the Express Loader uploading files to the datastore and background geocoding) can be found in: &amp;lt;code&amp;gt;/var/log/ckan-worker.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Edit templates here (changes to templates should show up when reloading the relevant web pages): &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/ckanext/wprdc/templates&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;templates/terms.html&amp;lt;/code&amp;gt; is the source for the pop-up version of the Terms of Use. There appears to be no template linked to the &amp;quot;Terms&amp;quot; hyperlink.&lt;br /&gt;
&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;templates/foo.html&amp;lt;/code&amp;gt; and then run &amp;lt;code&amp;gt;&amp;gt; sudo service supervisor restart&amp;lt;/code&amp;gt; and THEN load &amp;lt;code&amp;gt;data.wprdc.org/foo.html&amp;lt;/code&amp;gt; in your browser, and the page will be there.&lt;br /&gt;
&lt;br /&gt;
* Presumably &amp;lt;code&amp;gt;data.wprdc.org/foo/&amp;lt;/code&amp;gt; can be populated by creating a file at &amp;lt;code&amp;gt;templates/foo/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Managing the CKAN server ===&lt;br /&gt;
* To restart the Express Loader: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl restart ckan-worker:*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To edit the background worker configuration (including increasing the number of background workers), &lt;br /&gt;
*# Edit the config file: &amp;lt;code&amp;gt;&amp;gt; vi /etc/supervisor/conf.d/supervisor-ckan-worker.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Tell Supervisor to use the new configuration: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl reread&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Update the deployed configuration to start the desired number of workers: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Activate the virtual environment that lets you run &amp;lt;code&amp;gt;paster&amp;lt;/code&amp;gt; commands: &amp;lt;code&amp;gt;&amp;gt; . /usr/lib/ckan/default/bin/activate&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Managing the Docker containers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt; &amp;gt; cd docker-ckan&lt;br /&gt;
&lt;br /&gt;
&amp;gt; docker ps&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
should return a list of running containers, which should include the following container names: &amp;lt;code&amp;gt;datapusher-plus&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;solr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;redis&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Use&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; sudo docker-compose logs --tail=100 ckan&amp;lt;/code&amp;gt;&lt;br /&gt;
to show the last 100 lines of the log for the &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt; Docker instance.&lt;br /&gt;
&lt;br /&gt;
=== Adding/changing departments of publishers ===&lt;br /&gt;
To add or change the departments belonging to a particular publisher organization edit the &amp;lt;code&amp;gt;dataset_schema.json&amp;lt;/code&amp;gt; file: &amp;lt;code&amp;gt;&amp;gt; vi /usr/lib/ckan/default/src/ckanext-scheming/ckanext/scheming/dataset_schema.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then run &amp;lt;code&amp;gt;&amp;gt; sudo service apache2 reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The extra tricky part about this one is that [https://github.com/WPRDC/ckanext-wprdctheme our GitHub repository that includes this JSON file] is installed in a different directory: &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/&amp;lt;/code&amp;gt; but changes to the files in that directory (and subdirectories) do nothing.&lt;br /&gt;
&lt;br /&gt;
=== Dealing with inadequate disk space ===&lt;br /&gt;
Once, we were seeing OSError: write error in the CKAN Docker logs and had to increase disk space to make CKAN function again.&lt;br /&gt;
&lt;br /&gt;
Steve on increasing volume size on AWS:&lt;br /&gt;
&amp;gt; if you ever have to increase a volume, here’s what i followed to make the filesystem use the new space: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html&lt;br /&gt;
that server was a &amp;lt;code&amp;gt;Xen&amp;lt;/code&amp;gt; instance&lt;br /&gt;
&lt;br /&gt;
== Other changes ==&lt;br /&gt;
=== Using CKAN metadata instead of local caches ===&lt;br /&gt;
To avoid keeping local databases about datasets (for instance, when writing code to track some aspect of datasets), store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are cataloged on the [[CKAN Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
=== Hacky workaround for adding new users to publishers ===&lt;br /&gt;
In additional to adding the users to the organizations through the CKAN front-end, you also have to add them to groups, using this URL: [http://wprdc.org/group-adder/]&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]] [[Category:CKAN]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14388</id>
		<title>CKAN Administration</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14388"/>
		<updated>2023-09-05T16:40:09Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add tips on managing Docker containers and increasing disk space under AWS&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Changes that can be made through the frontend ==&lt;br /&gt;
There's a lot of documentation on publishing data on our CKAN portal [https://github.com/WPRDC/data-guide/tree/master/docs here].&lt;br /&gt;
&lt;br /&gt;
A few samples (to eventually migrate over):&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/PublishingCKAN.md Our documentation for publishers on publishing data on the WPRDC]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/data_dictionaries.md How to create data dictionaries] ==&amp;gt; [[Data Dictionaries]]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/metadata_extras.md Some of our standard extra metadata fields]&lt;br /&gt;
&lt;br /&gt;
=== Canonical Views ===&lt;br /&gt;
If you want to set a map or data table to be on the dataset landing page, you create a corresponding &amp;quot;view&amp;quot; under one of the resources in the dataset and then click the &amp;quot;Canonical View&amp;quot; button for that view. The catch is that CKAN does not enforce that only one view may be canonical, so if multiple views have their &amp;quot;Canonical View&amp;quot; button depressed, one of them will be chosen by CKAN to be the displayed one, and you will have to unclick others in order to get the one you want to display on the dataset landing page.&lt;br /&gt;
&lt;br /&gt;
=== Writing dataset descriptions ===&lt;br /&gt;
The description field supports some limited markup, which appears to be a subset of Markdown.&lt;br /&gt;
&lt;br /&gt;
* Starting a line with a single pound sign (#) indicates that the line should be in bigger, title text, but two pound signs (##) do not give a different font size, as they do in standard Markdown.&lt;br /&gt;
&lt;br /&gt;
* Dashes can be used to denote elements in an unordered list (though I haven't been able to get nested lists to work).&lt;br /&gt;
&lt;br /&gt;
* Use backticks to indicate that a sans serif font should be use to represent code, like `this`.&lt;br /&gt;
&lt;br /&gt;
Images, links, bold and italic text all work.&lt;br /&gt;
&lt;br /&gt;
It seems like it's limited to the original [https://www.markdownguide.org/cheat-sheet/#basic-syntax](very basic specification of Markdown).&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the backend ==&lt;br /&gt;
=== Configuring the CKAN server ===&lt;br /&gt;
(The contents of this section were initially taken from the &amp;lt;code&amp;gt;ORIENTATION&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/home/ubuntu&amp;lt;/code&amp;gt; on the CKAN production server.)&lt;br /&gt;
&lt;br /&gt;
* The main CKAN config file is at &amp;lt;code&amp;gt;/etc/ckan/default/production.ini&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To monitor HTTP requests in real-time: &amp;lt;code&amp;gt;&amp;gt; tail -f /var/log/nginx/access.log&amp;lt;/code&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
* Service-worker activity (like the Express Loader uploading files to the datastore and background geocoding) can be found in: &amp;lt;code&amp;gt;/var/log/ckan-worker.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Edit templates here (changes to templates should show up when reloading the relevant web pages): &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/ckanext/wprdc/templates&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;templates/terms.html&amp;lt;/code&amp;gt; is the source for the pop-up version of the Terms of Use. There appears to be no template linked to the &amp;quot;Terms&amp;quot; hyperlink.&lt;br /&gt;
&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;templates/foo.html&amp;lt;/code&amp;gt; and then run &amp;lt;code&amp;gt;&amp;gt; sudo service supervisor restart&amp;lt;/code&amp;gt; and THEN load &amp;lt;code&amp;gt;data.wprdc.org/foo.html&amp;lt;/code&amp;gt; in your browser, and the page will be there.&lt;br /&gt;
&lt;br /&gt;
* Presumably &amp;lt;code&amp;gt;data.wprdc.org/foo/&amp;lt;/code&amp;gt; can be populated by creating a file at &amp;lt;code&amp;gt;templates/foo/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Managing the CKAN server ===&lt;br /&gt;
* To restart the Express Loader: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl restart ckan-worker:*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To edit the background worker configuration (including increasing the number of background workers), &lt;br /&gt;
*# Edit the config file: &amp;lt;code&amp;gt;&amp;gt; vi /etc/supervisor/conf.d/supervisor-ckan-worker.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Tell Supervisor to use the new configuration: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl reread&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Update the deployed configuration to start the desired number of workers: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Activate the virtual environment that lets you run &amp;lt;code&amp;gt;paster&amp;lt;/code&amp;gt; commands: &amp;lt;code&amp;gt;&amp;gt; . /usr/lib/ckan/default/bin/activate&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Managing the Docker containers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt; &amp;gt; cd docker-ckan&lt;br /&gt;
&lt;br /&gt;
&amp;gt; docker ps&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
should return a list of running containers, which should include the following container names: &amp;lt;code&amp;gt;datapusher-plus&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;solr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;redis&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Use&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; sudo docker-compose logs --tail=100 ckan&amp;lt;/code&amp;gt;&lt;br /&gt;
to show the last 100 lines of the log for the &amp;lt;code&amp;gt;ckan&amp;lt;/code&amp;gt; Docker instance.&lt;br /&gt;
&lt;br /&gt;
=== Adding/changing departments of publishers ===&lt;br /&gt;
To add or change the departments belonging to a particular publisher organization edit the &amp;lt;code&amp;gt;dataset_schema.json&amp;lt;/code&amp;gt; file: &amp;lt;code&amp;gt;&amp;gt; vi /usr/lib/ckan/default/src/ckanext-scheming/ckanext/scheming/dataset_schema.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then run &amp;lt;code&amp;gt;&amp;gt; sudo service apache2 reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The extra tricky part about this one is that [https://github.com/WPRDC/ckanext-wprdctheme our GitHub repository that includes this JSON file] is installed in a different directory: &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/&amp;lt;/code&amp;gt; but changes to the files in that directory (and subdirectories) do nothing.&lt;br /&gt;
&lt;br /&gt;
=== Dealing with inadequate disk space ===&lt;br /&gt;
Once, we were seeing OSError: write error in the CKAN Docker logs and had to increase disk space to make CKAN function again.&lt;br /&gt;
&lt;br /&gt;
Steve on increasing volume size on AWS:&lt;br /&gt;
&amp;gt; if you ever have to increase a volume, here’s what i followed to make the filesystem use the new space: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html&lt;br /&gt;
that server was a &amp;lt;code&amp;gt;Xen&amp;lt;/code&amp;gt; instance&lt;br /&gt;
&lt;br /&gt;
== Other changes ==&lt;br /&gt;
=== Using CKAN metadata instead of local caches ===&lt;br /&gt;
To avoid keeping local databases about datasets (for instance, when writing code to track some aspect of datasets), store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are cataloged on the [[CKAN Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
=== Hacky workaround for adding new users to publishers ===&lt;br /&gt;
In additional to adding the users to the organizations through the CKAN front-end, you also have to add them to groups, using this URL: [http://wprdc.org/group-adder/]&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14387</id>
		<title>CKAN Administration</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14387"/>
		<updated>2023-09-05T16:36:59Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add tips on managing Docker containers and increasing disk space under AWS&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Changes that can be made through the frontend ==&lt;br /&gt;
There's a lot of documentation on publishing data on our CKAN portal [https://github.com/WPRDC/data-guide/tree/master/docs here].&lt;br /&gt;
&lt;br /&gt;
A few samples (to eventually migrate over):&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/PublishingCKAN.md Our documentation for publishers on publishing data on the WPRDC]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/data_dictionaries.md How to create data dictionaries] ==&amp;gt; [[Data Dictionaries]]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/metadata_extras.md Some of our standard extra metadata fields]&lt;br /&gt;
&lt;br /&gt;
=== Canonical Views ===&lt;br /&gt;
If you want to set a map or data table to be on the dataset landing page, you create a corresponding &amp;quot;view&amp;quot; under one of the resources in the dataset and then click the &amp;quot;Canonical View&amp;quot; button for that view. The catch is that CKAN does not enforce that only one view may be canonical, so if multiple views have their &amp;quot;Canonical View&amp;quot; button depressed, one of them will be chosen by CKAN to be the displayed one, and you will have to unclick others in order to get the one you want to display on the dataset landing page.&lt;br /&gt;
&lt;br /&gt;
=== Writing dataset descriptions ===&lt;br /&gt;
The description field supports some limited markup, which appears to be a subset of Markdown.&lt;br /&gt;
&lt;br /&gt;
* Starting a line with a single pound sign (#) indicates that the line should be in bigger, title text, but two pound signs (##) do not give a different font size, as they do in standard Markdown.&lt;br /&gt;
&lt;br /&gt;
* Dashes can be used to denote elements in an unordered list (though I haven't been able to get nested lists to work).&lt;br /&gt;
&lt;br /&gt;
* Use backticks to indicate that a sans serif font should be use to represent code, like `this`.&lt;br /&gt;
&lt;br /&gt;
Images, links, bold and italic text all work.&lt;br /&gt;
&lt;br /&gt;
It seems like it's limited to the original [https://www.markdownguide.org/cheat-sheet/#basic-syntax](very basic specification of Markdown).&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the backend ==&lt;br /&gt;
=== Configuring the CKAN server ===&lt;br /&gt;
(The contents of this section were initially taken from the &amp;lt;code&amp;gt;ORIENTATION&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/home/ubuntu&amp;lt;/code&amp;gt; on the CKAN production server.)&lt;br /&gt;
&lt;br /&gt;
* The main CKAN config file is at &amp;lt;code&amp;gt;/etc/ckan/default/production.ini&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To monitor HTTP requests in real-time: &amp;lt;code&amp;gt;&amp;gt; tail -f /var/log/nginx/access.log&amp;lt;/code&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
* Service-worker activity (like the Express Loader uploading files to the datastore and background geocoding) can be found in: &amp;lt;code&amp;gt;/var/log/ckan-worker.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Edit templates here (changes to templates should show up when reloading the relevant web pages): &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/ckanext/wprdc/templates&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;templates/terms.html&amp;lt;/code&amp;gt; is the source for the pop-up version of the Terms of Use. There appears to be no template linked to the &amp;quot;Terms&amp;quot; hyperlink.&lt;br /&gt;
&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;templates/foo.html&amp;lt;/code&amp;gt; and then run &amp;lt;code&amp;gt;&amp;gt; sudo service supervisor restart&amp;lt;/code&amp;gt; and THEN load &amp;lt;code&amp;gt;data.wprdc.org/foo.html&amp;lt;/code&amp;gt; in your browser, and the page will be there.&lt;br /&gt;
&lt;br /&gt;
* Presumably &amp;lt;code&amp;gt;data.wprdc.org/foo/&amp;lt;/code&amp;gt; can be populated by creating a file at &amp;lt;code&amp;gt;templates/foo/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Managing the CKAN server ===&lt;br /&gt;
* To restart the Express Loader: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl restart ckan-worker:*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To edit the background worker configuration (including increasing the number of background workers), &lt;br /&gt;
*# Edit the config file: &amp;lt;code&amp;gt;&amp;gt; vi /etc/supervisor/conf.d/supervisor-ckan-worker.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Tell Supervisor to use the new configuration: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl reread&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Update the deployed configuration to start the desired number of workers: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Activate the virtual environment that lets you run &amp;lt;code&amp;gt;paster&amp;lt;/code&amp;gt; commands: &amp;lt;code&amp;gt;&amp;gt; . /usr/lib/ckan/default/bin/activate&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Managing the Docker containers&lt;br /&gt;
&lt;br /&gt;
```&amp;gt; cd docker-ckan&lt;br /&gt;
&amp;gt; docker ps&lt;br /&gt;
```&lt;br /&gt;
should return a list of running containers, which should include the following container names: &amp;quot;datapusher-plus&amp;quot;, &amp;quot;ckan&amp;quot;, &amp;quot;solr&amp;quot;, and &amp;quot;redis&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Use&lt;br /&gt;
`&amp;gt; sudo docker-compose logs --tail=100 ckan`&lt;br /&gt;
to show the last 100 lines of the log for the `ckan` Docker instance.&lt;br /&gt;
&lt;br /&gt;
=== Adding/changing departments of publishers ===&lt;br /&gt;
To add or change the departments belonging to a particular publisher organization edit the &amp;lt;code&amp;gt;dataset_schema.json&amp;lt;/code&amp;gt; file: &amp;lt;code&amp;gt;&amp;gt; vi /usr/lib/ckan/default/src/ckanext-scheming/ckanext/scheming/dataset_schema.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then run &amp;lt;code&amp;gt;&amp;gt; sudo service apache2 reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The extra tricky part about this one is that [https://github.com/WPRDC/ckanext-wprdctheme our GitHub repository that includes this JSON file] is installed in a different directory: &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/&amp;lt;/code&amp;gt; but changes to the files in that directory (and subdirectories) do nothing.&lt;br /&gt;
&lt;br /&gt;
== Dealing with inadequate disk space ===&lt;br /&gt;
Once, we were seeing OSError: write error in the CKAN Docker logs and had to increase disk space to make CKAN function again.&lt;br /&gt;
&lt;br /&gt;
Steve on increasing volume size on AWS:&lt;br /&gt;
&amp;gt; if you ever have to increase a volume, here’s what i followed to make the filesystem use the new space: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/recognize-expanded-volume-linux.html&lt;br /&gt;
that server was a Xen instance&lt;br /&gt;
&lt;br /&gt;
== Other changes ==&lt;br /&gt;
=== Using CKAN metadata instead of local caches ===&lt;br /&gt;
To avoid keeping local databases about datasets (for instance, when writing code to track some aspect of datasets), store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are cataloged on the [[CKAN Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
=== Hacky workaround for adding new users to publishers ===&lt;br /&gt;
In additional to adding the users to the organizations through the CKAN front-end, you also have to add them to groups, using this URL: [http://wprdc.org/group-adder/]&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Crime,_Courts,_and_Corrections_in_the_City_of_Pittsburgh&amp;diff=14386</id>
		<title>Crime, Courts, and Corrections in the City of Pittsburgh</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Crime,_Courts,_and_Corrections_in_the_City_of_Pittsburgh&amp;diff=14386"/>
		<updated>2023-08-15T15:55:33Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* City of Pittsburgh Annual Police Statistical Reports */ Add newer reports and link to list of all reports&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This guide to Crime, Courts, and Corrections was created to make it easier to find, understand, and use information about public safety in the City of Pittsburgh, and the criminal justice and corrections systems in Allegheny County and the State of Pennsylvania. While public agencies are now sharing a growing amount of data, this information does not have value to most people without context or an understanding of underlying systems, laws, policies, and processes.&lt;br /&gt;
&lt;br /&gt;
Release of the data in this document allows the City to be a participant in the National Police Data Initiative. Founded in 2015, the Initiative is an outgrowth of President Obama’s Task Force on 21st Century Policing designed to reduce crime by building trust between citizens and police. Information sharing is viewed as a major trust-building step law enforcement organizations can take, and over 50 jurisdictions are now participating in the Initiative. For more information, please see the National Police Data Initiative’s FAQ.&lt;br /&gt;
&lt;br /&gt;
The Western Pennsylvania Regional Data Center is pleased to have worked with our City and County partners to publish much of the information referenced in this guide as open data. We’re also grateful to have had their help in assembling and editing this document. This guide also illustrates a critical partnership between the City’s Bureau of Police and the County Department of Human Services. Many of the tools featured in this guide were developed through this City-County collaboration.&lt;br /&gt;
&lt;br /&gt;
This guide was inspired by Crime and Punishment in Chicago, a Website developed by the Smart Chicago Collaborative.&lt;br /&gt;
&lt;br /&gt;
= Tools =&lt;br /&gt;
A number of tools have been developed to make it easier for people to use and understand the data contained in this guide, and a growing number of these tools are being developed by public sector staff at the City and County, the university community, journalists, and civic technologists. We’ve included links to some of them here. Please let us know if there are others that should be included in this list.&lt;br /&gt;
&lt;br /&gt;
=== Allegheny County Jail Population Management Dashboards ===&lt;br /&gt;
Allegheny County developed a series of [https://tableau.alleghenycounty.us/t/PublicSite/views/AC_JailPopulationManagement_Final/Home?iframeSizedToWindow=true&amp;amp;:embed=y&amp;amp;:showAppBanner=false&amp;amp;:display_count=no&amp;amp;:showVizHome=no publicly available, interactive dashboards] to allow users to explore different aspects of the Allegheny County Jail population. Dashboards provide information on the daily population, alternative housing, bookings, releases, length of stay, service usage, and involvement with the justice system prior to booking.&lt;br /&gt;
&lt;br /&gt;
=== City of Pittsburgh Annual Police Statistical Reports ===&lt;br /&gt;
&lt;br /&gt;
* [https://apps.pittsburghpa.gov/redtail/images/18173_2021_Annual_Report_Final.pdf 2021 Annual Report]&lt;br /&gt;
* [https://apps.pittsburghpa.gov/redtail/images/14012_FINAL_DRAFT_7_Annual_Report_2020.pdf 2020 Annual Report]&lt;br /&gt;
* [https://apps.pittsburghpa.gov/redtail/images/9640_2019_Annual_Report_Final.pdf 2019 Annual Report]&lt;br /&gt;
* [https://apps.pittsburghpa.gov/redtail/images/6371_2018_Annual_Report_Draft_-_Final.pdf 2018 Annual Report]&lt;br /&gt;
* [https://apps.pittsburghpa.gov/redtail/images/4801_2017_Annual_Report_Final_1.3.19.pdf 2017 Annual Report]&lt;br /&gt;
* [https://apps.pittsburghpa.gov/redtail/images/1236_2016_Annual_Report.pdf 2016 Annual Report]&lt;br /&gt;
* [http://apps.pittsburghpa.gov/pghbop/ANNUAL_REPORT_DRAFT_2015_May_31.pdf 2015 Annual Report]&lt;br /&gt;
* [http://apps.pittsburghpa.gov/pghbop/2014_Annual_Report_Final_draft.pdf 2014 Annual Report]&lt;br /&gt;
* [http://apps.pittsburghpa.gov/dps/2013_Annual_Report_draft_(final).pdf 2013 Annual Report]&lt;br /&gt;
* [http://apps.pittsburghpa.gov/pghbop/2012_Annual_Report_v2.pdf 2012 Annual Report]&lt;br /&gt;
&lt;br /&gt;
[https://pittsburghpa.gov/police/police-reports List of all Pittsburgh Annual Police Statistical Reports]&lt;br /&gt;
&lt;br /&gt;
=== Homicides ===&lt;br /&gt;
Homicide information in the [https://tableau.alleghenycounty.us/t/PublicSite/views/CJ_Homicides_PGH_8-22-17_v2/Home?%3Aembed=y&amp;amp;%3AshowAppBanner=false&amp;amp;%3AshowShareOptions=true&amp;amp;%3Adisplay_count=no&amp;amp;%3AshowVizHome=no City of Pittsburgh] and [https://tableau.alleghenycounty.us/t/PublicSite/views/Homicides_In_Allegheny_County/HomicideTrends?iframeSizedToWindow=true&amp;amp;%3Aembed=y&amp;amp;%3AshowAppBanner=false&amp;amp;%3Adisplay_count=no&amp;amp;%3AshowVizHome=no Allegheny County] are available through two separate dashboards. Data for the City dates back to 2010, with data from the County available as early as 2007. These tools include data on the number of homicides, clearance rates, weapons used, time of the incident, day of the week, demographics on victims and perpetrators, and benchmarks to other cities. They were developed through a collaboration of the Allegheny County Department of Human Services and the Allegheny County Office of the Medical Examiner.&lt;br /&gt;
&lt;br /&gt;
=== Gun Violence ===&lt;br /&gt;
Learn more about gun violence in your neighborhood on [https://tableau.alleghenycounty.us/t/PublicSite/views/CJ_GunViolence_PGH_8-22-17_v2/Home?:embed=y&amp;amp;:showAppBanner=false&amp;amp;:showShareOptions=true&amp;amp;:display_count=no&amp;amp;:showVizHome=no this dashboard developed by the Allegheny County Department of Human Services and the City of Pittsburgh]. This tool includes data on shootings, aggravated assaults involving a gun, and shots fired. The data, available back to 2010 also includes information on the time of day shootings occur.&lt;br /&gt;
&lt;br /&gt;
=== Overall Trends in Violence ===&lt;br /&gt;
[https://tableau.alleghenycounty.us/t/PublicSite/views/CJ_Overall_Violence_Trends_PGH_8-22-17_v2/Home?:embed=y&amp;amp;:showAppBanner=false&amp;amp;:showShareOptions=true&amp;amp;:display_count=no&amp;amp;:showVizHome=no This dashboard] contains information on trends in violence by neighborhood back to 2010. The tool contains information on homicides, shootings, assaults, and calls for shots fired, and also includes information on the number of incidents involving the use of a firearm. It was developed through a collaboration of the Allegheny County Department of Human Services and the City of Pittsburgh.&lt;br /&gt;
&lt;br /&gt;
=== Crime in Pittsburgh ===&lt;br /&gt;
The [https://tableau.alleghenycounty.us/t/PublicSite/views/CJ_UCR_PGH_8-22-17_v3/Home_1?iframeSizedToWindow=true&amp;amp;%3Aembed=y&amp;amp;%3AshowAppBanner=false&amp;amp;%3Adisplay_count=no&amp;amp;%3AshowVizHome=no&amp;amp;%3Aorigin=viz_share_link Crime Dashboard] provides information on the types of incidents occurring in your neighborhood. The tool developed through a collaboration of the Allegheny County Department of Human Services and the City of Pittsburgh also includes information on the number of crimes cleared, the time that incidents occur, and the victims back to 2005.&lt;br /&gt;
&lt;br /&gt;
=== Burgh's Eye View ===&lt;br /&gt;
With [https://pittsburghpa.shinyapps.io/BurghsEyeView/?_inputs_&amp;amp;dates=%5B%222017-02-14%22%2C%222017-02-24%22%5D&amp;amp;dept_select=null&amp;amp;filter_select=%22%22&amp;amp;GetScreenWidth=1920&amp;amp;hier=null&amp;amp;origin_select=null&amp;amp;report_ Burgh's Eye View] you can easily see all kinds of data about Pittsburgh—including 311 requests, building permits, code violations, and public safety incidents.But City data isn’t a log of when and where we put our park­ing chairs. It’s a huge collection of information about the world around us that can help us under­stand what’s happening in our neighborhoods, and lead us to ideas and decisions that can make where we live bet­ter.&lt;br /&gt;
&lt;br /&gt;
= Data =&lt;br /&gt;
This guide contains information on the types of data describing aspects of policing, criminal justice, and corrections system in the City of Pittsburgh, Allegheny County, and Pennsylvania. Each of the categories shown here in this section of the guide describe the types of data that are available, provide links to where it can be found, and describe some additional context to help people use and interpret the data accurately and responsibly.&lt;br /&gt;
&lt;br /&gt;
=== Victimization ===&lt;br /&gt;
[[Crime, Courts, and Corrections Guide:Victimization]]&lt;br /&gt;
&lt;br /&gt;
Data on the victims of reported homicides in Pittsburgh is available through an interactive data visualization developed through a partnership of the City of Pittsburgh and Allegheny County Department of Human Services.&lt;br /&gt;
&lt;br /&gt;
=== Incidents ===&lt;br /&gt;
[[Crime, Courts, and Corrections Guide:Incidents]]&lt;br /&gt;
&lt;br /&gt;
Crime incident reports are often created following a police investigation. Incident reports may also be generated from calls for service.&lt;br /&gt;
&lt;br /&gt;
'''''Police Incident Blotter data updates every day, and can be found on the Regional Data Center’s open data portal.'''''&lt;br /&gt;
&lt;br /&gt;
=== Prison ===&lt;br /&gt;
[[Crime, Courts, and Corrections Guide:Prison]]&lt;br /&gt;
&lt;br /&gt;
The Pennsylvania Department of Corrections manages 25 state correctional institutions and a number of other correctional facilities in Pennsylvania. Data on the monthly prison population by facility, and annual admissions and releases by county are available, along with information on inmates through the State’s Inmate locator tool.&lt;br /&gt;
&lt;br /&gt;
=== Non-Traffic Citations ===&lt;br /&gt;
[[Crime, Courts, and Corrections Guide:Non-Traffic Citations]]&lt;br /&gt;
&lt;br /&gt;
Non-Traffic Citations in Pittsburgh are given for minor criminal offenses, and are often called summary offenses. The types of offenses that often result in a citation include loitering, disorderly conduct, harassment, public drunkenness, and low-level retail theft.&lt;br /&gt;
&lt;br /&gt;
'''''Non-Traffic Citation data updates every day, and can be found on the Regional Data Center’s open data portal.'''''&lt;br /&gt;
&lt;br /&gt;
=== Jail ===&lt;br /&gt;
[[Crime, Courts, and Corrections Guide:Jail]]&lt;br /&gt;
&lt;br /&gt;
The population of the Allegheny County Jail changes on a daily basis as people are arrested, released on bail, exonerated, sentenced, transferred to another facility, paroled, or released.&lt;br /&gt;
&lt;br /&gt;
'''''Jail Census data updates every day, and can be found on the Regional Data Center’s open data portal.'''''&lt;br /&gt;
&lt;br /&gt;
=== Courts ===&lt;br /&gt;
[[Crime, Courts, and Corrections:Courts]] &lt;br /&gt;
&lt;br /&gt;
The Allegheny County Department of Court Records’ Criminal Division is the custodian of criminal records in Allegheny County. While the county manages this information, the data is owned by the Unified Judicial System of Pennsylvania.&lt;br /&gt;
&lt;br /&gt;
=== Call for Service ===&lt;br /&gt;
[[Crime, Courts, and Corrections:Call for Service]]&lt;br /&gt;
&lt;br /&gt;
In Allegheny County, data on emergency calls for service are managed by the Allegheny County Emergency Services. Non-emergency calls for service are made through the City of Pittsburgh’s 311 system managed by the City’s Department of Innovation and Performance.&lt;br /&gt;
&lt;br /&gt;
'''''Data for non-emergency 311 calls for service is updated hourly, and can be found on the Regional Data Center’s open data portal.'''''&lt;br /&gt;
&lt;br /&gt;
=== Arrests ===&lt;br /&gt;
[[Crime, Courts, and Corrections:Arrests]]&lt;br /&gt;
&lt;br /&gt;
Data on people taken into custody by City of Pittsburgh officers are available as open data. More serious crimes such as misdemeanors and felony offenses are more-likely to result in arrests, however arrests may occur for other reasons such as parole violations or failure to appear for trial.&lt;br /&gt;
&lt;br /&gt;
'''''Arrest data updates every day, and can be found on the Regional Data Center’s open data portal.'''''&lt;br /&gt;
&lt;br /&gt;
= Policies =&lt;br /&gt;
Policies and procedures govern the way officers interact with citizens, report incidents, and ensure accountability. Sharing policies and data on police-community interactions demonstrates the desire of City leaders to build trust with residents, and highlights their commitment to transparency.&lt;br /&gt;
&lt;br /&gt;
The City of Pittsburgh is making the following data available on the Regional Data Center’s open data portal:&lt;br /&gt;
&lt;br /&gt;
* [https://data.wprdc.org/dataset/police-community-outreach Police Community Outreach Events]&lt;br /&gt;
* [https://data.wprdc.org/dataset/officer-training Officer Training]&lt;br /&gt;
* [https://data.wprdc.org/dataset/police-civil-actions Police Civil Action Litigation]&lt;br /&gt;
* [http://apps.pittsburghpa.gov/dps/Use_of_Force_in_the_City_of_Pittsburgh.pdf Use of Force]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Crime,_Courts,_and_Corrections_Guide:Incidents&amp;diff=14385</id>
		<title>Crime, Courts, and Corrections Guide:Incidents</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Crime,_Courts,_and_Corrections_Guide:Incidents&amp;diff=14385"/>
		<updated>2023-08-15T15:26:17Z</updated>

		<summary type="html">&lt;p&gt;DRW: Fix typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''This guide is part of a larger guide on [[Crime, Courts, and Corrections in the City of Pittsburgh]].''&lt;br /&gt;
&lt;br /&gt;
Crime incident reports are often created following a police investigation. Incident reports may also be generated from calls for service.&lt;br /&gt;
&lt;br /&gt;
Incident data is published on a nightly basis by the City of Pittsburgh. The quality of incident level data may improve over time, and data published on the open data portal will reflect these changes. The data is presented in two separate files on the open data portal.&lt;br /&gt;
&lt;br /&gt;
* The Blotter will contain only the previous thirty days of reported crimes.The initial incident data can often change in the month following the initial incident report. Records older than thirty days will be deleted from this file and valid incidents will be moved to the archived dataset.Appropriate use of this file includes notifying community members of recent incidents.&lt;br /&gt;
* Once quality control and coding procedures have been run against the data by the Police Bureau, the data will then be published to the archived data file thirty days after the initial report. Data in the archived file will be of greater data quality and is the file most appropriate for reporting crime statistics. Data within this file is also subject to change. Archived data (2005-2015) is also being shared as part of this release.&lt;br /&gt;
&lt;br /&gt;
Pittsburgh Police do respond to incidents outside the City borders from time to time. For this reason, some incidents are mapped to locations outside the City. These can occur when City police assist police in other jurisdictions, when City police pursue a suspect across the City line to another municipality, and when a City officer happens to respond to an incident while outside the City.&lt;br /&gt;
&lt;br /&gt;
=== What's Included in the Data ===&lt;br /&gt;
&lt;br /&gt;
===== Publicly Available =====&lt;br /&gt;
&lt;br /&gt;
* Location generalized to a block (police zone for sex crimes)&lt;br /&gt;
* Type of incident&lt;br /&gt;
* Date&lt;br /&gt;
* Time&lt;br /&gt;
* Whether or not the incident has been cleared&lt;br /&gt;
&lt;br /&gt;
===== Not Publicly Available =====&lt;br /&gt;
&lt;br /&gt;
* Victim’s identity&lt;br /&gt;
* Actual incident location&lt;br /&gt;
&lt;br /&gt;
=== Where to Find the Data ===&lt;br /&gt;
&lt;br /&gt;
* Most-recent [https://data.wprdc.org/dataset/police-incident-blotter 30 days of incident data (The Blotter)] is published through the Western Pennsylvania Regional Data Center and is updated daily.&lt;br /&gt;
* [https://data.wprdc.org/dataset/uniform-crime-reporting-data Uniform Crime Reporting Data Recent Archive (Over 30 days old)] is published through the Western Pennsylvania Regional Data Center and is updated daily.&lt;br /&gt;
* [https://pittsburghpa.gov/publicsafety/index.html Pittsburgh Public Safety Press Releases] contain additional information on selected incidents&lt;br /&gt;
* This data is also featured on the City of Pittsburgh's [https://pittsburghpa.shinyapps.io/BurghsEyeView/ Burgh's Eye View] mapping tool&lt;br /&gt;
&lt;br /&gt;
=== Things to Know ===&lt;br /&gt;
&lt;br /&gt;
* Sex crimes will only be reported at the police zone level to protect victim confidentiality. All other crimes will be reported at the block level (based on street address range).&lt;br /&gt;
* Incident data is published using the UCR hierarchical classification system developed by the FBI. Multiple crimes may be included in the same incident, and incidents are coded by the highest-level offense.&lt;br /&gt;
* Unfounded incidents will be removed from the database. As the status or classification of an incident changes, these changes will be reflected on the open data portal. When using data, it is a good practice to cite when it was accessed.&lt;br /&gt;
* Incidents solely reported by other police departments operating in the City (campus police, Port Authority, etc) are not captured in this data.&lt;br /&gt;
* Archived data (2005-15) may first be published without coordinates, but coordinates will be made available as additional geocoding processes are run on the data.&lt;br /&gt;
* Not all incidents are reported. Incident reporting rates may vary from one community or person to the next.&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14384</id>
		<title>Data Guides</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Data_Guides&amp;diff=14384"/>
		<updated>2023-08-10T16:33:47Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* WPRDC Data Guides */ Add link to Property Assessments Data Guide&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Links to detailed document about data that includes social contextual information.&lt;br /&gt;
&lt;br /&gt;
== WPRDC Data Guides ==&lt;br /&gt;
[[DASH Data Guide]]&lt;br /&gt;
&lt;br /&gt;
[[Guide to Property and Housing Data]]&lt;br /&gt;
&lt;br /&gt;
[[Crime, Courts, and Corrections in the City of Pittsburgh]]&lt;br /&gt;
&lt;br /&gt;
[[City of Pittsburgh 311 Data User Guide]]&lt;br /&gt;
&lt;br /&gt;
[[Allegheny County Property Assessment Data User Guide]]&lt;br /&gt;
&lt;br /&gt;
[https://data.wprdc.org/dataset/2b3df818-601e-4f06-b150-643557229491/resource/cc4bafd2-25b6-41d7-83aa-d16bc211b020/download/allegheny-county-property-assessment-data-user-guide.pdf Property Assessments Data User Guide (pdf)]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Tooling&amp;diff=14383</id>
		<title>Tooling</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Tooling&amp;diff=14383"/>
		<updated>2023-07-20T19:07:11Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* Data tools */  Add &amp;quot;Modern CSV&amp;quot; and tips for safely editing CSVs in Excel&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Data tools ==&lt;br /&gt;
* [https://www.visidata.org/ VisiData] - Terminal user interface for a data exploration/manipulation tool that can handle large datasets.&lt;br /&gt;
** If you REALLY don't want to use VisiData because you don't want to use the terminal, [https://www.moderncsv.com/ Modern CSV] looks like a decent CSV editor that can handle large files and won't mangle your CSV file (unlike Excel).&lt;br /&gt;
*** If, in a pinch, you absolutely have to use Excel to edit a CSV file (which you really shouldn't), 1) make a copy of your CSV file, saving it as a text file (with the &amp;quot;txt&amp;quot; extension), 2) open it in Excel using the text import function, and 3) make all of the fields text fields. 4) Edit the file in Excel. 5) When finished &amp;quot;Save as...&amp;quot; a CSV file.&lt;br /&gt;
* [https://github.com/jqnatividad/qsv qsv] - &amp;quot;command line program for indexing, slicing, analyzing, splitting, enriching, validating &amp;amp; joining CSV files. Commands are simple, fast and composable.&amp;quot; Forked from xsv by CKAN Joel, so it's got some CKAN-specific features in the works.&lt;br /&gt;
&lt;br /&gt;
=== Anonymization ===&lt;br /&gt;
* [https://www.open-diffix.org/ Open Diffix] - Free, open-source desktop tool (and eventually Postgres extension) for anonymizing data.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14382</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14382"/>
		<updated>2023-06-03T21:27:57Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add &amp;quot;Inventorying ETL jobs&amp;quot; section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. This principle also applies to lists of geographic regions and other features. c) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
''After writing this section, I discovered that some of the ideas above also appear in the Urban Institute's &lt;br /&gt;
[https://www.urban.org/sites/default/files/publication/104296/do-no-harm-guide.pdf Do No Harm Guide: Applying Equity Awareness in Data Visualization]. While it is focussed on visualizations, some of its suggestions can be applied to designing data schemas (and especially if your data table includes representations of race/ethnicity). The General Recommendations on page 41 provide a good overview.''&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schema-source comparison ===&lt;br /&gt;
&lt;br /&gt;
When running ETL jobs, you will sometimes see console output indicating that 1) fields in the source file are not being used in the schema or 2) fields in the schema cannot be found in the source file. These are checks to ensure that the schema matches and accounts for all the fields in the source file. (While marshmallow does have some support for some operations, we've written our own code for handling these comparisons.)&lt;br /&gt;
&lt;br /&gt;
If there's a field in the source file that you don't want to publish (and you don't want to keep getting console output about it), you can either list it in the &amp;lt;code&amp;gt;exclude&amp;lt;/code&amp;gt; option, in the schema's &amp;lt;code&amp;gt;Meta&amp;lt;/code&amp;gt; class, or you can add the field to the schema, but set it to &amp;lt;code&amp;gt;load_only=True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there's a field in the schema that is supposed to be published (&amp;quot;dumped&amp;quot; in Marshmallow jargon) and is supposed to be loaded from the source file, but it can't be found in the source file, an error message will be printed to the console. If additionally the job is trying to push data to the CKAN datastore, an exception will be raised.&lt;br /&gt;
&lt;br /&gt;
These checks are really helpful when writing/testing/modifying an ETL job, as they make it easy to find typos in field names or other errors that are preventing source data from getting to the output correctly.&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
&lt;br /&gt;
== Retiring ETL jobs ==&lt;br /&gt;
Every ETL job has a life cycle. When an ETL job comes to the end of its existence because the data source has disappeared, we designate this dataset &amp;quot;orphaned&amp;quot;. Metadata values for the update frequency fields should be changed to the value that has &amp;quot;Historical&amp;quot; in its description. The dataset's &amp;quot;_etl&amp;quot; tag should be removed and replaced with the &amp;quot;_orphaned_etl&amp;quot; tag. If there is still a manual inventory of ETL jobs, that inventory should be updated.&lt;br /&gt;
&lt;br /&gt;
== Inventorying ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
When you add, delete, or modify ETL jobs, update [https://docs.google.com/spreadsheets/d/1amPsZ1RA9d9m41MXkMmMB8-VDmwAOxU72A35DlSWbT4/edit#gid=0 this Google Sheet]. (Eventually, we'll make ETL jobs self-tracking somehow, probably by adding the location of the script (and any other useful metadata) to the package &amp;lt;code&amp;gt;extras&amp;lt;/code&amp;gt; metadata field.)&lt;br /&gt;
&lt;br /&gt;
Currently ETL jobs are reporting as metadata things like last_etl_update date and the source file hash. Even the last source file name is being used in the new Data Rivers/Google Cloud Platform to check whether a given file is newer or older than the last one used. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14381</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14381"/>
		<updated>2023-06-03T20:31:41Z</updated>

		<summary type="html">&lt;p&gt;DRW: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. This principle also applies to lists of geographic regions and other features. c) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
''After writing this section, I discovered that some of the ideas above also appear in the Urban Institute's &lt;br /&gt;
[https://www.urban.org/sites/default/files/publication/104296/do-no-harm-guide.pdf Do No Harm Guide: Applying Equity Awareness in Data Visualization]. While it is focussed on visualizations, some of its suggestions can be applied to designing data schemas (and especially if your data table includes representations of race/ethnicity). The General Recommendations on page 41 provide a good overview.''&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schema-source comparison ===&lt;br /&gt;
&lt;br /&gt;
When running ETL jobs, you will sometimes see console output indicating that 1) fields in the source file are not being used in the schema or 2) fields in the schema cannot be found in the source file. These are checks to ensure that the schema matches and accounts for all the fields in the source file. (While marshmallow does have some support for some operations, we've written our own code for handling these comparisons.)&lt;br /&gt;
&lt;br /&gt;
If there's a field in the source file that you don't want to publish (and you don't want to keep getting console output about it), you can either list it in the &amp;lt;code&amp;gt;exclude&amp;lt;/code&amp;gt; option, in the schema's &amp;lt;code&amp;gt;Meta&amp;lt;/code&amp;gt; class, or you can add the field to the schema, but set it to &amp;lt;code&amp;gt;load_only=True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there's a field in the schema that is supposed to be published (&amp;quot;dumped&amp;quot; in Marshmallow jargon) and is supposed to be loaded from the source file, but it can't be found in the source file, an error message will be printed to the console. If additionally the job is trying to push data to the CKAN datastore, an exception will be raised.&lt;br /&gt;
&lt;br /&gt;
These checks are really helpful when writing/testing/modifying an ETL job, as they make it easy to find typos in field names or other errors that are preventing source data from getting to the output correctly.&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
&lt;br /&gt;
== Retiring ETL jobs ==&lt;br /&gt;
Every ETL job has a life cycle. When an ETL job comes to the end of its existence because the data source has disappeared, we designate this dataset &amp;quot;orphaned&amp;quot;. Metadata values for the update frequency fields should be changed to the value that has &amp;quot;Historical&amp;quot; in its description. The dataset's &amp;quot;_etl&amp;quot; tag should be removed and replaced with the &amp;quot;_orphaned_etl&amp;quot; tag. If there is still a manual inventory of ETL jobs, that inventory should be updated.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14380</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14380"/>
		<updated>2023-06-02T17:19:28Z</updated>

		<summary type="html">&lt;p&gt;DRW: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. This principle also applies to lists of geographic regions and other features. c) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
''After writing this section, I discovered that some of the ideas above also appear in the Urban Institute's &lt;br /&gt;
[https://www.urban.org/sites/default/files/publication/104296/do-no-harm-guide.pdf Do No Harm Guide: Applying Equity Awareness in Data Visualization]. While it is focussed on visualizations, some of its suggestions can be applied to designing data schemas (and especially if your data table includes representations of race/ethnicity). The General Recommendations on page 41 provide a good overview.''&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schema-source comparison ===&lt;br /&gt;
&lt;br /&gt;
When running ETL jobs, you will sometimes see console output indicating that 1) fields in the source file are not being used in the schema or 2) fields in the schema cannot be found in the source file. These are checks to ensure that the schema matches and accounts for all the fields in the source file. (While marshmallow does have some support for some operations, we've written our own code for handling these comparisons.)&lt;br /&gt;
&lt;br /&gt;
If there's a field in the source file that you don't want to publish (and you don't want to keep getting console output about it), you can either list it in the &amp;lt;code&amp;gt;exclude&amp;lt;/code&amp;gt; option, in the schema's &amp;lt;code&amp;gt;Meta&amp;lt;/code&amp;gt; class, or you can add the field to the schema, but set it to &amp;lt;code&amp;gt;load_only=True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there's a field in the schema that is supposed to be published (&amp;quot;dumped&amp;quot; in Marshmallow jargon) and is supposed to be loaded from the source file, but it can't be found in the source file, an error message will be printed to the console. If additionally the job is trying to push data to the CKAN datastore, an exception will be raised.&lt;br /&gt;
&lt;br /&gt;
These checks are really helpful when writing/testing/modifying an ETL job, as they make it easy to find typos in field names or other errors that are preventing source data from getting to the output correctly.&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14379</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14379"/>
		<updated>2023-06-02T17:17:56Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* Testing ETL jobs */  Add schema-source comparison section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. This principle also applies to lists of geographic regions and other features. c) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
''After writing this section, I discovered that some of the ideas above also appear in the Urban Institute's &lt;br /&gt;
[https://www.urban.org/sites/default/files/publication/104296/do-no-harm-guide.pdf Do No Harm Guide: Applying Equity Awareness in Data Visualization]. While it is focussed on visualizations, some of its suggestions can be applied to designing data schemas (and especially if your data table includes representations of race/ethnicity). The General Recommendations on page 41 provide a good overview.''&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schema-source comparison ===&lt;br /&gt;
&lt;br /&gt;
When running ETL jobs, you will sometimes see console output indicating that 1) fields in the source file are not being used in the schema or 2) fields in the schema cannot be found in the source file. These are checks to ensure that the schema matches and accounts for all the fields in the source file. (While marshmallow does have some support for some operations, we've written our own code for handling these comparisons.)&lt;br /&gt;
&lt;br /&gt;
If there's a field in the source file that you don't want to publish (and you don't want to keep getting console output about it), you can either list it in the &amp;lt;code&amp;gt;exclude&amp;lt;/code&amp;gt; option, in the schema's &amp;lt;code&amp;gt;Meta&amp;lt;/code&amp;gt; class, or you can add the field to the schema, but set it to &amp;lt;code&amp;gt;load_only=True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If there's a field in the schema that is supposed to be published (&amp;quot;dumped&amp;quot; in Marshmallow jargon) and is supposed to be loaded from the source file, but it can't be found in the source file, an error message will be printed to the console. If additionally the job is trying to push data to the CKAN datastore, an exception will be raised.&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14378</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14378"/>
		<updated>2023-06-02T16:43:50Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* Schema design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. This principle also applies to lists of geographic regions and other features. c) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
''After writing this section, I discovered that some of the ideas above also appear in the Urban Institute's &lt;br /&gt;
[https://www.urban.org/sites/default/files/publication/104296/do-no-harm-guide.pdf Do No Harm Guide: Applying Equity Awareness in Data Visualization]. While it is focussed on visualizations, some of its suggestions can be applied to designing data schemas (and especially if your data table includes representations of race/ethnicity). The General Recommendations on page 41 provide a good overview.''&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14377</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14377"/>
		<updated>2023-06-02T16:40:42Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* Schema design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. This principle also applies to lists of geographic regions and other features. c) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
''After writing this section, I discovered that some of the ideas above also appear in the Urban Institute's &lt;br /&gt;
[https://www.urban.org/sites/default/files/publication/104296/do-no-harm-guide.pdf Do No Harm Guide: Applying Equity Awareness in Data Visualization]. While it is focussed on visualizations, some of its suggestions can be applied to designing data schemas, particularly those on page 41 (General Recommendations).''&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14376</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14376"/>
		<updated>2023-06-02T16:35:10Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* Schema design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. This principle also applies to lists of geographic regions and other features. c) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
''After writing this section, I discovered that some of the ideas above also appear in the Urban Institute's &lt;br /&gt;
[https://www.urban.org/sites/default/files/publication/104296/do-no-harm-guide.pdf Do No Harm Guide: Applying Equity Awareness in Data Visualization]. While it is focussed on visualizations, some of its suggestions can be applied to designing data schemas.''&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14375</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14375"/>
		<updated>2023-06-01T21:17:04Z</updated>

		<summary type="html">&lt;p&gt;DRW: Reorder two statement in an ordered list.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. This principle also applies to lists of geographic regions and other features. c) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14374</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14374"/>
		<updated>2023-06-01T20:58:59Z</updated>

		<summary type="html">&lt;p&gt;DRW: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). c) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. This principle also applies to lists of geographic regions and other features. d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14373</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14373"/>
		<updated>2023-06-01T20:49:56Z</updated>

		<summary type="html">&lt;p&gt;DRW: Finish schema design section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;lt;code&amp;gt;FIELD NAME&amp;lt;/code&amp;gt; becomes &amp;lt;code&amp;gt;field_name&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;# of pirates&amp;lt;/code&amp;gt; should be changed to &amp;lt;code&amp;gt;number_of_pirates&amp;lt;/code&amp;gt;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates &amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;lat&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt;) but &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt; are already being used by other data tables, switch to &amp;lt;code&amp;gt;latitude&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;longitude&amp;lt;/code&amp;gt;.&lt;br /&gt;
# '''Standardize column values.''' Where possible transform columns to standardize their values. The first step is to look at the histogram of every column (&amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; in VisiData!) and see if anything is irregular. For instance, if the &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; column has 1038 records with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;Pittsburgh&amp;quot; and two with &amp;lt;code&amp;gt;municipality&amp;lt;/code&amp;gt; == &amp;quot;PGH&amp;quot;, add to the schema a @pre_dump decorator function to change all instances of &amp;quot;PGH&amp;quot; to &amp;quot;Pittsburgh&amp;quot;. In some cases, just converting an address field to upper case will go a long way toward standardizing it. You can think of this step as pre-cleaning the data. The Holy Grail of column standardization would be using the same values in every identically named column across the entire data portal. Maybe someday!&lt;br /&gt;
# '''Organize the column names.''' Often the source file comes with some record IDs on the left, followed by some highly relevant fields (e.g., names of things), but then the rest of the columns may be semirandomly ordered. Principles of column organization: a) '''The &amp;quot;input&amp;quot; should be on the left and the &amp;quot;output&amp;quot; should be on the right.''' Which fields is the user likeliest to use to look up a record (like you would look up a word in a dictionary)? Put those furthest to the left (or, at the top of the schema). Primary keys and unique identifiers should go on the far left. Things like the results of inspections are closer to outputs, and should be moved to the right. b) '''Prioritize important stuff'''. If there are fields you think are likely to be of most interest to the user, shift them as far left as you can (subject to other constraints). The further left the field is, the better chance the user will be able to see it in the Data Tables view (or their tabular data explorer of choice). c) '''Group similar fields together.''' Obviously street address, city, state, and ZIP code should be grouped together and presented in the canonical order. d) '''Maximize readability'''. Think like a user. How can you order the columns so that the sequence is logical?&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14372</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14372"/>
		<updated>2023-06-01T20:22:28Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add schema design section to ETL page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Schema design ===&lt;br /&gt;
&lt;br /&gt;
After running [https://github.com/WPRDC/little-lexicographer Little Lexicographer] on the source file you want to write an ETL job for and reviewing the proposed schema types for correctness, review the column names.&lt;br /&gt;
# '''Make column names clear.''' If you don't understand the meaning of the column from reading the column name and looking at sample values, figure out the column (by reading the data dictionary and documentation or asking someone closer to the source of the data) and then give it a meaningful name.&lt;br /&gt;
# '''Use snake case.''' Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;quot;FIELD NAME&amp;quot; becomes &amp;quot;field_name&amp;quot; and &amp;quot;# of pirates&amp;quot; should be changed to &amp;quot;number_of_pirates&amp;quot;). Reasons we prefer snake case: a) Marshmallow already converts field names to snake case to some extent automatically. b) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
# '''Standardize column names.''' Choose names that are already in use in other data tables published by the same publisher. For instance, if the source data calls the geocoordinates `y` and `x` (or `lat` and `long`) but `latitude` and `longitude` are already being used by other data tables, switch to `latitude` and `longitude`.&lt;br /&gt;
# '''Standardize column values.'''&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14371</id>
		<title>CKAN Administration</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14371"/>
		<updated>2023-03-20T18:40:27Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add Canonical Views section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Changes that can be made through the frontend ==&lt;br /&gt;
There's a lot of documentation on publishing data on our CKAN portal [https://github.com/WPRDC/data-guide/tree/master/docs here].&lt;br /&gt;
&lt;br /&gt;
A few samples (to eventually migrate over):&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/PublishingCKAN.md Our documentation for publishers on publishing data on the WPRDC]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/data_dictionaries.md How to create data dictionaries] ==&amp;gt; [[Data Dictionaries]]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/metadata_extras.md Some of our standard extra metadata fields]&lt;br /&gt;
&lt;br /&gt;
=== Canonical Views ===&lt;br /&gt;
If you want to set a map or data table to be on the dataset landing page, you create a corresponding &amp;quot;view&amp;quot; under one of the resources in the dataset and then click the &amp;quot;Canonical View&amp;quot; button for that view. The catch is that CKAN does not enforce that only one view may be canonical, so if multiple views have their &amp;quot;Canonical View&amp;quot; button depressed, one of them will be chosen by CKAN to be the displayed one, and you will have to unclick others in order to get the one you want to display on the dataset landing page.&lt;br /&gt;
&lt;br /&gt;
=== Writing dataset descriptions ===&lt;br /&gt;
The description field supports some limited markup, which appears to be a subset of Markdown.&lt;br /&gt;
&lt;br /&gt;
* Starting a line with a single pound sign (#) indicates that the line should be in bigger, title text, but two pound signs (##) do not give a different font size, as they do in standard Markdown.&lt;br /&gt;
&lt;br /&gt;
* Dashes can be used to denote elements in an unordered list (though I haven't been able to get nested lists to work).&lt;br /&gt;
&lt;br /&gt;
* Use backticks to indicate that a sans serif font should be use to represent code, like `this`.&lt;br /&gt;
&lt;br /&gt;
Images, links, bold and italic text all work.&lt;br /&gt;
&lt;br /&gt;
It seems like it's limited to the original [https://www.markdownguide.org/cheat-sheet/#basic-syntax](very basic specification of Markdown).&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the backend ==&lt;br /&gt;
=== Configuring the CKAN server ===&lt;br /&gt;
(The contents of this section were initially taken from the &amp;lt;code&amp;gt;ORIENTATION&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/home/ubuntu&amp;lt;/code&amp;gt; on the CKAN production server.)&lt;br /&gt;
&lt;br /&gt;
* The main CKAN config file is at &amp;lt;code&amp;gt;/etc/ckan/default/production.ini&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To monitor HTTP requests in real-time: &amp;lt;code&amp;gt;&amp;gt; tail -f /var/log/nginx/access.log&amp;lt;/code&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
* Service-worker activity (like the Express Loader uploading files to the datastore and background geocoding) can be found in: &amp;lt;code&amp;gt;/var/log/ckan-worker.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Edit templates here (changes to templates should show up when reloading the relevant web pages): &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/ckanext/wprdc/templates&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;templates/terms.html&amp;lt;/code&amp;gt; is the source for the pop-up version of the Terms of Use. There appears to be no template linked to the &amp;quot;Terms&amp;quot; hyperlink.&lt;br /&gt;
&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;templates/foo.html&amp;lt;/code&amp;gt; and then run &amp;lt;code&amp;gt;&amp;gt; sudo service supervisor restart&amp;lt;/code&amp;gt; and THEN load &amp;lt;code&amp;gt;data.wprdc.org/foo.html&amp;lt;/code&amp;gt; in your browser, and the page will be there.&lt;br /&gt;
&lt;br /&gt;
* Presumably &amp;lt;code&amp;gt;data.wprdc.org/foo/&amp;lt;/code&amp;gt; can be populated by creating a file at &amp;lt;code&amp;gt;templates/foo/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Managing the CKAN server ===&lt;br /&gt;
* To restart the Express Loader: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl restart ckan-worker:*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To edit the background worker configuration (including increasing the number of background workers), &lt;br /&gt;
*# Edit the config file: &amp;lt;code&amp;gt;&amp;gt; vi /etc/supervisor/conf.d/supervisor-ckan-worker.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Tell Supervisor to use the new configuration: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl reread&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Update the deployed configuration to start the desired number of workers: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Activate the virtual environment that lets you run &amp;lt;code&amp;gt;paster&amp;lt;/code&amp;gt; commands: &amp;lt;code&amp;gt;&amp;gt; . /usr/lib/ckan/default/bin/activate&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding/changing departments of publishers ===&lt;br /&gt;
To add or change the departments belonging to a particular publisher organization edit the &amp;lt;code&amp;gt;dataset_schema.json&amp;lt;/code&amp;gt; file: &amp;lt;code&amp;gt;&amp;gt; vi /usr/lib/ckan/default/src/ckanext-scheming/ckanext/scheming/dataset_schema.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then run &amp;lt;code&amp;gt;&amp;gt; sudo service apache2 reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The extra tricky part about this one is that [https://github.com/WPRDC/ckanext-wprdctheme our GitHub repository that includes this JSON file] is installed in a different directory: &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/&amp;lt;/code&amp;gt; but changes to the files in that directory (and subdirectories) do nothing.&lt;br /&gt;
&lt;br /&gt;
== Other changes ==&lt;br /&gt;
=== Using CKAN metadata instead of local caches ===&lt;br /&gt;
To avoid keeping local databases about datasets (for instance, when writing code to track some aspect of datasets), store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are cataloged on the [[CKAN Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
=== Hacky workaround for adding new users to publishers ===&lt;br /&gt;
In additional to adding the users to the organizations through the CKAN front-end, you also have to add them to groups, using this URL: [http://wprdc.org/group-adder/]&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Scraping_Data&amp;diff=14370</id>
		<title>Scraping Data</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Scraping_Data&amp;diff=14370"/>
		<updated>2023-02-15T18:37:40Z</updated>

		<summary type="html">&lt;p&gt;DRW: Fix markup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;So you want to scrape data from some web page on a regular basis, huh?&lt;br /&gt;
&lt;br /&gt;
Here are some ways you could do it:&lt;br /&gt;
&lt;br /&gt;
* A pure Google-Sheets-based approach, which can be set to run hourly, daily, or on other schedules: https://www.computerworld.com/article/3684733/how-to-create-automatically-updating-google-sheet.html&lt;br /&gt;
* A pure GitHub-based approach (exploiting GitHub actions): https://simonwillison.net/2020/Oct/9/git-scraping/&lt;br /&gt;
** [https://githubnext.com/projects/flat-data Flat Data] - An extensions of Simon Willison's git-scraping that includes a GUI for viewing the captured data (as long as it's stored in a public GitHub repo)&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Scraping_Data&amp;diff=14369</id>
		<title>Scraping Data</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Scraping_Data&amp;diff=14369"/>
		<updated>2023-02-15T18:34:40Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add Flat Data to Scraping Data page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;So you want to scrape data from some web page on a regular basis, huh?&lt;br /&gt;
&lt;br /&gt;
Here are some ways you could do it:&lt;br /&gt;
&lt;br /&gt;
* A pure Google-Sheets-based approach, which can be set to run hourly, daily, or on other schedules: https://www.computerworld.com/article/3684733/how-to-create-automatically-updating-google-sheet.html&lt;br /&gt;
&lt;br /&gt;
* A pure GitHub-based approach (exploiting GitHub actions): https://simonwillison.net/2020/Oct/9/git-scraping/&lt;br /&gt;
  * [https://githubnext.com/projects/flat-data Flat Data] - An extensions of Simon Willison's git-scraping that includes a GUI for viewing the captured data (as long as it's stored in a public GitHub repo)&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Scraping_Data&amp;diff=14368</id>
		<title>Scraping Data</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Scraping_Data&amp;diff=14368"/>
		<updated>2023-02-10T18:48:58Z</updated>

		<summary type="html">&lt;p&gt;DRW: Create &amp;quot;Scraping Data&amp;quot; page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;So you want to scrape data from some web page on a regular basis, huh?&lt;br /&gt;
&lt;br /&gt;
Here are some ways you could do it:&lt;br /&gt;
&lt;br /&gt;
A pure Google-Sheets-based approach, which can be set to run hourly, daily, or on other schedules:&lt;br /&gt;
https://www.computerworld.com/article/3684733/how-to-create-automatically-updating-google-sheet.html&lt;br /&gt;
&lt;br /&gt;
A pure GitHub-based approach (exploiting GitHub actions):&lt;br /&gt;
https://simonwillison.net/2020/Oct/9/git-scraping/&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14279</id>
		<title>CKAN Administration</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Administration&amp;diff=14279"/>
		<updated>2022-10-06T19:48:07Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add notes about CKAN's description support for Markdown&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Changes that can be made through the frontend ==&lt;br /&gt;
There's a lot of documentation on publishing data on our CKAN portal [https://github.com/WPRDC/data-guide/tree/master/docs here].&lt;br /&gt;
&lt;br /&gt;
A few samples (to eventually migrate over):&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/PublishingCKAN.md Our documentation for publishers on publishing data on the WPRDC]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/data_dictionaries.md How to create data dictionaries] ==&amp;gt; [[Data Dictionaries]]&lt;br /&gt;
* [https://github.com/WPRDC/data-guide/blob/master/docs/metadata_extras.md Some of our standard extra metadata fields]&lt;br /&gt;
&lt;br /&gt;
== Writing dataset descriptions ==&lt;br /&gt;
The description field supports some limited markup, which appears to be a subset of Markdown.&lt;br /&gt;
&lt;br /&gt;
* Starting a line with a single pound sign (#) indicates that the line should be in bigger, title text, but two pound signs (##) do not give a different font size, as they do in standard Markdown.&lt;br /&gt;
&lt;br /&gt;
* Dashes can be used to denote elements in an unordered list (though I haven't been able to get nested lists to work).&lt;br /&gt;
&lt;br /&gt;
* Use backticks to indicate that a sans serif font should be use to represent code, like `this`.&lt;br /&gt;
&lt;br /&gt;
Images, links, bold and italic text all work.&lt;br /&gt;
&lt;br /&gt;
It seems like it's limited to the original [https://www.markdownguide.org/cheat-sheet/#basic-syntax](very basic specification of Markdown).&lt;br /&gt;
&lt;br /&gt;
== Changes that can be made through the backend ==&lt;br /&gt;
=== Configuring the CKAN server ===&lt;br /&gt;
(The contents of this section were initially taken from the &amp;lt;code&amp;gt;ORIENTATION&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;/home/ubuntu&amp;lt;/code&amp;gt; on the CKAN production server.)&lt;br /&gt;
&lt;br /&gt;
* The main CKAN config file is at &amp;lt;code&amp;gt;/etc/ckan/default/production.ini&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To monitor HTTP requests in real-time: &amp;lt;code&amp;gt;&amp;gt; tail -f /var/log/nginx/access.log&amp;lt;/code&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
* Service-worker activity (like the Express Loader uploading files to the datastore and background geocoding) can be found in: &amp;lt;code&amp;gt;/var/log/ckan-worker.log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Edit templates here (changes to templates should show up when reloading the relevant web pages): &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/ckanext/wprdc/templates&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;templates/terms.html&amp;lt;/code&amp;gt; is the source for the pop-up version of the Terms of Use. There appears to be no template linked to the &amp;quot;Terms&amp;quot; hyperlink.&lt;br /&gt;
&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;templates/foo.html&amp;lt;/code&amp;gt; and then run &amp;lt;code&amp;gt;&amp;gt; sudo service supervisor restart&amp;lt;/code&amp;gt; and THEN load &amp;lt;code&amp;gt;data.wprdc.org/foo.html&amp;lt;/code&amp;gt; in your browser, and the page will be there.&lt;br /&gt;
&lt;br /&gt;
* Presumably &amp;lt;code&amp;gt;data.wprdc.org/foo/&amp;lt;/code&amp;gt; can be populated by creating a file at &amp;lt;code&amp;gt;templates/foo/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Managing the CKAN server ===&lt;br /&gt;
* To restart the Express Loader: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl restart ckan-worker:*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* To edit the background worker configuration (including increasing the number of background workers), &lt;br /&gt;
*# Edit the config file: &amp;lt;code&amp;gt;&amp;gt; vi /etc/supervisor/conf.d/supervisor-ckan-worker.conf&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Tell Supervisor to use the new configuration: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl reread&amp;lt;/code&amp;gt;&lt;br /&gt;
*# Update the deployed configuration to start the desired number of workers: &amp;lt;code&amp;gt;&amp;gt; sudo supervisorctl update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Activate the virtual environment that lets you run &amp;lt;code&amp;gt;paster&amp;lt;/code&amp;gt; commands: &amp;lt;code&amp;gt;&amp;gt; . /usr/lib/ckan/default/bin/activate&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding/changing departments of publishers ===&lt;br /&gt;
To add or change the departments belonging to a particular publisher organization edit the &amp;lt;code&amp;gt;dataset_schema.json&amp;lt;/code&amp;gt; file: &amp;lt;code&amp;gt;&amp;gt; vi /usr/lib/ckan/default/src/ckanext-scheming/ckanext/scheming/dataset_schema.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then run &amp;lt;code&amp;gt;&amp;gt; sudo service apache2 reload&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The extra tricky part about this one is that [https://github.com/WPRDC/ckanext-wprdctheme our GitHub repository that includes this JSON file] is installed in a different directory: &amp;lt;code&amp;gt;/usr/lib/ckan/default/src/ckanext-wprdctheme/&amp;lt;/code&amp;gt; but changes to the files in that directory (and subdirectories) do nothing.&lt;br /&gt;
&lt;br /&gt;
== Other changes ==&lt;br /&gt;
=== Using CKAN metadata instead of local caches ===&lt;br /&gt;
To avoid keeping local databases about datasets (for instance, when writing code to track some aspect of datasets), store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are cataloged on the [[CKAN Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
=== Hacky workaround for adding new users to publishers ===&lt;br /&gt;
In additional to adding the users to the organizations through the CKAN front-end, you also have to add them to groups, using this URL: [http://wprdc.org/group-adder/]&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14278</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14278"/>
		<updated>2022-07-11T14:11:08Z</updated>

		<summary type="html">&lt;p&gt;DRW: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Snake case ===&lt;br /&gt;
&lt;br /&gt;
Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;quot;FIELD NAME&amp;quot; becomes &amp;quot;field_name&amp;quot; and &amp;quot;# of pirates&amp;quot; should be changed to &amp;quot;number_of_pirates&amp;quot;). Reasons we prefer snake case: 1) Marshmallow already converts field names to snake case to some extent automatically. 2) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt; git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14277</id>
		<title>Tutorials</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14277"/>
		<updated>2022-06-06T02:47:43Z</updated>

		<summary type="html">&lt;p&gt;DRW: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* [https://kbroman.org/dataorg/ Organizing Data in Spreadsheets] - Tips on how to store data in a spreadsheet in a way that will make it easiest to sustainably work with. (Highly recommended if you ever want to publish your data.)&lt;br /&gt;
* [https://www.youtube.com/watch?v=7Ma8WIDinDc Data Cleaning Principles] - A video of a talk from csv,conf6 on how to approach and think about cleaning data. The slides, annotated with notes, are [https://kbroman.org/Talk_DataCleaning/data_cleaning_notes.pdf here] and the corresponding GitHub repo is [https://github.com/kbroman/dataorg here].&lt;br /&gt;
* [https://www.youtube.com/watch?v=Ssso_5X1UPs Creating Effective Figures and Tables] - A video of a talk about how to communicate data clearly with graphs and tables. [https://www.biostat.wisc.edu/~kbroman/presentations/graphs2018.pdf A PDF of the slides] and [https://github.com/kbroman/Talk_Graphs the GitHub repo] are also available.&lt;br /&gt;
&lt;br /&gt;
* [https://missing.csail.mit.edu/ The Missing Semester] - A series of MIT class videos teaching a lot of practical things that developers need to know.&lt;br /&gt;
* [https://gitlab.com/slackermedia/bashcrawl bashcrawl] - A text adventure to teach shell skills.&lt;br /&gt;
* [https://mystery.knightlab.com/ SQL Murder Mystery] - &amp;quot;The SQL Murder Mystery is designed to be both a self-directed lesson to learn SQL concepts and commands and a fun game for experienced SQL users to solve an intriguing crime.&amp;quot;&lt;br /&gt;
   - &amp;quot;... If you really want to learn a lot about SQL, you may prefer a complete tutorial like [https://selectstarsql.com/ Select Star SQL].&amp;quot;&lt;br /&gt;
* [https://jsvine.github.io/intro-to-visidata/index.html An Introduction to VisiData] - One way to learn the best power tool for exploring and analyzing CSV files, SQLite databases, Excel files, and many other file formats. VisiData is also handy for editing, manipulating, and joining CSV files. Click [https://www.visidata.org/install/ here] to install it.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14276</id>
		<title>Tutorials</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14276"/>
		<updated>2022-06-06T02:23:57Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add talk on creating plots and tables and add a link to the data-cleaning entry&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* [https://kbroman.org/dataorg/ Organizing Data in Spreadsheets] - Tips on how to store data in a spreadsheet in a way that will make it easiest to sustainably work with. &lt;br /&gt;
* [https://www.youtube.com/watch?v=7Ma8WIDinDc Data Cleaning Principles] - A video of a talk from csv,conf6 on how to approach and think about cleaning data. The slides, annotated with notes, are [https://kbroman.org/Talk_DataCleaning/data_cleaning_notes.pdf here] and the corresponding GitHub repo is [https://github.com/kbroman/dataorg here].&lt;br /&gt;
* [https://www.youtube.com/watch?v=Ssso_5X1UPs Creating Effective Figures and Tables] - A video of a talk about how to communicate data clearly with graphs and tables. [https://www.biostat.wisc.edu/~kbroman/presentations/graphs2018.pdf A PDF of the slides] and [https://github.com/kbroman/Talk_Graphs the GitHub repo] are also available.&lt;br /&gt;
&lt;br /&gt;
* [https://missing.csail.mit.edu/ The Missing Semester] - A series of MIT class videos teaching a lot of practical things that developers need to know.&lt;br /&gt;
* [https://gitlab.com/slackermedia/bashcrawl bashcrawl] - A text adventure to teach shell skills.&lt;br /&gt;
* [https://mystery.knightlab.com/ SQL Murder Mystery] - &amp;quot;The SQL Murder Mystery is designed to be both a self-directed lesson to learn SQL concepts and commands and a fun game for experienced SQL users to solve an intriguing crime.&amp;quot;&lt;br /&gt;
   - &amp;quot;... If you really want to learn a lot about SQL, you may prefer a complete tutorial like [https://selectstarsql.com/ Select Star SQL].&amp;quot;&lt;br /&gt;
* [https://jsvine.github.io/intro-to-visidata/index.html An Introduction to VisiData] - One way to learn the best power tool for exploring and analyzing CSV files, SQLite databases, Excel files, and many other file formats. VisiData is also handy for editing, manipulating, and joining CSV files. Click [https://www.visidata.org/install/ here] to install it.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14275</id>
		<title>Tutorials</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14275"/>
		<updated>2022-06-06T02:10:44Z</updated>

		<summary type="html">&lt;p&gt;DRW: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* [https://kbroman.org/dataorg/ Organizing Data in Spreadsheets] - Tips on how to store data in a spreadsheet in a way that will make it easiest to sustainably work with. &lt;br /&gt;
* [https://www.youtube.com/watch?v=7Ma8WIDinDc Data Cleaning Principles] - A video of a talk from csv,conf6 on how to approach and think about cleaning data. The slides, annotated with notes, are [https://kbroman.org/Talk_DataCleaning/data_cleaning_notes.pdf here].&lt;br /&gt;
&lt;br /&gt;
* [https://missing.csail.mit.edu/ The Missing Semester] - A series of MIT class videos teaching a lot of practical things that developers need to know.&lt;br /&gt;
* [https://gitlab.com/slackermedia/bashcrawl bashcrawl] - A text adventure to teach shell skills.&lt;br /&gt;
* [https://mystery.knightlab.com/ SQL Murder Mystery] - &amp;quot;The SQL Murder Mystery is designed to be both a self-directed lesson to learn SQL concepts and commands and a fun game for experienced SQL users to solve an intriguing crime.&amp;quot;&lt;br /&gt;
   - &amp;quot;... If you really want to learn a lot about SQL, you may prefer a complete tutorial like [https://selectstarsql.com/ Select Star SQL].&amp;quot;&lt;br /&gt;
* [https://jsvine.github.io/intro-to-visidata/index.html An Introduction to VisiData] - One way to learn the best power tool for exploring and analyzing CSV files, SQLite databases, Excel files, and many other file formats. VisiData is also handy for editing, manipulating, and joining CSV files. Click [https://www.visidata.org/install/ here] to install it.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14274</id>
		<title>Tutorials</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14274"/>
		<updated>2022-06-06T02:02:58Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add tutorials on data organization and data cleaning&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* [https://kbroman.org/dataorg/ Organizing Data in Spreadsheets] - Tips on how to store data in a spreadsheet in a way that will make it easiest to sustainably work with. &lt;br /&gt;
* [https://www.youtube.com/watch?v=7Ma8WIDinDc Data Cleaning Principles] - A video of a talk from csv,conf6 on how to approach and think about cleaning data. The slides are [https://kbroman.org/Talk_DataCleaning/data_cleaning.pdf here].&lt;br /&gt;
&lt;br /&gt;
* [https://missing.csail.mit.edu/ The Missing Semester] - A series of MIT class videos teaching a lot of practical things that developers need to know.&lt;br /&gt;
* [https://gitlab.com/slackermedia/bashcrawl bashcrawl] - A text adventure to teach shell skills.&lt;br /&gt;
* [https://mystery.knightlab.com/ SQL Murder Mystery] - &amp;quot;The SQL Murder Mystery is designed to be both a self-directed lesson to learn SQL concepts and commands and a fun game for experienced SQL users to solve an intriguing crime.&amp;quot;&lt;br /&gt;
   - &amp;quot;... If you really want to learn a lot about SQL, you may prefer a complete tutorial like [https://selectstarsql.com/ Select Star SQL].&amp;quot;&lt;br /&gt;
* [https://jsvine.github.io/intro-to-visidata/index.html An Introduction to VisiData] - One way to learn the best power tool for exploring and analyzing CSV files, SQLite databases, Excel files, and many other file formats. VisiData is also handy for editing, manipulating, and joining CSV files. Click [https://www.visidata.org/install/ here] to install it.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Programming_Tutorials&amp;diff=14273</id>
		<title>Programming Tutorials</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Programming_Tutorials&amp;diff=14273"/>
		<updated>2022-06-06T01:56:57Z</updated>

		<summary type="html">&lt;p&gt;DRW: DRW moved page Programming Tutorials to Tutorials: Generalizing from programming tutorials to just tutorials&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Tutorials]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14272</id>
		<title>Tutorials</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Tutorials&amp;diff=14272"/>
		<updated>2022-06-06T01:56:57Z</updated>

		<summary type="html">&lt;p&gt;DRW: DRW moved page Programming Tutorials to Tutorials: Generalizing from programming tutorials to just tutorials&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* [https://missing.csail.mit.edu/ The Missing Semester] - A series of MIT class videos teaching a lot of practical things that developers need to know.&lt;br /&gt;
* [https://gitlab.com/slackermedia/bashcrawl bashcrawl] - A text adventure to teach shell skills.&lt;br /&gt;
* [https://mystery.knightlab.com/ SQL Murder Mystery] - &amp;quot;The SQL Murder Mystery is designed to be both a self-directed lesson to learn SQL concepts and commands and a fun game for experienced SQL users to solve an intriguing crime.&amp;quot;&lt;br /&gt;
   - &amp;quot;... If you really want to learn a lot about SQL, you may prefer a complete tutorial like [https://selectstarsql.com/ Select Star SQL].&amp;quot;&lt;br /&gt;
* [https://jsvine.github.io/intro-to-visidata/index.html An Introduction to VisiData] - One way to learn the best power tool for exploring and analyzing CSV files, SQLite databases, Excel files, and many other file formats. VisiData is also handy for editing, manipulating, and joining CSV files. Click [https://www.visidata.org/install/ here] to install it.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14271</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14271"/>
		<updated>2022-04-28T17:27:58Z</updated>

		<summary type="html">&lt;p&gt;DRW: Fix markup on /* Deploying ETL jobs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Snake case ===&lt;br /&gt;
&lt;br /&gt;
Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;quot;FIELD NAME&amp;quot; becomes &amp;quot;field_name&amp;quot; and &amp;quot;# of pirates&amp;quot; should be changed to &amp;quot;number_of_pirates&amp;quot;). Reasons we prefer snake case: 1) Marshmallow already converts field names to snake case to some extent automatically. 2) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt;git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=CKAN_Tricks&amp;diff=14270</id>
		<title>CKAN Tricks</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=CKAN_Tricks&amp;diff=14270"/>
		<updated>2022-04-28T17:06:59Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add CKAN Tricks page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Manage datasets ==&lt;br /&gt;
=== Undelete deleted datasets ===&lt;br /&gt;
If you know the URL of the deleted dataset AND you are logged in as an administrator, you can go to that URL in your web browser and see the deleted dataset. &amp;quot;[Deleted]&amp;quot; will have been appended to the title of the dataset, and the value of the metadata field &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt; will be equal to &amp;quot;deleted&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To undelete such a dataset, just use the CKAN API or use the &lt;br /&gt;
[https://github.com/WPRDC/utility-belt/blob/master/gadgets.py#L767 set_package_parameters_to_values() function] to set the package's &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt; metadata value to &amp;quot;active&amp;quot;. For the latter option, invoke the function like this:&lt;br /&gt;
&amp;lt;code&amp;gt;set_package_parameters_to_values(&amp;quot;https://data.wprdc.org&amp;quot;, deleted_package_id, ['state'], ['active'], API_key)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Queries ==&lt;br /&gt;
=== Make queries faster ===&lt;br /&gt;
[https://ckan.org/2017/08/10/faster-datastore-in-ckan-2-7/ Speed up datastore_search queries] by including the &amp;lt;code&amp;gt;include_total=False&amp;lt;/code&amp;gt; parameter to skip calculation of the total number of rows (which can reduce response time by a factor of 2).  The [https://docs.ckan.org/en/ckan-2.7.3/maintaining/datastore.html#ckanext.datastore.logic.action.datastore_search datastore_search API call] lets you search a given datastore by column values and return subsets of the records. There's more on benchmarking CKAN performance [http://urbanopus.net/benchmarking-the-ckan-datastore-api/ here].&lt;br /&gt;
&lt;br /&gt;
Another way to speed up datastore search queries is to index fields used in the filtering. Note that (at least when the primary key is a combination of fields), if you don't list each primary key field as a separate field to index, those fields don't get indexed and queries take way longer.&lt;br /&gt;
&lt;br /&gt;
=== Avoiding stale query caches ===&lt;br /&gt;
Queries/API responses can be cached based upon nginx settings. If you find that your SQL query is getting a stale response, try changing your query slightly. For instance, instead of `SELECT MAX(some_field) AS biggest FROM &amp;lt;resource-id&amp;gt;`, you could change the assigned variable name (`SELECT MAX(some_field) AS biggest0413 FROM &amp;lt;resource-id&amp;gt;`) or add another field that you ignore (`SELECT MAX(some_field) AS biggest, MAX(some_field) AS whatever FROM &amp;lt;resource-id&amp;gt;`).&lt;br /&gt;
&lt;br /&gt;
== Scripts that interact with CKAN through the API ==&lt;br /&gt;
=== Run those CKAN-monitoring/modifying scripts from multiple servers by centralizing data ===&lt;br /&gt;
To avoid keeping local databases about datasets, store such information (such as the last time an ETL job was run on a given package) in the 'extras' metadata field of the CKAN package, as much as possible. This stores information in a centralized location so ETL jobs can be run from multiple computers without any other coordination. The extras metadata fields are currently cataloged on the [[CKAN_Metadata]] page.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14269</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14269"/>
		<updated>2022-04-22T14:26:27Z</updated>

		<summary type="html">&lt;p&gt;DRW: Update daylight-savings-time warnings&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Snake case ===&lt;br /&gt;
&lt;br /&gt;
Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;quot;FIELD NAME&amp;quot; becomes &amp;quot;field_name&amp;quot; and &amp;quot;# of pirates&amp;quot; should be changed to &amp;quot;number_of_pirates&amp;quot;). Reasons we prefer snake case: 1) Marshmallow already converts field names to snake case to some extent automatically. 2) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year (prior to 2023) in a series of hourly local timestamps skips an hour and another day (prior to 2022) has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt;git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14268</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14268"/>
		<updated>2022-04-22T14:06:02Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* Deploying ETL jobs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Snake case ===&lt;br /&gt;
&lt;br /&gt;
Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;quot;FIELD NAME&amp;quot; becomes &amp;quot;field_name&amp;quot; and &amp;quot;# of pirates&amp;quot; should be changed to &amp;quot;number_of_pirates&amp;quot;). Reasons we prefer snake case: 1) Marshmallow already converts field names to snake case to some extent automatically. 2) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year in a series of hourly local timestamps skips an hour and another day has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
Once tested, an ETL job can be deployed by 1) moving the source code for the ETL job to a production server and 2) scheduling the job to run automatically.&lt;br /&gt;
&lt;br /&gt;
Assuming that you are developing the ETL job on a separate computer and in a dev branch of &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt;, this is a typical deployment workflow:&lt;br /&gt;
# Use &amp;lt;code&amp;gt;&amp;gt; git add -p&amp;lt;/code&amp;gt; to construct atomic commits (each of which should thematically cluster changes) and &amp;lt;code&amp;gt;&amp;gt; git commit -m &amp;quot;&amp;lt;Meangingful commit description&amp;gt;&amp;quot;)&amp;lt;/code&amp;gt; to commit them. Repeat until all the code that needs to be deployed has been committed. If you need to add a new file (like &amp;quot;sky_maintenance.py&amp;quot;), try &amp;lt;code&amp;gt;&amp;gt;git add sky_maintenance.py&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt; &amp;gt; git commit -m &amp;quot;Add ETL job for sky-maintenance data&amp;quot;.&lt;br /&gt;
# If you have any other changes to your dev branch that aren't ready for deployment, type &amp;lt;code&amp;gt;&amp;gt; git stash save&amp;lt;/code&amp;gt; to temporarily stash those changes (so you can switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch).&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git checkout master&amp;lt;/code&amp;gt; lets you switch to the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# &amp;lt;code&amp;gt;&amp;gt; git merge dev&amp;lt;/code&amp;gt; merges the changes committed to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch into the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch.&lt;br /&gt;
# Push the changes to GitHub: &amp;lt;code&amp;gt;&amp;gt; git push&amp;lt;/code&amp;gt;&lt;br /&gt;
# Switch back to the &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; branch: &amp;lt;code&amp;gt;&amp;gt; git checkout dev&amp;lt;/code&amp;gt;&lt;br /&gt;
# Restore the stashed code: &amp;lt;code&amp;gt;&amp;gt; git stash pop&amp;lt;/code&amp;gt;&lt;br /&gt;
# Shell into the production server with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Navigate to wherever the &amp;lt;code&amp;gt;rocket-etl&amp;lt;/code&amp;gt; directory is.&lt;br /&gt;
# Pull the changes from GitHub: &amp;lt;code&amp;gt;&amp;gt; git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
# At this point, it's usually best to test the ETL job to make sure it will work in the production environment. Either the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;to_file&amp;lt;/code&amp;gt; command-line parameters can be used if you're not ready to publish data to the production dataset. Failure at this stage usually means that some code or parameter that was supposed to be committed to the git repository didn't get committed or is not defined on the production server.&lt;br /&gt;
# Schedule the job by writing a cron job: &amp;lt;code&amp;gt;&amp;gt; crontab -e&amp;lt;/code&amp;gt; + duplicate a launchpad line that's already in the crontab file + edit it to run the new ETL job and edit the schedule to match the desired ETL schedule.&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Tooling&amp;diff=14267</id>
		<title>Tooling</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Tooling&amp;diff=14267"/>
		<updated>2022-03-22T20:19:57Z</updated>

		<summary type="html">&lt;p&gt;DRW: /* Data tools */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Data tools ==&lt;br /&gt;
* [https://www.visidata.org/ VisiData] - Terminal user interface for a data exploration/manipulation tool that can handle large datasets.&lt;br /&gt;
* [https://github.com/jqnatividad/qsv qsv] - &amp;quot;command line program for indexing, slicing, analyzing, splitting, enriching, validating &amp;amp; joining CSV files. Commands are simple, fast and composable.&amp;quot; Forked from xsv by CKAN Joel, so it's got some CKAN-specific features in the works.&lt;br /&gt;
&lt;br /&gt;
=== Anonymization ===&lt;br /&gt;
* [https://www.open-diffix.org/ Open Diffix] - Free, open-source desktop tool (and eventually Postgres extension) for anonymizing data.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Tooling&amp;diff=14266</id>
		<title>Tooling</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Tooling&amp;diff=14266"/>
		<updated>2022-03-22T20:16:44Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add Tooling page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Data tools ==&lt;br /&gt;
* [https://www.visidata.org/ VisiData] - Terminal user interface for a data exploration/manipulation tool that can handle large datasets.&lt;br /&gt;
* [https://github.com/jqnatividad/qsv qsv] - &amp;quot;command line program for indexing, slicing, analyzing, splitting, enriching, validating &amp;amp; joining CSV files. Commands are simple, fast and composable.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Anonymization ===&lt;br /&gt;
* [https://www.open-diffix.org/ Open Diffix] - Free, open-source desktop tool (and eventually Postgres extension) for anonymizing data.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14263</id>
		<title>ETL</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=ETL&amp;diff=14263"/>
		<updated>2022-03-13T04:28:52Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add timestamp pitfalls&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== ETL overview ==&lt;br /&gt;
&lt;br /&gt;
ETL (an acronym for &amp;quot;Extract-Transform-Load&amp;quot;) describes a data process that obtains data from some source location, transforms it, and delivers it to some output destination.&lt;br /&gt;
&lt;br /&gt;
Most WPRDC ETL processes are written in [https://github.com/WPRDC/rocket-etl/ rocket-etl], an ETL framework customized for use with a) CKAN and b) the specific needs and uses of the [https://data.wprdc.org Western Pennsylvania Regional Data Center open-data portal]. It has been extended to allow the use of command-line parameters to (for instance) override the source and destination locations (pulling data instead from a local file or outputting data to a file, for the convenience of testing pipelines). It can pull data from web sites, FTP servers,  GIS servers that use the data.json standard, and Google Cloud storage, and can deliver data either to the CKAN datastore or the CKAN filestore. It supports CKAN's Express Loader feature to allow faster loading of large data tables.&lt;br /&gt;
&lt;br /&gt;
Some WPRDC ETL processes are still in an older framework; once they're all migrated over, it will be possible to extract a catalog of all ETL processes by parsing the job parameters in the files that represent the ETL jobs.&lt;br /&gt;
&lt;br /&gt;
== Getting data ==&lt;br /&gt;
&lt;br /&gt;
Some of the sources we get data from:&lt;br /&gt;
&lt;br /&gt;
* FTP servers&lt;br /&gt;
**  Here's the [https://docs.ipswitch.com/MOVEit/Transfer2019_1/API/Rest/#_getapi_v1_files_id_download-1_0 API documentation for the MOVEit FTP server ]&lt;br /&gt;
* APIs&lt;br /&gt;
** Google Cloud infrastructure could count as an API&lt;br /&gt;
** Some custom-built APIs by individual vendors&lt;br /&gt;
* GIS servers&lt;br /&gt;
** Historically this was done through CKAN's &amp;quot;Harvester&amp;quot; program.&lt;br /&gt;
** Now we are switching to writing ETL code to analyze the data.json file and pull the desired files over HTTP.&lt;br /&gt;
* Plain old web sites&lt;br /&gt;
&lt;br /&gt;
== Writing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
A useful tool for writing ETL jobs is [https://github.com/WPRDC/little-lexicographer Little Lexicographer]. While initially designed to just facilitate the writing of data dictionaries (by scanning each column and trying to determine the best type for it, then dumping field names and types into a data dictionary template), Little Lexicographer now also has the ability to output a proposed Marshmallow schema for a CSV file. Its type detection is not perfect, so manual review of the assigned types is necessary. Also, Little Lexicographer is often fooled by seemingly numeric values like ZIP codes; if a value is a code (like a ZIP code or a US Census tract), we treat it as a string. This is especially important in the case of codes that may have leading zeros that would be lost if the value were cast to an integer.&lt;br /&gt;
&lt;br /&gt;
=== Snake case ===&lt;br /&gt;
&lt;br /&gt;
Whenever possible, format column names in [https://en.wikipedia.org/wiki/Snake_case snake case]. This means you should convert everything to lower case and change all spaces and punctuation to underscores (so &amp;quot;FIELD NAME&amp;quot; becomes &amp;quot;field_name&amp;quot; and &amp;quot;# of pirates&amp;quot; should be changed to &amp;quot;number_of_pirates&amp;quot;). Reasons we prefer snake case: 1) Marshmallow already converts field names to snake case to some extent automatically. 2) Snake case field names do not need to be quoted or escaped in PostgreSQL queries (making queries of the CKAN datastore easier).&lt;br /&gt;
&lt;br /&gt;
=== Pitfalls ===&lt;br /&gt;
&lt;br /&gt;
* The [http://wingolab.org/2017/04/byteordermark byte-order mark] showing up at the beginning of the first field name in your file. Excel seems to add this character by default (unless the user tells it not to). As usual, the moral of the story is &amp;quot;Never use Excel&amp;quot;.&lt;br /&gt;
* Using a local timestamp instead of a UTC timestamp as a primary key often leads to problems. Because of Daylight Savings Time, one day each year in a series of hourly local timestamps skips an hour and another day has the same local timestamp twice. The [https://www.caktusgroup.com/blog/2019/03/21/coding-time-zones-and-daylight-saving-time/ general] [https://www.jamesridgway.co.uk/why-storing-datetimes-as-utc-isnt-enough/ advice] is to store (and publish) both the UTC timestamp and the local timestamp. We use the UTC timestamp for primary keys and other data operations, but also publish the local timestamp to make it easier for the user to understand the data.&lt;br /&gt;
&lt;br /&gt;
== Testing ETL jobs ==&lt;br /&gt;
&lt;br /&gt;
Typical initial tests of a rocket-etl job can be invoked like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/&amp;lt;name for publisher/project&amp;gt;/&amp;lt;script name&amp;gt;.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where the &amp;lt;code&amp;gt;mute&amp;lt;/code&amp;gt; parameter prevents errors from being sent to the &amp;quot;etl-hell&amp;quot; Slack channel and the to_file parameter writes the output to the default location for the job in question. For instance, the job&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute to_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
would write its output to a file in the directory &amp;lt;code&amp;gt;&amp;lt;PATH TO rocket-etl&amp;gt;/output_files/robopgh/&amp;lt;/code&amp;gt;. Note that the namespacing convention routes the output of &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; jobs to a different directory than that of &amp;lt;code&amp;gt;wormpgh&amp;lt;/code&amp;gt; jobs, but if there were two jobs in the &amp;lt;code&amp;gt;robopgh&amp;lt;/code&amp;gt; payload folder that write to &amp;lt;code&amp;gt;population.csv&amp;lt;/code&amp;gt;, each job would overwrite the output of the other. As this namespacing is for the convenience of testing and development, this level of collision avoidance seems sufficient for now. It's always possible to alter the default output file name by specifying the 'destination_file' parameter in the dict of parameters that define the job (found in, for instance, &amp;lt;code&amp;gt;robopgh/census.py&amp;lt;/code&amp;gt; file).&lt;br /&gt;
&lt;br /&gt;
After running the job, examine the output. [https://www.visidata.org/ VisiData] is an excellent tool for rapidly examining and navigating CSV files. As a first step, it's a good idea to go through each column in the output and make sure that the results make sense. Often this can be done by opening the file in VisiData (&amp;lt;code&amp;gt;&amp;gt; vd output_files/robopgh/population.csv&amp;lt;/code&amp;gt;) and invoking &amp;lt;code&amp;gt;Shift+F&amp;lt;/code&amp;gt; on each column to calculate the histogram of its values. This is a quick way to catch empty columns (which is either a sign that the source file has only null values in it or that there's an error in your ETL code, often because there's a typo in the name of the field you're trying to load from... How [https://marshmallow.readthedocs.io/en/2.x-line/why.html marshmallow] transforms the field names can often be non-intuitive.).&lt;br /&gt;
&lt;br /&gt;
Try to understand what the records in the data represent. Are there any transformations that could be made to help the user understand the data?&lt;br /&gt;
&lt;br /&gt;
Does the set of data as a whole make sense? For instance, look at counts over time (either by grouping records by year+month and aggregating to counts than you can visually scan or by [https://www.visidata.org/docs/graph/ plotting] record counts by date or timestamp).&lt;br /&gt;
&lt;br /&gt;
Are the field names clear? If not, change them to something clearer. Are they unreasonably long when a shorter name would do? Shorten them to something that is still clear.&lt;br /&gt;
&lt;br /&gt;
If you can't figure out something about the data, ask someone else and/or the publisher.&lt;br /&gt;
&lt;br /&gt;
Once you're satisfied with the output data you're getting, you can rerun the job with the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter to push the resulting output to the default testbed dataset (a private dataset used for testing ETL jobs):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute test&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Development instances of rocket-etl should be configured to load to this testbed dataset by default (that is, even if the &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; parameter is not specified) as a safety feature. The parameter that controls this setting is &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt;, which can be found in the &amp;lt;code&amp;gt;engine/parameters/local_parameters.py&amp;lt;/code&amp;gt; file and which should be defined like this:&lt;br /&gt;
&amp;lt;code&amp;gt;PRODUCTION = False&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Only in production environments should &amp;lt;code&amp;gt;PRODUCTION&amp;lt;/code&amp;gt; be set to &amp;lt;code&amp;gt;True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In a development environment, to run an ETL job and push the results to the production version of the dataset, do this:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;gt; python launchpad.py engine/payload/robopgh/census.py mute production&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Deploying ETL jobs ==&lt;br /&gt;
(To be written.)&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=File_Formats&amp;diff=14262</id>
		<title>File Formats</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=File_Formats&amp;diff=14262"/>
		<updated>2022-03-09T20:48:14Z</updated>

		<summary type="html">&lt;p&gt;DRW: Add File Formats page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== GeoJSON ==&lt;br /&gt;
* [https://macwright.com/2015/03/23/geojson-second-bite.html More than you ever wanted to know about GeoJSON]&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
	<entry>
		<id>https://wiki.tessercat.net/index.php?title=Data_Dictionaries&amp;diff=14261</id>
		<title>Data Dictionaries</title>
		<link rel="alternate" type="text/html" href="https://wiki.tessercat.net/index.php?title=Data_Dictionaries&amp;diff=14261"/>
		<updated>2022-03-09T20:44:44Z</updated>

		<summary type="html">&lt;p&gt;DRW: Move table out of list to improve parsing of Markdown&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== What is a data dictionary? ==&lt;br /&gt;
&lt;br /&gt;
Much like a regular dictionary defines words and tells the reader how to use them, a data dictionary explains all the columns (fields) of your data with enough detail that the reader can start using your data.&lt;br /&gt;
&lt;br /&gt;
It lists each field name that appears in the table of data, provides a definition for the field, specifies the type of data in that field (e.g., &amp;quot;string&amp;quot; or &amp;quot;integer&amp;quot;), and gives an example value that could occur in that field.&lt;br /&gt;
&lt;br /&gt;
== Why are data dictionaries important? ==&lt;br /&gt;
&lt;br /&gt;
Published data often has field names that are deliberately short, and they may wind up being cryptic or unclear, particularly to someone unfamiliar with the topic that the data describes. The field might be &amp;quot;weight&amp;quot;, but if the value is 8, the user won't know if the item weighs 8 pounds or 8 kilograms or 8 tons.&lt;br /&gt;
&lt;br /&gt;
Data dictionaries are a simple way to explain such details and make the data you publish more accessible to other people. &lt;br /&gt;
&lt;br /&gt;
== Suggested formats ==&lt;br /&gt;
&lt;br /&gt;
Our integrated data dictionaries use a four-field data dictionary to define your data's fields.&lt;br /&gt;
&lt;br /&gt;
The fields we recommend are:&lt;br /&gt;
* &amp;quot;column&amp;quot;: The name of the field. (Preferably formatted in all lowercase letters and with underscores instead of spaces or other punctuation.)&lt;br /&gt;
* &amp;quot;type&amp;quot;: The type of the field, coded as something like &amp;quot;text&amp;quot; or &amp;quot;float&amp;quot; or &amp;quot;int&amp;quot;. The [http://docs.ckan.org/en/latest/maintaining/datastore.html#field-types full list of types for values that go into the CKAN datastore] (which the WPRDC data portal runs on) is shown in Table 1 below. Note that when using CKAN's integrated data dictionaries, the type of a data dictionary entry is set by the type of the field in the CKAN datastore, so you shouldn't have to set this yourself (unless you need to override the type that CKAN thinks that field is [this is only for a non-ETL upload]).&lt;br /&gt;
* &amp;quot;label&amp;quot;: A short human-readable label for this field. If the field name is &amp;lt;code&amp;gt;zip&amp;lt;/code&amp;gt;, an appropriate label might be &amp;quot;ZIP Code&amp;quot; or &amp;quot;Postal Code&amp;quot;. &lt;br /&gt;
* &amp;quot;description&amp;quot;: A definition of the field (you could, for instance, include in here that the units of the field value are furlongs). This is also the place to put any other information relevant to the field, including information about how the field was calculated from another field or how the field was transformed for publication.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1&lt;br /&gt;
|-&lt;br /&gt;
! CKAN type !! description&lt;br /&gt;
|-&lt;br /&gt;
| text || text string&lt;br /&gt;
|-&lt;br /&gt;
| int || integer&lt;br /&gt;
|-&lt;br /&gt;
| float || real number&lt;br /&gt;
|-&lt;br /&gt;
| boolean || a Boolean value (True or False)&lt;br /&gt;
|-&lt;br /&gt;
| date || a date without a time&lt;br /&gt;
|-&lt;br /&gt;
| time || a time without a date&lt;br /&gt;
|-&lt;br /&gt;
| timestamp || a date and a time together (a.k.a., a &amp;quot;datetime&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| json || a JSON representation of some data (superuseful but by far the most obscure type on this list)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
We also like [https://opendata.stackexchange.com/a/319 the Frictionless Data JSON Table Schema example] approach to data dictionaries, but we're not quite ready for that yet.&lt;br /&gt;
&lt;br /&gt;
Other suggestions: We like to name fields by making all the letters lowercase and converting spaces and other punctuation to the underscore character \(\_\). So, we would convert the field name &amp;quot;Walrus Count&amp;quot; to the name &amp;quot;walrus_count&amp;quot;. This is called &amp;quot;snake case&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== How to create a data dictionary ==&lt;br /&gt;
&lt;br /&gt;
Option 1 (preferred and easiest): Use our fancy new integrated data dictionaries, which you can create through the CKAN web interface.&lt;br /&gt;
&lt;br /&gt;
Option 2: You can use a spreadsheet program and then export the results to CSV.&lt;br /&gt;
&lt;br /&gt;
If you do it this way, check it over by opening it in a text editor, to make sure that Excel didn't format anything (like dates) weirdly.&lt;br /&gt;
&lt;br /&gt;
Option 3: You can type it up by hand. It's not that hard if you have an example.&lt;br /&gt;
(See [https://github.com/WPRDC/little-lexicographer/tree/master/examples here] for examples about books.)&lt;br /&gt;
&lt;br /&gt;
Option 4: Use this handy Python script I wrote: [https://github.com/WPRDC/little-lexicographer little-lexicographer].&lt;br /&gt;
&lt;br /&gt;
== Integrated data dictionaries ==&lt;br /&gt;
&lt;br /&gt;
Our latest version of the data-portal software includes nifty built-in data-dictionary capabilities. As the publisher, you can edit the data dictionary through the management interface and then the user can view it right below the corresponding data table.&lt;br /&gt;
&lt;br /&gt;
=== How to edit a resource's integrated data dictionary ===&lt;br /&gt;
&lt;br /&gt;
1) From the resource page, click the “Manage” button.&lt;br /&gt;
2) Click on the “Data Dictionary” tab. You will see a long form with selectors and blanks for each field.&lt;br /&gt;
3) Optional: Use the “Type Override” selector to change the types for any fields that need to be changed.&lt;br /&gt;
4) You can also provide human-readable names for the fields in the “Label” blank and a longer description in the catch-all “Description” field.&lt;br /&gt;
5) Click “Save” at the bottom of the page.&lt;br /&gt;
&lt;br /&gt;
=== Uploading integrated data dictionaries ===&lt;br /&gt;
&lt;br /&gt;
[https://github.com/WPRDC/little-lexicographer#uploading-integrated-data-dictionaries Little lexicographer] supports uploading properly formatted CSV files to the integrated data dictionary of an existing resource: https://github.com/WPRDC/little-lexicographer#uploading-integrated-data-dictionaries&lt;br /&gt;
&lt;br /&gt;
== Beyond data dictionaries ==&lt;br /&gt;
&lt;br /&gt;
Some datasets benefit from extended documentation. For these, we have [https://tools.wprdc.org/guides/ Data Guides]!&lt;br /&gt;
&lt;br /&gt;
We can also recommend the [https://arxiv.org/abs/1803.09010 Datasheets for Datasets standard] for a comprehensive approach to documenting data that you are publishing.&lt;br /&gt;
&lt;br /&gt;
[[Category:Onboarding]]&lt;/div&gt;</summary>
		<author><name>DRW</name></author>
	</entry>
</feed>