diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/01_intro_open_sci/01_intro_open_sci.md b/01_intro_open_sci/01_intro_open_sci.md new file mode 100644 index 000000000..7d7eee487 --- /dev/null +++ b/01_intro_open_sci/01_intro_open_sci.md @@ -0,0 +1,889 @@ +# Introduction to Open Science + +!!! Success "Learning Objectives" + + After this lesson, you should be able to: + + - Explain what Open Science is + - Explain the components of Open Science + - Describe the behaviors of Open Science + - Explain why Open Science matters in education, research, and society + - Understand the advantages and the challenges to Open Science + +
+
+ +## What is Open Science? + +
+ +_"Open Science is transparent and accessible knowledge that is shared and developed through collaborative networks"_ + +[-Vincente-Saez & Martinez-Fuentes 2018](https://doi.org/10.1016/j.jbusres.2017.12.043) + +
+
+
+
+
+ +_"Open Science is a collaborative and transparent approach to scientific research that emphasizes the accessibility, sharing, and reproducibility of data, methodologies, and findings to foster innovation and inclusivity"_ + +-ChatGPT + +
+
+
+
+
+ +_"A series of reforms that interrogate every step in the research life cycle to make it more efficient, powerful and accountable in our emerging digital society"._ + +-Jeffrey Gillan + + +
+ ![open science](../assets/life_cycle.png){ width="400" } +
The Research Life Cycle from [Open Science Framework](https://osf.io/)
+
+
+
+
+
+ +!!! Quote "Other Definitions" + + "Open Science is defined as an inclusive construct that combines various movements and practices aiming to make multilingual scientific knowledge openly available, accessible and reusable for everyone, to increase scientific collaborations and sharing of information for the benefits of science and society, and to open the processes of scientific knowledge creation, evaluation and communication to societal actors beyond the traditional scientific community." - [UNESCO Definition](https://www.unesco.org/en/natural-sciences/open-science){target=_blank} + + - [UNESCO's Recommendation on Open Science](https://unesdoc.unesco.org/ark:/48223/pf0000379949.locale=en){target=_blank} + + "Open Science is the movement to make scientific research (including publications, data, physical samples, and software) and its dissemination accessible to all levels of society, amateur or professional..." [ :material-wikipedia: Wikipedia definition](https://en.wikipedia.org/wiki/Open_science){target=_blank} +
+ +## Foundational Open Science Skills + +_1. Building a culture of scientists eager to share research materials - such as data, code, methods, documentation, and early results - with colleagues and society at large, in addition to traditional publications_ + +
+
+ +_2. Mastery of digital tools to create reproducible science that others can build upon_ + +
+
+ +_3. Understanding the push towards increased transparency and accountability for those practicing science (ie., compliance)_ + +
+
+
+
+
+ +
+ ![open science](../assets/open_science_word_cloud.png){ width="500" } +
Open Science Word Cloud by [Pownall et al. 2023](http://dx.doi.org/10.31234/osf.io/vypkb)
+
+ +
+
+
+
+
+ +
+ +
_What is Open Science | The Royal Society_ +
+ +
+
+
+
+
+
+ +## 2023: the Year of Open Science + +The White House, joined by 10 federal agencies, and a coalition of more than 85 universities, declared 2023 the Year of Open Science as a way to bring awareness to the benefits of Open Science and to steer the scientitic community towards its adoption. + +NASA leads a prominent program called [Transform to Open Science](https://github.com/nasa/Transform-to-Open-Science) which includes an [online class on Open Science](https://nasa.github.io/Transform-to-Open-Science/take-os101/). + +
+ ![open science](https://zenodo.org/record/7262104/files/Tops_Badge_Nasa.png){ width="200" } +
NASA Transform to Open Science (TOPS)
+
+ +
+
+
+ +--- + +
+
+
+ + +## **:material-pillar: 6 Pillars of Open Science** + +**:material-pillar: Open Access Publications** + +**:material-pillar: Open Data** + +**:material-pillar: Open Educational Resources** + +**:material-pillar: Open Methodology** + +**:material-pillar: Open Peer Review** + +**:material-pillar: Open Source Software** + +??? Question "Wait, how many pillars :material-pillar: of Open Science Are There Really?" + + The number can be from [4 :material-pillar:](https://narratives.insidehighered.com/four-pillars-of-open-science/){target=_blank} to [8 :material-pillar:](https://www.ucl.ac.uk/library/research-support/open-science/8-pillars-open-science){target=_blank} + +
+
+
+
+
+ +## **:material-pillar: Open Access Publications** + +[![open access](https://upload.wikimedia.org/wikipedia/commons/f/f3/Open_Access_PLoS.svg){width=210}](https://en.wikipedia.org/wiki/Open_access){target=_blank} + +!!! Quote "Definition" + + "Open access is a publishing model for scholarly communication that makes research information available to readers at no cost, as opposed to the traditional subscription model in which readers have access to scholarly information by paying a subscription (usually via libraries)." -- [OpenAccess.nl](https://www.openaccess.nl/en/what-is-open-access){target=_blank} + +
+
+
+ +!!! Example "Open Access Journal Examples" + + Major publishers have provided access points for publishing your work + + - [AAAS Science](https://www.science.org/content/page/open-access-aaas){target=_blank} + - [Nature](https://www.nature.com/nature-portfolio/open-access){target=_blank} + - [American Geophysical Union](https://www.agu.org/Publish-with-AGU/Publish/Open-Access){target=_blank} + - [Commonwealth Scientific and Industrial Research Organisation (CSIRO)](https://acsopenscience.org/australia-csiro/){target=_blank} + - [Open Research Europe](https://open-research-europe.ec.europa.eu/){target=_blank} + - [PLOS](https://plos.org/){target=_blank} + - [MDPI](https://www.mdpi.com/){target=_blank} + - [Ecosphere](https://esajournals.onlinelibrary.wiley.com/journal/21508925){target=_blank} + +
+
+ +### Types of Publishing Business Models: + +1. Subscription model - the author pays a smaller fee (or no fee) for the article to be published. The publisher then sells subscription access to the article (usually to institutes of higher education). + +2. Open Access model - The author pays a larger fee to make the article freely available to anyone through a Creative Commons license. + + - Open Access publishing in Nature costs $12,290! + - Open Access publising in PlosOne costs $2,290 + +
+
+
+ +### Research Article Versions + +1. Preprint - In academic publishing, a preprint is a version of scholary paper that precedes formal peer-review and publication in a scientific journal. The preprint may be available, often as a non-typeset version available for free online. + + ??? Example "Pre-print Services" + + - [ASAPbio Pre-Print Server List](https://asapbio.org/preprint-servers){target=_blank} - ASAPbio is a scientist-driven non-profit promoting transparency and innovation comprehensive list of pre-print servers inthe field of life science communication. + - [ESSOar](https://www.essoar.org/){target=_blank} - Earth and Space Science Open Archive hosted by the American Geophysical Union. + - [Peer Community In (PCI)](https://peercommunityin.org/) a free recommendation process of scientific preprints based on peer reviews + - [OSF.io Preprints](https://osf.io/preprints/){target=_blank} are partnered with numerous projects under the "-rXivs" + + ??? Tip "The rXivs" + + - [AfricArXiv](https://osf.io/preprints/africarxiv/){target=_blank} + + - [AgrirXiv](https://cabidigitallibrary.org/journal/agrirxiv){target=_blank} + + - [Arabixiv](https://arabixiv.org/discover){target=_blank} + + - [arXiv](https://arxiv.org/){target=_blank} - is a free distribution service and an open-access archive for 2,086,431 scholarly articles in the fields of physics, mathematics, computer science, quantitative biology, quantitative finance, statistics, electrical engineering and systems science, and economics. + + - [BioHackrXiv](https://biohackrxiv.org/){target=_blank} + - [BioRxiv](https://www.biorxiv.org/){target=_blank} - is an open access preprint repository for the biological sciences. + - [BodorXiv](https://bodoarxiv.wordpress.com/){target=_blank} + - [EarthArXiv](https://eartharxiv.org/){target=_blank} - is an open access preprint repository for the Earth sciences. + - [EcsArXiv](https://ecsarxiv.org/){target=_blank} - a free preprint service for electrochemistry and solid state science and technology + - [EdArXiv](https://edarxiv.org/){target=_blank} - for the education research community + - [EngrXiv](https://engrxiv.org/){target=_blank} for the engineering community + - [EvoEcoRxiv](https://www.ecoevorxiv.com/){target=_blank} - is an open acccess preprint repository for Evolutionary and Ecological sciences. + - [MediArXiv](https://mediarxiv.com/){target=_blank} for Media, Film, & Communication Studies + - [MedRxiv](https://www.medrxiv.org/){target=_blank} - is an open access preprint repository for Medical sciences. + - [PaleorXiv](https://paleorxiv.org/){target=_blank} - is an open access preprint repository for Paleo Sciences + - [PsyrXiv](https://psyarxiv.com/){target=_blank} - is an open access preprint repository for Psychological sciences. + - [SocArXiv](https://socopen.org/){target=_blank} - is an open access preprint repository for Social sciences. + - [SportrXiv](https://sportrxiv.org/){target=_blank} - is an open access preprint for Sports sciences. + - [ThesisCommons](https://thesiscommons.org/) - open Theses + + +2. Author's accepted manuscript (AAM) - includes changes that came about during peer-review process. It is a non-typeset or formatted article. This often had an embargo period of 12-24 months + +3. Published version of record (VOR) - includes stylistic edits, online & print formatting. This is the version that publishers claim ownership of with copyrights or exclusive licensing. + +
+
+ +??? Tip "Copyrights and Science Publishing" + + Upon completion of a peer-reviewed science paper, the author typically 1. signs over the copyright of the paper to the publisher or 2. signs an exclusive license agreement with the publisher + + For example authors that publish in [_Science_](https://www.science.org/content/page/science-journals-editorial-policies#copyright-license-to-publish) retain their copyright but sign a 'license to pubish' agreement with AAAS + + Elsevier requires authors to sign over copyright of the article but authors retains some rights of distribution + + - [Elesevier summary of copyright policies](https://www.elsevier.com/about/policies-and-standards/copyright#1-author-rights) + - [Elesevier article sharing policy](https://www.elsevier.com/about/policies-and-standards/sharing) + - [Wiley policy on self-archiving](https://authorservices.wiley.com/author-resources/Journal-Authors/licensing/self-archiving.html) + - [Springer Nature copyright policies](https://www.springer.com/gp/open-access/publication-policies/copyright-transfer#:~:text=Springer%20Nature%20authors%20retain%20copyright,found%20in%20our%20publishing%20policies.) + +
+
+ +### New Open Access Mandates in US + +The White House Office of Science and Technology (OSTP) has recently released a policy document known as the [Nelson Memo](https://www.whitehouse.gov/ostp/news-updates/2022/08/25/ostp-issues-guidance-to-make-federally-funded-research-freely-available-without-delay/) stating that tax-payer funded research must by open access by 2026 with no embargo period. + +Authors can comply with the memo by either: + +1. Publishing Open Access (this usually requires higher fees) +2. Distributing the Author's Accepted Manuscript (AAM) + +Read [NSF's open access plan](NSF’s Public Access Plan 2.0 is document NSF 23-104) in reponse to the Nelson Memo + +Read [USDA's open access plan](https://www.nal.usda.gov/sites/default/files/page-files/USDA_Public_Access_Implementation_Plan_8_10_2023_0.pdf) in reponse to the Nelson Memo + +
+
+
+
+ +### Additional Info + +University of Arizona Libraries information on [Open Access publishing](https://lib.arizona.edu/research/open-access) including agreements with several journals to reduce or waive publishing fees. + +https://www.coalition-s.org/ + + +
+
+
+
+ +--- + +
+
+ + +## **:material-pillar: Open Data** + +
+ +!!! Quote "Definitions" + + “Open data and content can be freely used, modified, and shared by anyone for any purpose” - [The Open Definition](https://opendefinition.org/){target=_blank} + + "Open data is data that can be freely used, re-used and redistributed by anyone - subject only, at most, to the requirement to attribute and sharealike." - [Open Data Handbook](https://opendatahandbook.org/guide/en/what-is-open-data/){target=_blank} + + [:material-wikipedia: Wikipedia definition](https://en.wikipedia.org/wiki/Open_data){target=_blank} + +
+
+
+ +Data are the foundation for any scientific endeavor. A lot of thought needs to go into how to best collect, store, analyze, curate, share, and archive data. + +
+ ![open science](https://upload.wikimedia.org/wikipedia/commons/0/06/DIKW_Pyramid.svg){ width="400" } +
DIKW Pyramid
+
+ +
+
+ + +### FAIR Principles + +In 2016, the [FAIR Guiding Principles](https://www.nature.com/articles/sdata201618) for scientific data management and stewardship were published in _Scientific Data_. + +_**Findable:**_ +Making data discoverable by the wider academic community and the public + +_**Accessible:**_ +Using unique identifiers, metadata and a clear use of language and access protocols + +_**Interoperable:**_ +Applying standards to encode and exchange data and metadata + +_**Reusable:**_ +Enabling the repurposing of researach outputs to maximize their research potential + +
+
+
+
+ +!!! Tip "Reasons to Make your Data Open" + + - Unnecessary duplication. Duplication of research is costly for society, and places unnecessary burden on heavily researched people and populations. + - The data underlying publications are maintained and accessible, allowing for validation of results. + - Data openness leads to more collaboration and advances research and innovation. + - Your research is more visible and has greater impact. Publications which allow access to the underlying data get more citations. Greater visibility also allows for better validation and scrutiny of findings. + - Other researchers can cite your data, which will drive up your citation number and increase your influence in your field of research. + - Storing your data in a public repository also provides you with secure and ongoing storage that may otherwise not be available to you. + -[Foster Open Science](https://www.fosteropenscience.eu/) + + +
+
+
+
+ +### As Open as Possible, as Closed as Necessary + +There are many circumstances where open data could be harmful: + +- Data on human health + +- Location of endangered species or archaeological sites + +- Data that individuals or groups do not want to be public + + ??? Tip "CARE Principles" + + The [CARE Principles](https://www.gida-global.org/care) for Indigenous Data Governance were drafted at the International Data Week and Research Data Alliance Plenary co-hosted event "Indigenous Data Sovereignty Principles for the Governance of Indigenous Data Workshop," 8 November 2018, Gaborone, Botswana. + + *Collective Benefit* + + - C1. For inclusive development and innovation + - C2. For improved governance and citizen engagement + - C3. For equitable outcomes + + *Authority to Control* + + - A1. Recognizing rights and interests + - A2. Data for governance + - A3. Governance of data + + *Responsibility* + + - R1. For positive relationships + - R2. For expanding capability and capacity + - R3. For Indigenous languages and worldviews + + *Ethics* + + - E1. For minimizing harm and maximizing benefit + - E2. For justice + - E3. For future use + +* Data for making [lethal weapons](https://www.theverge.com/2022/3/17/22983197/ai-new-possible-chemical-weapons-generative-models-vx) + +
+
+ +!!! Tip "Open vs. FAIR" + + FAIR does not demand that data be open: See one definition of open: http://opendefinition.org/ + + Open data does not necessarily mean it is FAIR + +
+
+ +#### Additional Info + +- The Ethics of Geolocated Data from [UK Statistics Authority](https://uksa.statisticsauthority.gov.uk/publication/ethical-considerations-in-the-use-of-geospatial-data-for-research-and-statistics/pages/1/){target=_blank} + +- Health information [US HIPAA](https://www.hhs.gov/hipaa/index.html){target=_blank} + +- Indigenous data sovereignty: [CARE Principles for Indigenous Data Governance](http://doi.org/10.5334/dsj-2020-043){target=_blank} , [Global Indigenous Data Alliance (GIDA)](https://www.gida-global.org/care){target=_blank}, [First Nations OCAP® (Ownership Control Access and Possession)](https://fnigc.ca/ocap-training/){target=_blank}, [Circumpolar Inuit Protocols for Equitable and Ethical Engagement](https://www.arcus.org/arctic-info/archive/33236){target=_blank} + +
+
+
+
+ +--- + +
+
+
+ +## **:material-pillar: Open Educational Resources** + +
+ [![open educational resources](https://upload.wikimedia.org/wikipedia/commons/2/20/Global_Open_Educational_Resources_Logo.svg){width=240}](https://www.unesco.org/en/communication-information/open-solutions/open-educational-resources) +
+ +!!! Quote "Definitions" + + "Open Educational Resources (OER) are learning, teaching and research materials in any format and medium that reside in the public domain or are under copyright that have been released under an open license, that permit no-cost access, re-use, re-purpose, adaptation and redistribution by others." - [UNESCO](https://www.unesco.org/en/communication-information/open-solutions/open-educational-resources){target=_blank} + + [:material-wikipedia: Wikipedia definition](https://en.wikipedia.org/wiki/Open_educational_resources){target=_blank} + +??? Example "Digital Literacy Organizations" + + - [The Carpentries](https://carpentries.org/){target=_blank} - teaches foundational coding and data science skills to researchers worldwide + - [EdX](https://www.edx.org/){target=_blank} - Massively Open Online Courses (not all open) hosted through University of California Berkeley + - [EveryoneOn](https://www.everyoneon.org/ ){target=_blank} - mission is to unlock opportunity by connecting families in underserved communities to affordable internet service and computers, and delivering digital skills trainings + - [ConnectHomeUSA](https://connecthomeusa.org/){target=_blank} - is a movement to bridge the digital divide for HUD-assisted housing residents in the United States under the leadership of national nonprofit EveryoneOn + - [Global Digital Literacy Council](https://www.gdlcouncil.org/){target=_blank} - has dedicated more than 15 years of hard work to the creation and maintenance of worldwide standards in digital literacy + - [IndigiData](https://indigidata.nativebio.org/){target=_blank} - training and engaging tribal undergraduate and graduate students in informatics + - [National Digital Equity Center](https://digitalequitycenter.org/about-us/){target=_blank} a 501c3 non-profit, is a nationally recognized organization with a mission to close the digital divide across the United States + - [National Digital Inclusion Allaince](https://www.digitalinclusion.org/){target=_blank} - advances digital equity by supporting community programs and equipping policymakers to act + - [Net Literacy](https://www.netliteracy.org/){target=_blank} + - [Open Educational Resources Commons](https://www.oercommons.org/){target=_blank} + - [Project Pythia](https://projectpythia.org/){target=_blank} is the education working group for Pangeo and is an educational resource for the entire geoscience community + - [Research Bazaar](https://resbaz.github.io/resbaz2021/){target=_blank} - is a worldwide festival promoting the digital literacy emerging at the centre of modern research + - [TechBoomers](https://techboomers.com/){target=_blank} - is an education and discovery website that provides free tutorials of popular websites and Internet-based services in a manner that is accessible to older adults and other digital technology newcomers + +??? Example "Educational Materials" + + - [Teach Together](https://teachtogether.tech/en/index.html#){target=_blank} by Greg Wilson + - [DigitalLearn](https://www.digitallearn.org/){target=_blank} + +
+
+
+
+ +--- + +
+
+
+ +## **:material-pillar: Open Methodology** + +
+ +!!! Quote "Definitions" + + "An open methodology is simply one which has been described in sufficient detail to allow other researchers to repeat the work and apply it elsewhere." - [Watson (2015)](https://doi.org/10.1186/s13059-015-0669-2){target=_blank} + + "Open Methodology refers to opening up methods that are used by researchers to achieve scientific results and making them publicly available." - [Open Science Network Austria](https://www.oana.at/en/about-open-science){target=_blank} + +
+
+ +### Sharing Research Computer Code + +Scientists around the globe are creating computer code for scientific analysis. These are valuable contributions that need to be shared! + +Platforms like [GitHub](https://github.com/search?q=open+science){target=_blank} and [GitLab](https://gitlab.com/explore/projects/topics/Open%20Science){target=_blank} are ideal for collaboratively developing code and sharing with the open internet. In FOSS, we will show you how to use Github for sharing code, [documentation](04_documentation_communication.md), [hosting websites](04_documentation_communication.md), and [software version control](05_version_control.md). + +
+ github + gitlab +
+ +
+
+
+
+ +### Publishing Your Methods or Protocols + +??? Example "Platforms for Publishing Protocols & Bench Techniques" + + - [BioProtocol](https://bio-protocol.org/Default.aspx){target=_blank} + - [Current Protocols](https://currentprotocols.onlinelibrary.wiley.com/){target=_blank} + - [Gold Biotechnology Protocol list](https://www.goldbio.com/search?q=&type=documentation&documentation_type=protocol){target=_blank} + - [JoVE](https://www.jove.com/){target=_blank} - Journal of Visualized Experiments + - [Nature Protocols](https://www.nature.com/nprot/){target=_blank} + - [OpenWetWare](https://openwetware.org/wiki/Main_Page){target=_blank} + - [Protocol Exchange](https://protocolexchange.researchsquare.com/){target=_blank} + - [Protocols Online](http://www.protocol-online.org/prot/){target=_blank} + - [:material-microscope: Protocols](https://www.protocols.io/){target=_blank} + - [SciGene](http://scigine.com/blog/){target=_blank} + - [Springer Nature Experiments](https://experiments.springernature.com/){target=_blank} + + +
+
+
+
+ +### PreRegistration + +Preregistration is detailing your research and analysis plan and submitting it to an online registry **before** you engage in the research. + +
+ ![open science](../assets/cycle_prereg.png){ width="500" } +
PreRegistration in the Research Life Cycle
+
+ +#### Why Do This? + +Preregistration makes your process more open and records the difference between your initial research plan what you end up actually doing. + +Preregistration separates _hypothesis-generating_ (exploratory) from _hypothesis-testing_ (confirmatory) research. Both are important. But the same data cannot be used to generate and test a hypothesis, which can happen unintentionally and reduce the credibility of your results. + +It also helps us avoid practices like [p-hacking](https://en.wikipedia.org/wiki/Data_dredging){target=_blank} or [Hypothesizing After the Results are Known(HARKing)](https://en.wikipedia.org/wiki/HARKing){target=_blank}. + +
+ +#### Additional Info + +Read this publication by [Nosek et al. 2018](https://www.pnas.org/doi/10.1073/pnas.1708274114){target=_blank} + +Open Science Framework Preregistration https://www.cos.io/initiatives/prereg + +
+
+
+
+ +--- + +
+
+
+ +## **:material-pillar: Open Peer Review** + +
+
+ + +!!! Quote "Definitions" + + Open peer review is an umbrella term for a number of overlapping ways that peer review models can be adapted in line with the aims of Open Science, including making reviewer and author identities open, publishing review reports and enabling greater participation in the peer review process. + + [-Ross-Hellauer et al. (2017)](https://doi.org/10.12688%2Ff1000research.11369.2) + +
+ + [:material-wikipedia: Wikipedia's definition](https://en.wikipedia.org/wiki/Open_peer_review) + +
+
+
+ + +### Traditional Closed Peer-Review System + +
+ ![close peer-review](../assets/peer_review.png){ width="400" } +
+
+ +- Throughout and after the process, the author remains unaware of the reviewers' identities, while the reviewers know the identity of the authors. +- All communications between authors, reviewers and editors remains private + +
+
+
+ +### Complaints with the Traditional Closed Peer-Review System + +- Unreliable and Inconsistent +- Delays and Expense +- Lack of Accountability and Risks of Subversion +- Social and Publication Biases +- Lack of Incentives + +[_Ross-Hallauer 2017_](https://f1000research.com/articles/6-588/v2) + +
+
+
+
+ + +### Open Peer-Review Ideas + +
+ ![open science](../assets/plos_peer_review.png){ width="500" } +
Open Peer Review Options at [PLOS](https://plos.org/resource/open-peer-review/)
+
+ +
+
+
+ + +[Defenders of the Traditional Peer-Review System](https://doi.org/10.1038/6295) + +
+
+
+ +!!! Tips "Example Open Peer-Review Systems" + + [F1000Research](https://f1000research.com/about){target=_blank} An open research publishing platform that offers open peer review and rapid publication. + The article from [Ross-Hellauer et al. (2017)](https://doi.org/10.12688%2Ff1000research.11369.2) has open peer-reviews. + +
+ +!!! Tips "Platforms for Reviewing Preprints" + + + - [PREreview](https://prereview.org/){target=_blank} + - [Sciety](https://sciety.org/){target=_blank} + - [PubPeer](https://pubpeer.com/){target=_blank} + - [ASAPbio](https://asapbio.org/){target=_blank} + +
+
+
+
+ +--- + +
+
+
+ +## **:material-pillar: Open Source Software** + +[![](https://upload.wikimedia.org/wikipedia/commons/4/42/Opensource.svg){width=240}](https://opensource.org/){target=_blank} + +!!! Quote "Definitions" + + "Open source software is code that is designed to be publicly accessible—anyone can see, modify, and distribute the code as they see fit. Open source software is developed in a decentralized and collaborative way, relying on peer review and community production." - [:material-redhat: Red Hat](https://www.redhat.com/en/topics/open-source/what-is-open-source){target=_blank} + + + [:material-wikipedia: Wikipedia definition](https://en.wikipedia.org/wiki/Open-source_software){target=_blank} + +
+
+ +Research science (and also many companies) rely on open source software to operate + +
+
+ +!!! tip "Open Source Software" + + - Linux operating system and shell + - Python + - R + - git + - Conda + - Docker + - Cyverse + - Pytorch + - [Tyson's Awesome List](https://tyson-swetnam.github.io/awesome-open-science/software/){target=_blank} + + +
+
+ +When you create a new software, library, or package, you become its parent and guardian. + +
+ ![xkcd](https://imgs.xkcd.com/comics/dependency.png){ width="400" } +
Image Credit: [XKCD Dependency](https://m.xkcd.com/2347/){target=_blank}
+
+ +
+
+
+
+ +--- + +
+
+
+ +## *WHY* do Open Science? + + +A paper from [Bartling & Friesike (2014)](https://doi.org/10.1007/978-3-319-00026-8) posits that there are 5 main schools of thought in Open Science, which represent 5 underlying motivations: + +1. **Democratic school**: primarily concerned with making scholarly work freely available to everyone +2. **Pragmatic school**: primarily concerned with improving the quality of scholarly work by fostering collaboration and improving critiques +3. **Infrastructure school**: primarily focused on the platforms, tools, and services necessary to conduct efficient research, collaboration, and communication +4. **Public school**: primarily concerned with societal impact of scholarly work, focusing on engagement with broader public via citizen science, understandable scientific communication, and less formal communication +5. **Measurement school**: primarily concerned with the existing focus on journal publications as a means of measuring scholarly output, and focused on developing alternative measurements of scientific impact + +
+ ![fecher_friesike](assets/five_schools.png){ width="700" } +
In [Bartling & Friesike (2014)](https://doi.org/10.1007/978-3-319-00026-8){target=_blank} Open Science: One Term, Five Schools of Thought
+
+ +We have added another school of thought + +6. **Compliance school**: government, universities, and granting agencies have embraced Open Science and are mandating some elements (e.g., data sharing with publications) +
+
+
+
+ +--- + +
+
+
+ +## Discussion Questions + +??? Question "Which of the :material-pillar: pillars of Open Science is nearest to your own heart?" + + **:material-pillar: Open Access Publications** + + **:material-pillar: Open Data** + + **:material-pillar: Open Educational Resources** + + **:material-pillar: Open Methodology** + + **:material-pillar: Open Peer Review** + + **:material-pillar: Open Source Software** + +??? Question "Are any of the :material-pillar: pillars more important than the others?" + +??? Question "Are there any :material-pillar: pillars not identified that you think should be considered?" + +??? Question "What characteristics might a paper, project, lab group require to qualify as doing *Open Science*" + +??? Question "What are some barriers to you, your lab group, or your domain doing Open Science?" + +??? Question "What motivates you to do Open Science?" + +??? Question "Do you feel that you fall into a particular "school"? If so, which one, and why?" + +??? Question "Are there any motivating factors for doing Open Science that don't fit into this framework?" + + +
+
+
+ +--- + +
+
+ + + +## Recommended Open Science Communities + +
+ turingway + nasatops + foster + carpentries + cos +
+ +[:material-school: Open Scholarship Grassroots Community Networks](https://docs.google.com/spreadsheets/d/1LNF5_bOkRV-RLIF4HYmu-gOemIa4IdfXEer89fM-Vy8/edit#gid=847887324){target=_blank} + +??? Info ":fontawesome-solid-earth-europe: International Open Science Networks" + + - [Center for Scientific Collaboration and Community Engagement (CSCCE)](https://www.cscce.org/){target=_blank} + - [Center for Open Science (COS)](https://www.cos.io/){target=_blank} + - [Eclipse Science Working Group](https://science.eclipse.org/){target=_blank} + - [eLife](https://elifesciences.org/){target=_blank} + - [NumFocus](https://numfocus.org/){target=_blank} + - [Open Access Working Group](https://sparcopen.org/people/open-access-working-group/){target=_blank} + - [Open Research Funders Group](https://www.orfg.org/) + - [Open Science Foundation](https://osf.io/){target=_blank} + - [Open Science Network](https://www.opensciencenetwork.org/){target=_blank} + - [pyOpenSci](https://www.pyopensci.org/){target=_blank} + - [R OpenSci](https://ropensci.org/){target=_blank} + - [Research Data Alliance (RDA)](https://www.rd-alliance.org/){target=_blank} + - [The Turing Way](https://the-turing-way.netlify.app/welcome){target=_blank} + - [UNESCO Global Open Science Partnership](https://en.unesco.org/science-sustainable-future/open-science/partnership){target=_blank} + - [World Wide Web Consortium (W3C)](https://www.w3.org/){target=_blank} + +??? Info ":fontawesome-solid-earth-americas: US-based Open Science Networks" + + - [CI Compass](https://ci-compass.org/){target=_blank} - provides expertise and active support to cyberinfrastructure practitioners at USA NSF Major Facilities in order to accelerate the data lifecycle and ensure the integrity and effectiveness of the cyberinfrastructure upon which research and discovery depend. + - [Earth Science Information Partners (ESIP) Federation](https://www.esipfed.org/){target=_blank} - is a 501(c)(3) nonprofit supported by NASA, NOAA, USGS and 130+ member organizations. + - [Internet2](https://internet2.edu/){target=_blank} - is a community providing cloud solutions, research support, and services tailored for Research and Education. + - [Minority Serving Cyberinfrastructure Consortium (MS-CC)](https://www.ms-cc.org/){target=_blank} envisions a transformational partnership to promote advanced cyberinfrastructure (CI) capabilities on the campuses of Historically Black Colleges and Universities (HBCUs), Hispanic-Serving Institutions (HSIs), Tribal Colleges and Universities (TCUs), and other Minority Serving Institutions (MSIs). + - [NASA Transform to Open Science (TOPS)](https://github.com/nasa/Transform-to-Open-Science){target=_blank} - coordinates efforts designed to rapidly transform agencies, organizations, and communities for Earth Science + - [OpenScapes](https://www.openscapes.org/){target=_blank} - is an approach for doing better science for future us + - [The Quilt](https://www.thequilt.net/){target=_blank} - non-profit regional research and education networks collaborate to develop, deploy and operate advanced cyberinfrastructure that enables innovation in research and education. + +??? Info ":fontawesome-solid-earth-oceania: Oceania Open Science Networks" + + - [New Zealand Open Research Network](https://nzorn.netlify.app/) - New Zealand Open Research Network (NZORN) is a collection of researchers and research-associated workers in New Zealand. + - [Australia & New Zealand Open Research Network](https://www.anzopenresearch.org/) - ANZORN is a network of local networks distributed without Australia and New Zealand. + +
+
+
+ +--- + +
+
+
+ +## Self Assessment + +??? Question "True or False: All research papers published in the top journals, like Science and Nature, are always Open Access?" + + ??? Success "Answer" + + False + + Major Research journals like [Science](https://www.science.org/content/page/open-access-aaas){target=_blank} and [Nature](https://www.nature.com/nature-portfolio/open-access){target=_blank} have an "Open Access" option when a manuscript is accepted, but they charge an extra fee to the authors to make those papers Open Access. + + These [high page costs](https://www.science.org/content/article/9500-nature-journals-will-now-make-your-paper-free-read){target=_blank} are exclusionary to the majority of global scientists who cannot afford to front these costs out of pocket. + + This will soon change, at least in the United States. The [Executive Branch of the federal government recently mandated](https://www.nature.com/articles/d41586-022-02351-1){target=_blank} that future federally funded research be made Open Access after 2026. + + +??? Question "True or False: an article states all of the research data used in the experiments "are available upon request from the corresponding author(s)," meaning the data are "Open"" + + ??? Success "Answer" + + False + + In order for research to be open, the data need to be freely available from a digital repository, like [Data Dryad](https://datadryad.org){target=_blank}, [Zenodo.org](https://zenodo.org){target=_blank}, or [CyVerse](https://cyverse.org/data-commons){target=_blank}. + + Data that are 'available upon request' do not meet the FAIR data principles. + + + +??? Question "Using a version control system to host the analysis code and computational notebooks, and including these in your Methods section or Supplementary Materials, is an example of an Open Methodology?" + + ??? Success "Answer" + + Yes! + + Using a VCS like GitHub or GitLab is a great step towards making your research more reproducible. + + Ways to improve your open methology can include documentation of your physical bench work, and even video recordings and step-by-step guides for every part of your project. + +??? Question "You are asked to review a paper for an important journal in your field. The editor asks if you're willing to release your identity to the authors, thereby "signing" your review. Is this an example of "Open Peer Review"?" + + ??? Success "Answer" + + Maybe + + There are many opinions on what 'open-review' should consist of. A reviewer signing their review and releasing their identity to the authors is a step toward a more open process. However, it is far less open than publishing the peer-review reports online next to the final published paper. + + +??? Question "You read a paper where the author(s) wrote their own code and licensed as "Open Source" software for a specific set of scientific tasks which you want to replicate. When you visit their personal website, you find the GitHub repository does not exist (because its now private). You contact the authors asking for access, but they refuse to share it 'due to competing researchers who are seeking to steal their intellectual property". Is the software open source?" + + ??? Success "Answer" + + No + + Just because an author states they have given their software a permissive software license, does not make the software open source. + + Always make certain there is a [LICENSE](https://choosealicense.com/licenses/){target=_blank} associated with any software you find on the internet. + + In order for the software to be open, it must follow the [Open Source Initiative definition](https://opensource.org/osd){target=_blank} + + diff --git a/01_intro_open_sci/index.html b/01_intro_open_sci/index.html new file mode 100644 index 000000000..45eaec769 --- /dev/null +++ b/01_intro_open_sci/index.html @@ -0,0 +1,2435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1. Open Science - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Introduction to Open Science

+
+

Learning Objectives

+

After this lesson, you should be able to:

+
    +
  • Explain what Open Science is
  • +
  • Explain the components of Open Science
  • +
  • Describe the behaviors of Open Science
  • +
  • Explain why Open Science matters in education, research, and society
  • +
  • Understand the advantages and the challenges to Open Science
  • +
+
+


+

+

What is Open Science?

+


+

"Open Science is transparent and accessible knowledge that is shared and developed through collaborative networks"

+

-Vincente-Saez & Martinez-Fuentes 2018

+


+
+
+
+

+

"Open Science is a collaborative and transparent approach to scientific research that emphasizes the accessibility, sharing, and reproducibility of data, methodologies, and findings to foster innovation and inclusivity"

+

-ChatGPT

+


+
+
+
+

+

"A series of reforms that interrogate every step in the research life cycle to make it more efficient, powerful and accountable in our emerging digital society".

+

-Jeffrey Gillan

+
+

open science +

The Research Life Cycle from Open Science Framework

+
+


+
+
+

+
+

Other Definitions

+

"Open Science is defined as an inclusive construct that combines various movements and practices aiming to make multilingual scientific knowledge openly available, accessible and reusable for everyone, to increase scientific collaborations and sharing of information for the benefits of science and society, and to open the processes of scientific knowledge creation, evaluation and communication to societal actors beyond the traditional scientific community." - UNESCO Definition

+ +

"Open Science is the movement to make scientific research (including publications, data, physical samples, and software) and its dissemination accessible to all levels of society, amateur or professional..." Wikipedia definition

+
+


+

Foundational Open Science Skills

+

1. Building a culture of scientists eager to share research materials - such as data, code, methods, documentation, and early results - with colleagues and society at large, in addition to traditional publications

+


+

+

2. Mastery of digital tools to create reproducible science that others can build upon

+


+

+

3. Understanding the push towards increased transparency and accountability for those practicing science (ie., compliance)

+


+
+
+
+

+
+ open science +
Open Science Word Cloud by Pownall et al. 2023
+
+


+
+
+
+

+
+ +
What is Open Science | The Royal Society +
+


+
+
+
+
+

+

2023: the Year of Open Science

+

The White House, joined by 10 federal agencies, and a coalition of more than 85 universities, declared 2023 the Year of Open Science as a way to bring awareness to the benefits of Open Science and to steer the scientitic community towards its adoption.

+

NASA leads a prominent program called Transform to Open Science which includes an online class on Open Science.

+
+

open science +

NASA Transform to Open Science (TOPS)

+
+


+
+

+
+


+
+

+

6 Pillars of Open Science

+

Open Access Publications

+

Open Data

+

Open Educational Resources

+

Open Methodology

+

Open Peer Review

+

Open Source Software

+
+Wait, how many pillars of Open Science Are There Really? +

The number can be from 4 to 8

+
+


+
+
+
+

+

Open Access Publications

+

open access

+
+

Definition

+

"Open access is a publishing model for scholarly communication that makes research information available to readers at no cost, as opposed to the traditional subscription model in which readers have access to scholarly information by paying a subscription (usually via libraries)." -- OpenAccess.nl

+
+


+
+

+
+

Open Access Journal Examples

+

Major publishers have provided access points for publishing your work

+ +
+


+

+

Types of Publishing Business Models:

+
    +
  1. +

    Subscription model - the author pays a smaller fee (or no fee) for the article to be published. The publisher then sells subscription access to the article (usually to institutes of higher education).

    +
  2. +
  3. +

    Open Access model - The author pays a larger fee to make the article freely available to anyone through a Creative Commons license.

    +
      +
    • Open Access publishing in Nature costs $12,290!
    • +
    • Open Access publising in PlosOne costs $2,290
    • +
    +
  4. +
+


+
+

+

Research Article Versions

+
    +
  1. +

    Preprint - In academic publishing, a preprint is a version of scholary paper that precedes formal peer-review and publication in a scientific journal. The preprint may be available, often as a non-typeset version available for free online.

    +
    +Pre-print Services +
      +
    • ASAPbio Pre-Print Server List - ASAPbio is a scientist-driven non-profit promoting transparency and innovation comprehensive list of pre-print servers inthe field of life science communication.
    • +
    • ESSOar - Earth and Space Science Open Archive hosted by the American Geophysical Union.
    • +
    • Peer Community In (PCI) a free recommendation process of scientific preprints based on peer reviews
    • +
    • OSF.io Preprints are partnered with numerous projects under the "-rXivs"
    • +
    +
    +The rXivs +
      +
    • +

      AfricArXiv

      +
    • +
    • +

      AgrirXiv

      +
    • +
    • +

      Arabixiv

      +
    • +
    • +

      arXiv - is a free distribution service and an open-access archive for 2,086,431 scholarly articles in the fields of physics, mathematics, computer science, quantitative biology, quantitative finance, statistics, electrical engineering and systems science, and economics.

      +
    • +
    • +

      BioHackrXiv

      +
    • +
    • BioRxiv - is an open access preprint repository for the biological sciences.
    • +
    • BodorXiv
    • +
    • EarthArXiv - is an open access preprint repository for the Earth sciences.
    • +
    • EcsArXiv - a free preprint service for electrochemistry and solid state science and technology
    • +
    • EdArXiv - for the education research community
    • +
    • EngrXiv for the engineering community
    • +
    • EvoEcoRxiv - is an open acccess preprint repository for Evolutionary and Ecological sciences.
    • +
    • MediArXiv for Media, Film, & Communication Studies
    • +
    • MedRxiv - is an open access preprint repository for Medical sciences.
    • +
    • PaleorXiv - is an open access preprint repository for Paleo Sciences
    • +
    • PsyrXiv - is an open access preprint repository for Psychological sciences.
    • +
    • SocArXiv - is an open access preprint repository for Social sciences.
    • +
    • SportrXiv - is an open access preprint for Sports sciences.
    • +
    • ThesisCommons - open Theses
    • +
    +
    +
    +
  2. +
  3. +

    Author's accepted manuscript (AAM) - includes changes that came about during peer-review process. It is a non-typeset or formatted article. This often had an embargo period of 12-24 months

    +
  4. +
  5. +

    Published version of record (VOR) - includes stylistic edits, online & print formatting. This is the version that publishers claim ownership of with copyrights or exclusive licensing.

    +
  6. +
+


+

+
+Copyrights and Science Publishing +

Upon completion of a peer-reviewed science paper, the author typically 1. signs over the copyright of the paper to the publisher or 2. signs an exclusive license agreement with the publisher

+

For example authors that publish in Science retain their copyright but sign a 'license to pubish' agreement with AAAS

+

Elsevier requires authors to sign over copyright of the article but authors retains some rights of distribution

+ +
+


+

+

New Open Access Mandates in US

+

The White House Office of Science and Technology (OSTP) has recently released a policy document known as the Nelson Memo stating that tax-payer funded research must by open access by 2026 with no embargo period.

+

Authors can comply with the memo by either:

+
    +
  1. Publishing Open Access (this usually requires higher fees)
  2. +
  3. Distributing the Author's Accepted Manuscript (AAM)
  4. +
+

Read NSF's open access plan in reponse to the Nelson Memo

+

Read USDA's open access plan in reponse to the Nelson Memo

+


+
+
+

+

Additional Info

+

University of Arizona Libraries information on Open Access publishing including agreements with several journals to reduce or waive publishing fees.

+

https://www.coalition-s.org/

+



+
+
+

+
+


+

+

Open Data

+


+
+

Definitions

+

“Open data and content can be freely used, modified, and shared by anyone for any purpose” - The Open Definition

+

"Open data is data that can be freely used, re-used and redistributed by anyone - subject only, at most, to the requirement to attribute and sharealike." - Open Data Handbook

+

Wikipedia definition

+
+


+
+

+

Data are the foundation for any scientific endeavor. A lot of thought needs to go into how to best collect, store, analyze, curate, share, and archive data.

+
+

open science +

DIKW Pyramid

+
+


+

+

FAIR Principles

+

In 2016, the FAIR Guiding Principles for scientific data management and stewardship were published in Scientific Data.

+

Findable: +Making data discoverable by the wider academic community and the public

+

Accessible: +Using unique identifiers, metadata and a clear use of language and access protocols

+

Interoperable: +Applying standards to encode and exchange data and metadata

+

Reusable: +Enabling the repurposing of researach outputs to maximize their research potential

+


+
+
+

+
+

Reasons to Make your Data Open

+
    +
  • Unnecessary duplication. Duplication of research is costly for society, and places unnecessary burden on heavily researched people and populations.
  • +
  • The data underlying publications are maintained and accessible, allowing for validation of results.
  • +
  • Data openness leads to more collaboration and advances research and innovation.
  • +
  • Your research is more visible and has greater impact. Publications which allow access to the underlying data get more citations. Greater visibility also allows for better validation and scrutiny of findings.
  • +
  • Other researchers can cite your data, which will drive up your citation number and increase your influence in your field of research.
  • +
  • Storing your data in a public repository also provides you with secure and ongoing storage that may otherwise not be available to you. +-Foster Open Science
  • +
+
+


+
+
+

+

As Open as Possible, as Closed as Necessary

+

There are many circumstances where open data could be harmful:

+
    +
  • +

    Data on human health

    +
  • +
  • +

    Location of endangered species or archaeological sites

    +
  • +
  • +

    Data that individuals or groups do not want to be public

    +
    +CARE Principles +

    The CARE Principles for Indigenous Data Governance were drafted at the International Data Week and Research Data Alliance Plenary co-hosted event "Indigenous Data Sovereignty Principles for the Governance of Indigenous Data Workshop," 8 November 2018, Gaborone, Botswana.

    +

    Collective Benefit

    +
      +
    • C1. For inclusive development and innovation
    • +
    • C2. For improved governance and citizen engagement
    • +
    • C3. For equitable outcomes
    • +
    +

    Authority to Control

    +
      +
    • A1. Recognizing rights and interests
    • +
    • A2. Data for governance
    • +
    • A3. Governance of data
    • +
    +

    Responsibility

    +
      +
    • R1. For positive relationships
    • +
    • R2. For expanding capability and capacity
    • +
    • R3. For Indigenous languages and worldviews
    • +
    +

    Ethics

    +
      +
    • E1. For minimizing harm and maximizing benefit
    • +
    • E2. For justice
    • +
    • E3. For future use
    • +
    +
    +
  • +
  • +

    Data for making lethal weapons

    +
  • +
+


+

+
+

Open vs. FAIR

+

FAIR does not demand that data be open: See one definition of open: http://opendefinition.org/

+

Open data does not necessarily mean it is FAIR

+
+


+

+

Additional Info

+ +


+
+
+

+
+



+
+

+

Open Educational Resources

+
+ open educational resources +
+
+

Definitions

+

"Open Educational Resources (OER) are learning, teaching and research materials in any format and medium that reside in the public domain or are under copyright that have been released under an open license, that permit no-cost access, re-use, re-purpose, adaptation and redistribution by others." - UNESCO

+

Wikipedia definition

+
+
+Digital Literacy Organizations +
    +
  • The Carpentries - teaches foundational coding and data science skills to researchers worldwide
  • +
  • EdX - Massively Open Online Courses (not all open) hosted through University of California Berkeley
  • +
  • EveryoneOn - mission is to unlock opportunity by connecting families in underserved communities to affordable internet service and computers, and delivering digital skills trainings
  • +
  • ConnectHomeUSA - is a movement to bridge the digital divide for HUD-assisted housing residents in the United States under the leadership of national nonprofit EveryoneOn
  • +
  • Global Digital Literacy Council - has dedicated more than 15 years of hard work to the creation and maintenance of worldwide standards in digital literacy
  • +
  • IndigiData - training and engaging tribal undergraduate and graduate students in informatics
  • +
  • National Digital Equity Center a 501c3 non-profit, is a nationally recognized organization with a mission to close the digital divide across the United States
  • +
  • National Digital Inclusion Allaince - advances digital equity by supporting community programs and equipping policymakers to act
  • +
  • Net Literacy
  • +
  • Open Educational Resources Commons
  • +
  • Project Pythia is the education working group for Pangeo and is an educational resource for the entire geoscience community
  • +
  • Research Bazaar - is a worldwide festival promoting the digital literacy emerging at the centre of modern research
  • +
  • TechBoomers - is an education and discovery website that provides free tutorials of popular websites and Internet-based services in a manner that is accessible to older adults and other digital technology newcomers
  • +
+
+
+Educational Materials + +
+


+
+
+

+
+



+
+

+

Open Methodology

+


+
+

Definitions

+

"An open methodology is simply one which has been described in sufficient detail to allow other researchers to repeat the work and apply it elsewhere." - Watson (2015)

+

"Open Methodology refers to opening up methods that are used by researchers to achieve scientific results and making them publicly available." - Open Science Network Austria

+
+


+

+

Sharing Research Computer Code

+

Scientists around the globe are creating computer code for scientific analysis. These are valuable contributions that need to be shared!

+

Platforms like GitHub and GitLab are ideal for collaboratively developing code and sharing with the open internet. In FOSS, we will show you how to use Github for sharing code, documentation, hosting websites, and software version control.

+
+ github + gitlab +
+ +


+
+
+

+

Publishing Your Methods or Protocols

+
+Platforms for Publishing Protocols & Bench Techniques + +
+


+
+
+

+

PreRegistration

+

Preregistration is detailing your research and analysis plan and submitting it to an online registry before you engage in the research.

+
+

open science +

PreRegistration in the Research Life Cycle

+
+

Why Do This?

+

Preregistration makes your process more open and records the difference between your initial research plan what you end up actually doing.

+

Preregistration separates hypothesis-generating (exploratory) from hypothesis-testing (confirmatory) research. Both are important. But the same data cannot be used to generate and test a hypothesis, which can happen unintentionally and reduce the credibility of your results.

+

It also helps us avoid practices like p-hacking or Hypothesizing After the Results are Known(HARKing).

+


+

Additional Info

+

Read this publication by Nosek et al. 2018

+

Open Science Framework Preregistration https://www.cos.io/initiatives/prereg

+


+
+
+

+
+



+
+

+

Open Peer Review

+


+

+
+

Definitions

+

Open peer review is an umbrella term for a number of overlapping ways that peer review models can be adapted in line with the aims of Open Science, including making reviewer and author identities open, publishing review reports and enabling greater participation in the peer review process.

+

-Ross-Hellauer et al. (2017)

+


+

Wikipedia's definition

+
+


+
+

+

Traditional Closed Peer-Review System

+
+

close peer-review +

+
+
    +
  • Throughout and after the process, the author remains unaware of the reviewers' identities, while the reviewers know the identity of the authors.
  • +
  • All communications between authors, reviewers and editors remains private
  • +
+


+
+

+

Complaints with the Traditional Closed Peer-Review System

+
    +
  • Unreliable and Inconsistent
  • +
  • Delays and Expense
  • +
  • Lack of Accountability and Risks of Subversion
  • +
  • Social and Publication Biases
  • +
  • Lack of Incentives
  • +
+

Ross-Hallauer 2017

+


+
+
+

+

Open Peer-Review Ideas

+
+

open science +

Open Peer Review Options at PLOS

+
+


+
+

+

Defenders of the Traditional Peer-Review System

+


+
+

+
+

Example Open Peer-Review Systems

+

F1000Research An open research publishing platform that offers open peer review and rapid publication. +The article from Ross-Hellauer et al. (2017) has open peer-reviews.

+
+


+
+

Platforms for Reviewing Preprints

+ +
+


+
+
+

+
+



+
+

+

Open Source Software

+

+
+

Definitions

+

"Open source software is code that is designed to be publicly accessible—anyone can see, modify, and distribute the code as they see fit. Open source software is developed in a decentralized and collaborative way, relying on peer review and community production." - Red Hat

+

Wikipedia definition

+
+


+

+

Research science (and also many companies) rely on open source software to operate

+


+

+
+

Open Source Software

+
    +
  • Linux operating system and shell
  • +
  • Python
  • +
  • R
  • +
  • git
  • +
  • Conda
  • +
  • Docker
  • +
  • Cyverse
  • +
  • Pytorch
  • +
  • Tyson's Awesome List
  • +
+
+


+

+

When you create a new software, library, or package, you become its parent and guardian.

+
+

xkcd +

Image Credit: XKCD Dependency

+
+


+
+
+

+
+



+
+

+

WHY do Open Science?

+

A paper from Bartling & Friesike (2014) posits that there are 5 main schools of thought in Open Science, which represent 5 underlying motivations:

+
    +
  1. Democratic school: primarily concerned with making scholarly work freely available to everyone
  2. +
  3. Pragmatic school: primarily concerned with improving the quality of scholarly work by fostering collaboration and improving critiques
  4. +
  5. Infrastructure school: primarily focused on the platforms, tools, and services necessary to conduct efficient research, collaboration, and communication
  6. +
  7. Public school: primarily concerned with societal impact of scholarly work, focusing on engagement with broader public via citizen science, understandable scientific communication, and less formal communication
  8. +
  9. Measurement school: primarily concerned with the existing focus on journal publications as a means of measuring scholarly output, and focused on developing alternative measurements of scientific impact
  10. +
+
+

fecher_friesike +

In Bartling & Friesike (2014) Open Science: One Term, Five Schools of Thought

+
+

We have added another school of thought

+
    +
  1. Compliance school: government, universities, and granting agencies have embraced Open Science and are mandating some elements (e.g., data sharing with publications) +
    +
    +
    +
  2. +
+
+



+
+

+

Discussion Questions

+
+Which of the pillars of Open Science is nearest to your own heart? +

Open Access Publications

+

Open Data

+

Open Educational Resources

+

Open Methodology

+

Open Peer Review

+

Open Source Software

+
+
+Are any of the pillars more important than the others? +
+
+Are there any pillars not identified that you think should be considered? +
+
+What characteristics might a paper, project, lab group require to qualify as doing Open Science +
+
+What are some barriers to you, your lab group, or your domain doing Open Science? +
+
+What motivates you to do Open Science? +
+
+Do you feel that you fall into a particular "school"? If so, which one, and why? +
+
+Are there any motivating factors for doing Open Science that don't fit into this framework? +
+


+
+

+
+



+

+ +
+ turingway + nasatops + foster + carpentries + cos +
+ +

Open Scholarship Grassroots Community Networks

+
+ International Open Science Networks + +
+
+ US-based Open Science Networks +
    +
  • CI Compass - provides expertise and active support to cyberinfrastructure practitioners at USA NSF Major Facilities in order to accelerate the data lifecycle and ensure the integrity and effectiveness of the cyberinfrastructure upon which research and discovery depend.
  • +
  • Earth Science Information Partners (ESIP) Federation - is a 501©(3) nonprofit supported by NASA, NOAA, USGS and 130+ member organizations.
  • +
  • Internet2 - is a community providing cloud solutions, research support, and services tailored for Research and Education.
  • +
  • Minority Serving Cyberinfrastructure Consortium (MS-CC) envisions a transformational partnership to promote advanced cyberinfrastructure (CI) capabilities on the campuses of Historically Black Colleges and Universities (HBCUs), Hispanic-Serving Institutions (HSIs), Tribal Colleges and Universities (TCUs), and other Minority Serving Institutions (MSIs).
  • +
  • NASA Transform to Open Science (TOPS) - coordinates efforts designed to rapidly transform agencies, organizations, and communities for Earth Science
  • +
  • OpenScapes - is an approach for doing better science for future us
  • +
  • The Quilt - non-profit regional research and education networks collaborate to develop, deploy and operate advanced cyberinfrastructure that enables innovation in research and education.
  • +
+
+
+ Oceania Open Science Networks + +
+


+
+

+
+


+
+

+

Self Assessment

+
+True or False: All research papers published in the top journals, like Science and Nature, are always Open Access? +
+Answer +

False

+

Major Research journals like Science and Nature have an "Open Access" option when a manuscript is accepted, but they charge an extra fee to the authors to make those papers Open Access.

+

These high page costs are exclusionary to the majority of global scientists who cannot afford to front these costs out of pocket.

+

This will soon change, at least in the United States. The Executive Branch of the federal government recently mandated that future federally funded research be made Open Access after 2026.

+
+
+
+True or False: an article states all of the research data used in the experiments "are available upon request from the corresponding author(s)," meaning the data are "Open" +
+Answer +

False

+

In order for research to be open, the data need to be freely available from a digital repository, like Data Dryad, Zenodo.org, or CyVerse.

+

Data that are 'available upon request' do not meet the FAIR data principles.

+
+
+
+Using a version control system to host the analysis code and computational notebooks, and including these in your Methods section or Supplementary Materials, is an example of an Open Methodology? +
+Answer +

Yes!

+

Using a VCS like GitHub or GitLab is a great step towards making your research more reproducible.

+

Ways to improve your open methology can include documentation of your physical bench work, and even video recordings and step-by-step guides for every part of your project.

+
+
+
+You are asked to review a paper for an important journal in your field. The editor asks if you're willing to release your identity to the authors, thereby "signing" your review. Is this an example of "Open Peer Review"? +
+Answer +

Maybe

+

There are many opinions on what 'open-review' should consist of. A reviewer signing their review and releasing their identity to the authors is a step toward a more open process. However, it is far less open than publishing the peer-review reports online next to the final published paper.

+
+
+
+You read a paper where the author(s) wrote their own code and licensed as "Open Source" software for a specific set of scientific tasks which you want to replicate. When you visit their personal website, you find the GitHub repository does not exist (because its now private). You contact the authors asking for access, but they refuse to share it 'due to competing researchers who are seeking to steal their intellectual property". Is the software open source? +
+Answer +

No

+

Just because an author states they have given their software a permissive software license, does not make the software open source.

+

Always make certain there is a LICENSE associated with any software you find on the internet.

+

In order for the software to be open, it must follow the Open Source Initiative definition

+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/02_managing_data/02_managing_data.md b/02_managing_data/02_managing_data.md new file mode 100644 index 000000000..c13525d8a --- /dev/null +++ b/02_managing_data/02_managing_data.md @@ -0,0 +1,454 @@ +# Managing Data + +!!! Success "Learning Objectives" + + After this lesson, you should be able to: + + * Recognize data as the foundation of open science and be able to describe the "life cycle of data" + * Use self-assessments to evaluate your current data management practices + * Cite tools and resources to improve your data management practices + * Know the biggest challenge to effective data management + +??? Question "How would you answer?" + - If you give your data to a colleague who has not been involved with your project, would they be able to make sense of it? Would they be able to use it properly? + - If you come back to your own data in five years, will you be able to make sense of it? Will you be able to use it properly? + - When you are ready to publish a paper, is it easy to find all the correct versions of all the data you used and present them in a comprehensible manner? + +## Why should you care about data management? + +!!! Danger "The biggest challenge to data management is making it an afterthought. " + +Poor data management doesn't have an upfront cost. **You can do substantial work before realizing you are in trouble.** + +**The solution?** Make data management the first thing you consider when starting a research project. + +Well-managed Data Sets: + +- Can make life much easier for you and your collaborators +- Benefit the scientific research community by allowing others to reuse your data +- Are becoming required by most funders and many journals, which are requesting a submission of a Data Management Plan (DMP) with the initial submission of your proposal. + +!!! info "The NSF is stepping in, getting stricter about data" + - Recent [Dear Colleague letter](https://www.nsf.gov/pubs/2019/nsf19069/nsf19069.jsp)from NSF's points out that: + - Open science promotes broader access to research data, enhancing public benefits and replicability. + - NSF requires (DMPs) in proposals, encouraging use of persistent IDs and machine-readable DMPs. + - NSF [proposal preparation guidelines](https://new.nsf.gov/policies/pappg/23-1/ch-2-proposal-preparation#2D2i-ii) now require at least the following: + - Proposals must include a 2-page DMP outlining data types, formats, sharing, and archiving. + - The DMP must address privacy, intellectual property, and reuse policies, and collaborative projects should submit one unified DMP. + - A DMP stating no detailed plan is needed is allowed with justification, and the DMP will be reviewed as part of the proposal's merit. + +--- + +## What Classifies as Data? + +Different types of data require different management practices. Here are some examples of what we can call Data (Adapted from DMPTool [Data management general guidance](https://dmptool.org/general_guidance#types-of-data)). + +**Data Types**: + +- **Text**: field or laboratory notes, survey responses +- **Numeric**: tables, counts, measurements +- **Audiovisual**: images, sound recordings, video +- **Models, computer code** +- **Discipline-specific**: FASTA in biology, FITS in astronomy, CIF in chemistry +- **Instrument-specific**: equipment outputs + +**Data Sources**: + +*Observational* + +- Captured in real-time, typically outside the lab +- Usually irreplaceable and therefore the most important to safeguard +- Examples: Sensor readings, telemetry, survey results, images + +*Experimental* + +- Typically generated in the lab or under controlled conditions +- Often reproducible, but can be expensive or time-consuming +- Examples: gene sequences, chromatograms, magnetic field readings + +*Simulation* + +- Machine generated from test models +- Likely to be reproducible if the model and inputs are preserved +- Examples: climate models, economic models + +*Derived / Compiled* + +- Generated from existing datasets +- Reproducible, but can be very expensive and time-consuming +- Examples: text and data mining, compiled database, 3D models + +## Data Self-assessment + +!!! Question "Activity" + In small groups, discuss the following questions. + + 1. What are the two or three data types that you most frequently work with? + - Think about the sources (observational, experimental, simulated, compiled/derived) + - Also consider the formats (tabular, sequence, database, image, etc.) + 2. What is the scale of your data? + + ??? Tip "Tip: think of the [Three V's](https://www.bigdataframework.org/four-vs-of-big-data/)" + - Volume: Size of the data (MBs, GBs, TBs); can also include how many files (e.g dozens of big files, or millions of small ones) + - Velocity: How quickly are these data produced and analyzed? A lot coming in a single batch infrequently, or, a constant small amount of data that must be rapidly analyzed? + - Variety: How many different data types (raw files? databases?) + A fourth V (Veracity) captures the need to make decisions about data processing (i.e., separating low- and high-quality data) + + 3. What is your strategy for storing and backing up your data? + 4. What is your strategy for verifying the integrity of your data? (i.e. verifying that your data has not be altered) + 5. What is your strategy for searching your data? + 6. What is your strategy for sharing (and getting credit for) your data? (i.e. How will do you share with your community/clients? How is that sharing documented? How do you evaluate the impact of data shared? ) + +--- + +## The Data Life Cycle + +**Data management** is the set of practices that allow researchers to effectively and efficiently handle data throughout the data life cycle. Although typically shown as a circle (below) the actually life cycle of any data item may follow a different path, with branches and internal loops. Being aware of your data's future helps you plan how to best manage them. + +
+ ![lifecycle](assets/data_life_cycle.png) +
The Data Life Cycle, from [Strasser *et al*](https://dataoneorg.github.io/Education/bestpractices/).
+
+ +??? Info "Breaking down the Data Life Cycle Graph" + + **Plan** + + - Describe the data that will be compiled, and how the data will be managed and made accessible throughout its lifetime + - A good plan considers each of the stages below + + **Collect** + + - Have a plan for data organization in place before collecting data + - Collect and store observation metadata at the same time you collect the metadata + - Take advantage of machine generated metadata + + **Assure** + + - Record any conditions during collection that might affect the quality of the data + - Distinguish estimated values from measured values + - Double check any data entered by hand + - Perform statistical and graphical summaries (e.g., max/min, average, range) to check for questionable or impossible values. + - Mark data quality, outliers, missing values, etc. + + **Describe** + + - Comprehensive data documentation (i.e. **metadata**) is the key to future understanding of data. Without a thorough description of the context of the data, the context in which they were collected, the measurements that were made, and the quality of the data, it is unlikely that the data can be easily discovered, understood, or effectively used. + - Thoroughly describe the dataset (e.g., name of dataset, list of files, date(s) created or modified, related datasets) including the people and organizations involved in data collection (e.g., authors, affiliations, sponsor). Also include: + - An [ORCID](https://orcid.org/) (obtain one if you don't have one). + - The scientific context (reason for collecting the data, how they were collected, equipment and software used to generate the data, conditions during data collection, spatial and temporal resolution) + - The data themselves + - How each measurement was produced + - Units + - Format + - Quality assurance activities + - Precision, accuracy, and uncertainty + + Some metadata standards you may want to consider: + + - [DataCite](https://schema.datacite.org/) for publishing data + - [Dublin Core](http://www.dublincore.org/specifications/dublin-core/dcmi-terms/) for sharing data on the web + - [MIxS](https://press3.mcs.anl.gov/gensc/mixs/) Minimum Information for any (x) sequence + - [OGC standards](https://www.opengeospatial.org/docs/is) for geospatial data + + ??? tip "Ontologies provide standardization for metadata values" + + Example of ontologies: + + - [Environment Ontology](http://environmentontology.org/) terms for the MIxS standards + - [Plant Ontology](http://planteome.org/) for plant tissue types or development stages + - [FAIRSharing.org](https://fairsharing.org/) lists standards and ontologies for life sciences. + + **Preserve** + + In general, data must be preserved in an appropriate long-term archive (i.e. data center). Here are some examples: + + - Sequence data should go to a national repository, frequently [NCBI](https://www.ncbi.nlm.nih.gov/) + - Identify data with value - it may not be necessary to preserve all data from a project + - The CyVerse [Data Commons](http://datacommons.cyverse.org/) provides a place to publish and preserve data that was generated on or can be used in CyVerse, where no other repository exists. + - See lists of repositories at [FAIRSharing.org](https://fairsharing.org/) + - See lists of repositories at [Data Dryad](https://datadryad.org/stash) + - Github repos can get DOIs through [Zenodo](https://guides.github.com/activities/citable-code/) + - Be aware of licensing and other intellectual property issues + - Repositories will require some kind of license, often the least restrictive (see for example [Creative Commons](https://creativecommons.org/)) + - Repositories are unlikely to enforce reuse restrictions, even if you apply them. + + **Discover** + + - Good metadata allows you to discover your own data! + - Databases, repositories, and search indices provide ways to discover relevant data for reuse + - [Google dataset search](https://toolbox.google.com/datasetsearch) + - [DataOne](https://www.dataone.org/) + - [FAIRSharing.org](https://fairsharing.org/) + + **Integrate** + + - Data integration is a lot of work + - Standards and ontologies are key to future data integration + - Know the data before you integrate them + - Don't trust that two columns with the same header are the same data + - Properly cite the data you reuse! + - Use DOIs ([Digital Object Identifiers](https://en.wikipedia.org/wiki/Digital_object_identifier)) wherever possible + + **Analyze** + + - Follow open science principles for reproducible analyses (CyVerse, RStudio, notebooks, IDEs) + - State your hypotheses and analysis workflow before collecting data. Tools like [Open Science Framework](https://osf.io/) (OSF) allow you to make this public. + - Record all software, parameters, inputs, etc. + +??? Note "References and Resources" + + - [DataOne best practices](https://dataoneorg.github.io/Education/bestpractices/) + - [Center for Open Science](https://cos.io/) + +--- + +## Data Principles + +
+ ![fair](https://www.nlm.nih.gov/oet/ed/cde/tutorial/img/06aCDE.png){width="450"} +
FAIR data, [NIH](https://www.nlm.nih.gov/oet/ed/cde/tutorial/02-200.html).
+
+ +
+ ![care](https://swehsc.pharmacy.arizona.edu/sites/default/files/styles/az_small/public/2023-11/CARE%20Principles%20graphic.png?itok=YnCAC_JI){width="350"} +
CARE data, [University of Arizona](https://swehsc.pharmacy.arizona.edu/news/using-care-principles-preserve-indigenous-data-sovereignty).
+
+ +!!! Success "Learning Objectives" + - Recall the meaning of FAIR + - Understand why FAIR is a collection of principles (rather than rules) + - Understand CARE + +### FAIR Principles + +In 2016, the [**FAIR Guiding Principles**](https://www.nature.com/articles/sdata201618) for scientific data management and stewardship were published in Scientific Data. + +

Read it.

+ +!!! Tip "Why Principles?" + + FAIR is a collection of principles. Ultimately, different communities within different scientific disciplines must work to interpret and implement these principles. Because technologies change quickly, focusing on the desired end result allows FAIR to be applied to a variety of situations now and in the foreseeable future. + +**Findable** + +- F1. (meta)data are assigned a globally unique and persistent identifier +- F2. data are described with rich metadata (defined by R1 below) +- F3. metadata clearly and explicitly include the identifier of the data it describes +- F4. (meta)data are registered or indexed in a searchable resource + +**Accessible** + +- A1. (meta)data are retrievable by their identifier using a standardized communications protocol +- A1.1 the protocol is open, free, and universally implementable +- A1.2 the protocol allows for an authentication and authorization procedure, where necessary +- A2. metadata are accessible, even when the data are no longer available + +**Interoperable** + +- I1. (meta)data use a formal, accessible, shared, and broadly applicable language for knowledge representation. +- I2. (meta)data use vocabularies that follow FAIR principles +- I3. (meta)data include qualified references to other (meta)data + +**Reusable** + +- R1. meta(data) are richly described with a plurality of accurate and relevant attributes +- R1.1. (meta)data are released with a clear and accessible data usage license +- R1.2. (meta)data are associated with detailed provenance +- R1.3. (meta)data meet domain-relevant community standard + +!!! Tip "Open vs. Public vs. FAIR" + + Open: “Open data and content can be freely used, modified, and shared by anyone for any purpose”. + + FAIR does **NOT** demand that data be open: See one definition of open: + http://opendefinition.org/. + +### CARE Principles + +
+ +

Nihil de nobis, sine nobis.

+

Nothing about us without us.

+ +
+ +!!! question "Who owns the açaí?" + +The [CARE Principles](https://www.gida-global.org/care) for Indigenous Data Governance were drafted at the International Data Week and Research Data Alliance Plenary co-hosted event "Indigenous Data Sovereignty Principles for the Governance of Indigenous Data Workshop," 8 November 2018, Gaborone, Botswana. + +**Collective Benefit** + +- C1. For inclusive development and innovation +- C2. For improved governance and citizen engagement +- C3. For equitable outcomes + +**Authority to Control** + +- A1. Recognizing rights and interests +- A2. Data for governance +- A3. Governance of data + +**Responsibility** + +- R1. For positive relationships +- R2. For expanding capability and capacity +- R3. For Indigenous languages and worldviews + +**Ethics** + +- E1. For minimizing harm and maximizing benefit +- E2. For justice +- E3. For future use + +!!! Tip "Connecting FOSS and CARE: [Lydia Jennings](https://nativesoilnerd.com/)" + + Dr. Lydia Jennings was a Data Science Fellow at the University of Arizona, who attended FOSS in Fall of 2022. + Lydia graduated from the University of Arizona's Department of Evironemtal Sciences, and has published a paper on the application of the CARE principles to ecology and biodiversity research. + Go Lydia! + [Appying the 'CARE Principles for Indigenous Data Governance' to ecology and biodiversity](https://www.nature.com/articles/s41559-023-02161-2), *Nature Ecology & Evolution*, 2023. + +### How to get to FAIR? + +This is a question that only you can answer, that is because it depends on (among other things) + +1. Your scientific discipline: Your datatypes and existing standards for what constitutes acceptable data management will vary. +2. The extent to which your scientific community has implemented FAIR: Some disciplines have significant guidelines on FAIR, while others have not addressed the subject in any concerted way. +3. Your level of technical skills: Some approaches to implementing FAIR may require technical skills you may not yet feel comfortable with. + +While a lot is up to you, the first step is to evaluate how FAIR you think your data are: + +??? Question "Assessing the FAIRness of you data" + Thinking about a dataset you work with, complete the ARDC [FAIR assessment](https://ardc.edu.au/resource/fair-data-self-assessment-tool/) in your own time. + +??? Note "Resources" + + - [The FAIR Guiding Principles for scientific data management and stewardship](https://www.nature.com/articles/sdata201618) + - [Wilkinson et al. (2016)](https://doi.org/10.1038/sdata.2016.18) established the guidelines to improve the Findability, Accessibility, Interoperability, and Reuse (FAIR) of digital assets for research. + - [Go-FAIR website](https://www.go-fair.org/fair-principles/) + - [Carroll *et al.* (2020)](http://doi.org/10.5334/dsj-2020-043) established the CARE Principles for Indigenous Data Governance. [full document :fontawesome-solid-file-pdf:](https://static1.squarespace.com/static/5d3799de845604000199cd24/t/5da9f4479ecab221ce848fb2/1571419335217/CARE+Principles_One+Pagers+FINAL_Oct_17_2019.pdf) + - [Indigenous Data Sovereignty Networks](https://indigenousdatalab.org/networks/) + +--- + +## Data Management Plans + +

"Those who fail to plan, plan to fail."

+ +!!! Success "Learning Objectives" + - Describe the purpose of a data management plan + - Describe the important elements of a data management plan + +!!! Quote "What is a DMP?" + + "A data management plan or DMP is a formal document that outlines how data are to be handled both during a research project, and after the project is completed. The goal of a data management plan is to consider the many aspects of data management, metadata generation, data preservation, and analysis before the project begins; this may lead to data being well-managed in the present, and prepared for preservation in the future." + + Source: [Wikipedia](https://en.wikipedia.org/wiki/Data_management_plan). + +Here are some [Example DMPs](https://dmptool.org/public_plans) made public from the DMPtool website. You can use these as example for creating your own DMP. + +**Why bother with a DMP?** + +??? Question "How would you answer?" + Do you have a data management plan? If so, how do you use it? + +Returning to the assertion that data (and its value) is at the foundation of your science, working without a data management plan should be considered scientific misconduct. + +Those are strong words. And while we might have an intuition of the boundaries of research ethics - data mismanagement seems more like an annoyance than misconduct. However, if your mismanagement leads to error in your research data, or the inability to make publicly-funded research open to the public, these are serious consequences. Increasingly, funders realize this. + +
+ ![carrot&stick](https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Europe_Boardman_Robinson.jpg/800px-Europe_Boardman_Robinson.jpg){width="450"} +
"Europe 1916", by cartoonist Boardman Robinson, depicting the [carrot and stick metaphor](https://en.wikipedia.org/wiki/Carrot_and_stick).
+
+ +**Stick:** + +- [You have to make one](https://www.nsf.gov/pubs/2019/nsf19069/nsf19069.jsp). +- Reviewers definitely look at them, but they may not be enforced. + +**Carrot:** + +- Make your life easier. +- Planning for you project makes it run more smoothly. +- Avoid surprise costs. + +
+
+ + +!!! Tip "DMPTools: making your (data) life a little easier" + + Here are a couple of tools you can utilize in order to create a Data Management Plan: + + - [Data Stewardship Wizard](https://ds-wizard.org/). + - [DMPTool](https://dmptool.org/). + +--- + +## Licenses + +By default, when you make creative work, that work is under exclusive copyright. This means that you have the right to decide how your work is used, and that others must ask your permission to use your work. + +If you want your work to be Open and used by others, you need to specify how others can use your work. This is done by *licensing* your work. + +!!! Tip "License Examples" + + - [MIT License](https://choosealicense.com/licenses/mit/) + - [GNU General Public License v3.0](https://choosealicense.com/licenses/gpl-3.0/) + - FOSS material has been licensed using the [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/) + +### License Options from UArizona Library + +
+ ![open science](../assets/license_matrix.png){ width="600" } +
License options for University of [Arizona Research Data Repository (ReDATA)](https://data.library.arizona.edu/supported-platforms/redata).
+
+ +
+ +**Additional Info** + +- General guidance on how to choose a license https://choosealicense.com/ +- More good guidance on how to choose a license https://opensource.guide/legal/ +- Licensing options for your [Github Repository](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository#choosing-the-right-license){target=_blank} + +??? Note "References and Resources" + + - [NSF Guidelines on DMPs](https://www.nsf.gov/bio/biodmp.jsp) + - https://dmptool.org/general_guidance + - https://dmptool.org/public_templates + - Professional and scholarly societies, e.g., theEcological Society of America http://www.esa.org/esa/science/data-sharing/resources-and-tools/ + - DataOne - https://dataoneorg.github.io/Education/bestpractices/ + - Data Carpentry - http://datacarpentry.org/ + - The US Geological Survey https://www.usgs.gov/data-management + - Repository registry (and search) service: http://www.re3data.org/ + - Your university library + +---- + +## Self Assessment + +??? Question "What is a Data Management Plan?" + + **Important**: A data management plan (DMP) is now required aspect of publicly funded research. + + DMPs are short, formal, documents outlining what types of data will be used, and what will be done with the data both during and after a research project concludes. + +??? Question "True or False: When science project funding ends, the data should end with it" + + !!! Success "False" + + Data live on after a project ends. + + Ensuring that data have a full lifecycle where they can be (re)hosted and made available after a project ends is critical to open science and reproducible research + + !!! Danger "Maybe" + + Sometimes destroying data is part of the life cycle of data - this may be required if data are sensitive and could be used unethically in the future, beyond the control of the original investigator team. + +??? Question "True or False: FAIR and CARE data principles are the same" + + !!! Success "False" + + The CARE principles were created in order to help guide and answer when and how applying FAIR data principles to soverign indigenous-controlled data should be done and when it should not. + diff --git a/02_managing_data/index.html b/02_managing_data/index.html new file mode 100644 index 000000000..c8616e830 --- /dev/null +++ b/02_managing_data/index.html @@ -0,0 +1,1783 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2. Data Management - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Managing Data

+
+

Learning Objectives

+

After this lesson, you should be able to:

+
    +
  • Recognize data as the foundation of open science and be able to describe the "life cycle of data"
  • +
  • Use self-assessments to evaluate your current data management practices
  • +
  • Cite tools and resources to improve your data management practices
  • +
  • Know the biggest challenge to effective data management
  • +
+
+
+How would you answer? +
    +
  • If you give your data to a colleague who has not been involved with your project, would they be able to make sense of it? Would they be able to use it properly?
  • +
  • If you come back to your own data in five years, will you be able to make sense of it? Will you be able to use it properly?
  • +
  • When you are ready to publish a paper, is it easy to find all the correct versions of all the data you used and present them in a comprehensible manner?
  • +
+
+

Why should you care about data management?

+
+

The biggest challenge to data management is making it an afterthought.

+
+

Poor data management doesn't have an upfront cost. You can do substantial work before realizing you are in trouble.

+

The solution? Make data management the first thing you consider when starting a research project.

+

Well-managed Data Sets:

+
    +
  • Can make life much easier for you and your collaborators
  • +
  • Benefit the scientific research community by allowing others to reuse your data
  • +
  • Are becoming required by most funders and many journals, which are requesting a submission of a Data Management Plan (DMP) with the initial submission of your proposal.
  • +
+
+

The NSF is stepping in, getting stricter about data

+
    +
  • Recent Dear Colleague letterfrom NSF's points out that:
      +
    • Open science promotes broader access to research data, enhancing public benefits and replicability.
    • +
    • NSF requires (DMPs) in proposals, encouraging use of persistent IDs and machine-readable DMPs.
    • +
    +
  • +
  • NSF proposal preparation guidelines now require at least the following:
      +
    • Proposals must include a 2-page DMP outlining data types, formats, sharing, and archiving.
    • +
    • The DMP must address privacy, intellectual property, and reuse policies, and collaborative projects should submit one unified DMP.
    • +
    • A DMP stating no detailed plan is needed is allowed with justification, and the DMP will be reviewed as part of the proposal's merit.
    • +
    +
  • +
+
+
+

What Classifies as Data?

+

Different types of data require different management practices. Here are some examples of what we can call Data (Adapted from DMPTool Data management general guidance).

+

Data Types:

+
    +
  • Text: field or laboratory notes, survey responses
  • +
  • Numeric: tables, counts, measurements
  • +
  • Audiovisual: images, sound recordings, video
  • +
  • Models, computer code
  • +
  • Discipline-specific: FASTA in biology, FITS in astronomy, CIF in chemistry
  • +
  • Instrument-specific: equipment outputs
  • +
+

Data Sources:

+

Observational

+
    +
  • Captured in real-time, typically outside the lab
  • +
  • Usually irreplaceable and therefore the most important to safeguard
  • +
  • Examples: Sensor readings, telemetry, survey results, images
  • +
+

Experimental

+
    +
  • Typically generated in the lab or under controlled conditions
  • +
  • Often reproducible, but can be expensive or time-consuming
  • +
  • Examples: gene sequences, chromatograms, magnetic field readings
  • +
+

Simulation

+
    +
  • Machine generated from test models
  • +
  • Likely to be reproducible if the model and inputs are preserved
  • +
  • Examples: climate models, economic models
  • +
+

Derived / Compiled

+
    +
  • Generated from existing datasets
  • +
  • Reproducible, but can be very expensive and time-consuming
  • +
  • Examples: text and data mining, compiled database, 3D models
  • +
+

Data Self-assessment

+
+

Activity

+

In small groups, discuss the following questions.

+
    +
  1. What are the two or three data types that you most frequently work with? + - Think about the sources (observational, experimental, simulated, compiled/derived) + - Also consider the formats (tabular, sequence, database, image, etc.)
  2. +
  3. +

    What is the scale of your data?

    +
    +Tip: think of the Three V's +
      +
    • Volume: Size of the data (MBs, GBs, TBs); can also include how many files (e.g dozens of big files, or millions of small ones)
    • +
    • Velocity: How quickly are these data produced and analyzed? A lot coming in a single batch infrequently, or, a constant small amount of data that must be rapidly analyzed?
    • +
    • Variety: How many different data types (raw files? databases?) +A fourth V (Veracity) captures the need to make decisions about data processing (i.e., separating low- and high-quality data)
    • +
    +
    +
  4. +
  5. +

    What is your strategy for storing and backing up your data?

    +
  6. +
  7. What is your strategy for verifying the integrity of your data? (i.e. verifying that your data has not be altered)
  8. +
  9. What is your strategy for searching your data?
  10. +
  11. What is your strategy for sharing (and getting credit for) your data? (i.e. How will do you share with your community/clients? How is that sharing documented? How do you evaluate the impact of data shared? )
  12. +
+
+
+

The Data Life Cycle

+

Data management is the set of practices that allow researchers to effectively and efficiently handle data throughout the data life cycle. Although typically shown as a circle (below) the actually life cycle of any data item may follow a different path, with branches and internal loops. Being aware of your data's future helps you plan how to best manage them.

+
+ lifecycle +
The Data Life Cycle, from Strasser et al.
+
+
+Breaking down the Data Life Cycle Graph +

Plan

+
    +
  • Describe the data that will be compiled, and how the data will be managed and made accessible throughout its lifetime
  • +
  • A good plan considers each of the stages below
  • +
+

Collect

+
    +
  • Have a plan for data organization in place before collecting data
  • +
  • Collect and store observation metadata at the same time you collect the metadata
  • +
  • Take advantage of machine generated metadata
  • +
+

Assure

+
    +
  • Record any conditions during collection that might affect the quality of the data
  • +
  • Distinguish estimated values from measured values
  • +
  • Double check any data entered by hand
  • +
  • Perform statistical and graphical summaries (e.g., max/min, average, range) to check for questionable or impossible values.
  • +
  • Mark data quality, outliers, missing values, etc.
  • +
+

Describe

+
    +
  • Comprehensive data documentation (i.e. metadata) is the key to future understanding of data. Without a thorough description of the context of the data, the context in which they were collected, the measurements that were made, and the quality of the data, it is unlikely that the data can be easily discovered, understood, or effectively used.
  • +
  • Thoroughly describe the dataset (e.g., name of dataset, list of files, date(s) created or modified, related datasets) including the people and organizations involved in data collection (e.g., authors, affiliations, sponsor). Also include:
      +
    • An ORCID (obtain one if you don't have one).
    • +
    • The scientific context (reason for collecting the data, how they were collected, equipment and software used to generate the data, conditions during data collection, spatial and temporal resolution)
    • +
    • The data themselves
    • +
    • How each measurement was produced
    • +
    • Units
    • +
    • Format
    • +
    • Quality assurance activities
    • +
    • Precision, accuracy, and uncertainty
    • +
    +
  • +
+

Some metadata standards you may want to consider:

+ +
+Ontologies provide standardization for metadata values +

Example of ontologies:

+ +
+

Preserve

+

In general, data must be preserved in an appropriate long-term archive (i.e. data center). Here are some examples:

+
    +
  • Sequence data should go to a national repository, frequently NCBI
  • +
  • Identify data with value - it may not be necessary to preserve all data from a project
  • +
  • The CyVerse Data Commons provides a place to publish and preserve data that was generated on or can be used in CyVerse, where no other repository exists.
  • +
  • See lists of repositories at FAIRSharing.org
  • +
  • See lists of repositories at Data Dryad
  • +
  • Github repos can get DOIs through Zenodo
  • +
  • Be aware of licensing and other intellectual property issues
      +
    • Repositories will require some kind of license, often the least restrictive (see for example Creative Commons)
    • +
    • Repositories are unlikely to enforce reuse restrictions, even if you apply them.
    • +
    +
  • +
+

Discover

+ +

Integrate

+
    +
  • Data integration is a lot of work
  • +
  • Standards and ontologies are key to future data integration
  • +
  • Know the data before you integrate them
  • +
  • Don't trust that two columns with the same header are the same data
  • +
  • Properly cite the data you reuse!
  • +
  • Use DOIs (Digital Object Identifiers) wherever possible
  • +
+

Analyze

+
    +
  • Follow open science principles for reproducible analyses (CyVerse, RStudio, notebooks, IDEs)
  • +
  • State your hypotheses and analysis workflow before collecting data. Tools like Open Science Framework (OSF) allow you to make this public.
  • +
  • Record all software, parameters, inputs, etc.
  • +
+
+
+References and Resources + +
+
+

Data Principles

+
+ fair +
FAIR data, NIH.
+
+
+ care +
CARE data, University of Arizona.
+
+
+

Learning Objectives

+
    +
  • Recall the meaning of FAIR
  • +
  • Understand why FAIR is a collection of principles (rather than rules)
  • +
  • Understand CARE
  • +
+
+

FAIR Principles

+

In 2016, the FAIR Guiding Principles for scientific data management and stewardship were published in Scientific Data.

+

Read it.

+ +
+

Why Principles?

+

FAIR is a collection of principles. Ultimately, different communities within different scientific disciplines must work to interpret and implement these principles. Because technologies change quickly, focusing on the desired end result allows FAIR to be applied to a variety of situations now and in the foreseeable future.

+
+

Findable

+
    +
  • F1. (meta)data are assigned a globally unique and persistent identifier
  • +
  • F2. data are described with rich metadata (defined by R1 below)
  • +
  • F3. metadata clearly and explicitly include the identifier of the data it describes
  • +
  • F4. (meta)data are registered or indexed in a searchable resource
  • +
+

Accessible

+
    +
  • A1. (meta)data are retrievable by their identifier using a standardized communications protocol
  • +
  • A1.1 the protocol is open, free, and universally implementable
  • +
  • A1.2 the protocol allows for an authentication and authorization procedure, where necessary
  • +
  • A2. metadata are accessible, even when the data are no longer available
  • +
+

Interoperable

+
    +
  • I1. (meta)data use a formal, accessible, shared, and broadly applicable language for knowledge representation.
  • +
  • I2. (meta)data use vocabularies that follow FAIR principles
  • +
  • I3. (meta)data include qualified references to other (meta)data
  • +
+

Reusable

+
    +
  • R1. meta(data) are richly described with a plurality of accurate and relevant attributes
  • +
  • R1.1. (meta)data are released with a clear and accessible data usage license
  • +
  • R1.2. (meta)data are associated with detailed provenance
  • +
  • R1.3. (meta)data meet domain-relevant community standard
  • +
+
+

Open vs. Public vs. FAIR

+

Open: “Open data and content can be freely used, modified, and shared by anyone for any purpose”.

+

FAIR does NOT demand that data be open: See one definition of open: +http://opendefinition.org/.

+
+

CARE Principles

+


+

Nihil de nobis, sine nobis.

+

Nothing about us without us.

+ +


+
+

Who owns the açaí?

+
+

The CARE Principles for Indigenous Data Governance were drafted at the International Data Week and Research Data Alliance Plenary co-hosted event "Indigenous Data Sovereignty Principles for the Governance of Indigenous Data Workshop," 8 November 2018, Gaborone, Botswana.

+

Collective Benefit

+
    +
  • C1. For inclusive development and innovation
  • +
  • C2. For improved governance and citizen engagement
  • +
  • C3. For equitable outcomes
  • +
+

Authority to Control

+
    +
  • A1. Recognizing rights and interests
  • +
  • A2. Data for governance
  • +
  • A3. Governance of data
  • +
+

Responsibility

+
    +
  • R1. For positive relationships
  • +
  • R2. For expanding capability and capacity
  • +
  • R3. For Indigenous languages and worldviews
  • +
+

Ethics

+
    +
  • E1. For minimizing harm and maximizing benefit
  • +
  • E2. For justice
  • +
  • E3. For future use
  • +
+
+

Connecting FOSS and CARE: Lydia Jennings

+

Dr. Lydia Jennings was a Data Science Fellow at the University of Arizona, who attended FOSS in Fall of 2022.
+Lydia graduated from the University of Arizona's Department of Evironemtal Sciences, and has published a paper on the application of the CARE principles to ecology and biodiversity research. +Go Lydia! +Appying the 'CARE Principles for Indigenous Data Governance' to ecology and biodiversity, Nature Ecology & Evolution, 2023.

+
+

How to get to FAIR?

+

This is a question that only you can answer, that is because it depends on (among other things)

+
    +
  1. Your scientific discipline: Your datatypes and existing standards for what constitutes acceptable data management will vary.
  2. +
  3. The extent to which your scientific community has implemented FAIR: Some disciplines have significant guidelines on FAIR, while others have not addressed the subject in any concerted way.
  4. +
  5. Your level of technical skills: Some approaches to implementing FAIR may require technical skills you may not yet feel comfortable with.
  6. +
+

While a lot is up to you, the first step is to evaluate how FAIR you think your data are:

+
+Assessing the FAIRness of you data +

Thinking about a dataset you work with, complete the ARDC FAIR assessment in your own time.

+
+
+Resources + +
+
+

Data Management Plans

+

"Those who fail to plan, plan to fail."

+ +
+

Learning Objectives

+
    +
  • Describe the purpose of a data management plan
  • +
  • Describe the important elements of a data management plan
  • +
+
+
+

What is a DMP?

+

"A data management plan or DMP is a formal document that outlines how data are to be handled both during a research project, and after the project is completed. The goal of a data management plan is to consider the many aspects of data management, metadata generation, data preservation, and analysis before the project begins; this may lead to data being well-managed in the present, and prepared for preservation in the future."

+

Source: Wikipedia.

+
+

Here are some Example DMPs made public from the DMPtool website. You can use these as example for creating your own DMP.

+

Why bother with a DMP?

+
+How would you answer? +

Do you have a data management plan? If so, how do you use it?

+
+

Returning to the assertion that data (and its value) is at the foundation of your science, working without a data management plan should be considered scientific misconduct.

+

Those are strong words. And while we might have an intuition of the boundaries of research ethics - data mismanagement seems more like an annoyance than misconduct. However, if your mismanagement leads to error in your research data, or the inability to make publicly-funded research open to the public, these are serious consequences. Increasingly, funders realize this.

+
+ carrot&stick +
"Europe 1916", by cartoonist Boardman Robinson, depicting the carrot and stick metaphor.
+
+

Stick:

+ +

Carrot:

+
    +
  • Make your life easier.
  • +
  • Planning for you project makes it run more smoothly.
  • +
  • Avoid surprise costs.
  • +
+


+

+
+

DMPTools: making your (data) life a little easier

+

Here are a couple of tools you can utilize in order to create a Data Management Plan:

+ +
+
+

Licenses

+

By default, when you make creative work, that work is under exclusive copyright. This means that you have the right to decide how your work is used, and that others must ask your permission to use your work.

+

If you want your work to be Open and used by others, you need to specify how others can use your work. This is done by licensing your work.

+
+

License Examples

+ +
+

License Options from UArizona Library

+
+

open science +

License options for University of Arizona Research Data Repository (ReDATA).

+
+


+

Additional Info

+ +
+References and Resources + +
+
+

Self Assessment

+
+What is a Data Management Plan? +

Important: A data management plan (DMP) is now required aspect of publicly funded research.

+

DMPs are short, formal, documents outlining what types of data will be used, and what will be done with the data both during and after a research project concludes.

+
+
+True or False: When science project funding ends, the data should end with it +
+

False

+

Data live on after a project ends.

+

Ensuring that data have a full lifecycle where they can be (re)hosted and made available after a project ends is critical to open science and reproducible research

+
+
+

Maybe

+

Sometimes destroying data is part of the life cycle of data - this may be required if data are sensitive and could be used unethically in the future, beyond the control of the original investigator team.

+
+
+
+True or False: FAIR and CARE data principles are the same +
+

False

+

The CARE principles were created in order to help guide and answer when and how applying FAIR data principles to soverign indigenous-controlled data should be done and when it should not.

+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/03_documentation_communication/03_documentation_communication.md b/03_documentation_communication/03_documentation_communication.md new file mode 100644 index 000000000..13e083471 --- /dev/null +++ b/03_documentation_communication/03_documentation_communication.md @@ -0,0 +1,650 @@ +# :material-file-document-multiple: Documentation & :material-antenna: Communication + +!!! Success "Learning Objectives" + + After this lesson, you should be able to: + + * Identify and explain different types of project documentation (both internal and external) + * Describe tools and approaches to creating your own documentation + * Describe best practices for maintaining documentation + * Create your own GitHub Pages website (!) + + +## :material-file-document-multiple: Project Documentation + +Documentation is the practice of recording, preserving, and organizing information, data, or details in a structured and systematic manner. Documentation is also essential to communicate with your future self, your collaborators, or the world on specific ideas and information. Effective documentation must take into consideration the following points: + +- **:material-glasses: Clarity**: Documentation should be easy to understand with clear language and no ambiguity. +- **:octicons-circle-16: Completeness**: It must cover all essential details, leaving nothing crucial undocumented. +- **:fontawesome-solid-bullseye: Accuracy**: Information should be up-to-date and correct to prevent errors and misunderstandings. +- **:simple-instructure: Organization**: A logical structure and clear organization make it easy to navigate and find information. +- **:fontawesome-solid-exclamation: Relevance**: Documentation should focus on what's pertinent to its intended audience or purpose, avoiding unnecessary information. + + +Not all documentation is the same. **The documentation system**, by Divio, categorizes the different types of documentation into 4 quadrants: + +
+ ![xkcd](https://documentation.divio.com/_images/overview.png) +
Read more in depth on the documentation system here: https://documentation.divio.com
+
+ +??? Question "Explanining the quadrants" + + - **Tutorials**: Lessons! Tutorials are lessons that take the reader by the hand to understand how the basics of a tool work. They are what your project needs in order to show a beginner that they can achieve something with it. The techical teaching we do in FOSS are mostly tutorials. For example, we do simple tutorials to teach the mechanics of version control. + - **How-to-guides**: Recipes! How-to-guides take the reader through the steps required to acheive a specific outcome or answer a specific question. An example how-to-guide could be a guide on how to install a specific software on a specific operating system. + - **References**: References offer technical descriptions of the machinery and how to operate it. References have one job only: to describe. They are code-determined, because ultimately that’s what they describe: key classes, functions, APIs, and so they should list things like functions, fields, attributes and methods, and set out how to use them. + - **Explanation**: Discussions! The aims of explanations are to clarify and illuminate a particular topic by broadening the documentation’s coverage of a topic. + + + +### Public Repositories for Documentation + + + + +*:simple-github: GitHub* + +- On Github, good documentation starts with a robust ReadMe file. The ReadMe file is the first thing that people see when they visit your repository. It is a good place to explain what your project does, how to use it, and how to contribute to it. Here is an [example](https://github.com/jeffgillan/geospatial_pipeline). +- Also on Github, you can use the Wiki feature to create a separate space for documentation. The Wiki is a place to document your project in a way that is separate from the code. Here is an [example](https://github.com/ua-datalab/Geospatial_Workshops/wiki) + +*:simple-github: GitHub Pages* + +- You can pull templates from other GitHub users for your website, + e.g. [:simple-jekyll: Jekyll themes](http://themes.jekyllrc.org/){target=_blank} +- GitHub pages are free, fast, and easy to build, but limited in use + of subdomain or URLs. +- The FOSS website is rendered using [:simple-github: GitHub Pages](https://pages.github.com/){target=_blank} using [:simple-markdown: MkDocs](https://www.mkdocs.org/){target=_blank} and the [Material](https://squidfunk.github.io/mkdocs-material/){target=_blank} theme for MkDocs. +- Other popular website generator for GitHub Pages is [:simple-bootstrap: Bootstrap.js](https://getbootstrap.com/){target=_blank}. + +*:simple-markdown: Material MkDocs* + +- [Material Design](https://squidfunk.github.io/mkdocs-material/){target=_blank} theme for MkDocs, a static site generator geared towards (technical) project documentation. +- publish via GitHub Actions +- Uses open source Material or ReadTheDocs Themes + +*:simple-readthedocs: ReadTheDocs* + +- publishing websites via + [ReadTheDocs.com](https://readthedocs.com/dashboard/){target=_blank} costs money. +- You can work in an offline state, where you develop the materials + and publish them to your localhost using + [Sphinx](https://docs.readthedocs.io/en/stable/intro/getting-started-with-sphinx.html){target=_blank} +- You can work on a website template in a GitHub repository, and + pushes are updated in near real time using ReadTheDocs.com. +- Here is example documentation of Pytorch using ReadTheDocs: [PyTorch](https://pytorch.org/docs/stable/index.html){target=_blank}. + + +*:material-book-arrow-down: Bookdown* + +- [:material-book-arrow-down: Bookdown](https://bookdown.org/){target=_blank} is an open-source R package that facilitates writing books and long-form articles/reports with R Markdown. +- Bookdown websites can be hosted by [RStudio + Connect](https://www.rstudio.com/products/connect/){target=_blank} +- You can publish a Bookdown website using [Github + Pages](https://github.blog/2016-08-17-simpler-github-pages-publishing/){target=_blank} + + +*:simple-r: Quarto* + +- [:simple-r: Quarto](https://quarto.org/){target=_blank} is an open-source scientific and technical publishing system built on Pandoc +- [Build a website](https://quarto.org/docs/websites/) using Quarto's template builder +- [Build with Github Pages](https://quarto.org/docs/publishing/github-pages.html) + +*:simple-jupyter: JupyterBook* + +- [Based on Project Jupyter](https://jupyterbook.org/en/stable/start/overview.html){target=_blank} `ipynb` and MarkDown +- Uses `conda` package management + +*:simple-git: GitBook* + +- [GitBook](https://docs.gitbook.com/){target=_blank} websites use MarkDown syntax +- Free for open source projects, paid plans are available + +*:simple-confluence: Confluence Wikis* + +- [:simple-confluence: Confluence Wikis](https://www.atlassian.com/software/confluence/use-cases/wiki){target=_blank} are another tool for documenting your work. You can see an example from [Cyverse](https://wiki.cyverse.org){target=_blank}. + +!!! Quote "Things to remember about Documentation" + + - Documentation should be written in such a way that people who did not write the documentation can read and then use or read and then teach others in the applications of the material. + + - Documentation is best treated as a living document, but version control is necessary to maintain it + + - Technology changes over time, expect to refresh documentation every 3-5 years as your projects age and progress. + +### Websites to Host Methods & Protocols + +[Open Science Framework](https://osf.io/){target=_blank} for free. OSF can be directly linked to your ORCID. + +- Integrated project management tools +- Uses templates to create a project website +- Can publish preprints from within project management tools + +[Protocols.io](https://www.protocols.io/){target=_blank} - collaborative platform and preprint server for: science methods, computational workflows, clinical trials, operational procedures, safety checklists, and instructions / manuals. + +[QUBES](https://qubeshub.org/){target=_blank} - community of math and biology educators who share resources and methods for preparing students to tackle real, complex, biological problems. + +??? Question "What are the benefits of using a GitHub.io website?" + + [Github Pages](https://pages.github.com/) are hosted directly from your GitHub repository. + + Just edit, push, and your changes are live. + + You do not need to run your own web server!! + +--- + +## :material-antenna: Communication + +### Internal Project + +Choosing which software to use for your internal lab communication can be complicated by the cost of setting up, the cost of maintaining, and simply by the sheer number of platforms that are out there. + +For this workshop, we use [:simple-slack: SLACK](https://slack.com/){target=_blank} (Searchable Log of All Conversation & Knowledge). Microsoft's competitor to SLACK is [:material-microsoft-teams: Microsoft Teams](https://teams.microsoft.com/start){target=_blank}. + +Remember, the intention of these platforms are to **improve productivity** & not become a distraction. + +**:simple-slack: SLACK** + +- Slack has [plenty of apps](https://slack.com/apps){target=_blank} for coordinating + multiple services, i.e. Calendars, Github, GoogleDrive, Box, etc. +- Free Slack is limiting (e.g., 90 day history; limited connections across workspaces). +- Paid Slack is $7.25 per user per month. (10 users for 1 year = $870) + +**:material-microsoft-teams: Microsoft Teams** + +- Teams is used by many R1 research universities as part of their + campus wide license agreement for Office 365 Business and Education +- For example, anyone with a `arizona.edu` email address can use Teams for free +- Limitations: + - Not sure you can create your own Teams + - Limited to messaging with people in your university Team + +*Other popular alternatives* + +- [:fontawesome-brands-bandcamp: BaseCamp](https://basecamp.com/){target=_blank} +- [:simple-discord: Discord](https://discordapp.com/){target=_blank} +- [:simple-mastodon: Mastodon](https://joinmastodon.org/){target=_blank} +- [:simple-mattermost: Mattermost](https://mattermost.com/){target=_blank} + +!!! Info "Useful links for creating a SLACK workspace" + 1. [Create a new Workspace](https://get.slack.help/hc/en-us/articles/206845317-Create-a-Slack-workspace){target=_blank} + 2. [Create channels, add apps & tools](https://get.slack.help/hc/en-us/articles/217626298-tips-for-team-creators-and-admins){target=_blank} + +--- + +### External (Public) + +Communicating with the public and other members of your science community (in addition to traditional peer-review publications and conferences) is one of the most important parts of your science! + +There are many ways scientists use social media and the web to share their data science ideas: + +1. [:simple-twitter: "Science Twitter" (now X)](https://www.sciencemag.org/news/2018/08/scientists-do-you-want-succeed-twitter-here-s-how-many-followers-you-need){target=_blank} - + is really just regular [Twitter](https://twitter.com/hashtag/science?lang=en){target=_blank}, but with a focus on following other scientists and organizations, and tweeting about research you're interested in. By building up a significant following, more people will know you, know about your work, and you'll have a higher likelihood of meeting other new collaborators. +2. Blogging Platforms such as [Medium](https://medium.com/){target=_blank} are a great place to self publish your writing on just about any topic. It's free to sign up and start blogging, but does have a fee for accessing premium content. Some of my favorite blogs include [Toward Data Science](https://towardsdatascience.com/){target=_blank} and [Chris Holmes](https://medium.com/@cholmes){target=_blank}. +3. Community groups - There are lists (and lists of lists) of [nationals research organizations](https://www.google.com/search?q=list+of+professional+science+organizations){target=_blank}, in which a researcher can become involved. These older organziations + still rely on official websites, science journal blogs, and email lists to communicate with their members. In the earth sciences there are open groups which focus on communication like the [Earth Science Information Partners (ESIP)](https://www.esipfed.org/){target=_blank} with progressive ideas about how data and science can be done. Other groups, like [The Carpentries](https://carpentries.org/){target=_blank} and [Research Bazaar](https://resbazblog.wordpress.com/about/){target=_blank} are focused on data science training and digital literacy. +4. Podcasts - Creating and distributing audio content to masses is easier than ever before. There are many podcast hosting platforms including [Spotify](https://podcasters.spotify.com/){target=_blank}, [Podbean](https://www.podbean.com/){target=_blank}, [Acast](https://www.acast.com/){target=_blank}, and [Libsyn](https://libsyn.com/){target=_blank}. From there is it simple to make your podcast availble in the [Google Podcast](https://rss.com/blog/how-to-submit-podcast-to-google-podcasts/){target=_blank} app or [Apple Podcast](https://transistor.fm/upload-podcast-itunes-apple/){target=_blank} app. +5. Webinars - With platforms such as [Zoom](https://zoom.us/){target=_blank}, [Microsoft Teams](https://www.microsoft.com/en-us/microsoft-teams/group-chat-software){target=_blank}, and [Google Meet](https://meet.google.com/){target=_blank}, it is so easy nowadays to host a webinar touting and explaining your science. +6. Youtube - The king of video sharing platforms is a great place to post content promoting your science (and yourself!). For example, [Cyverse](https://www.youtube.com/@CyverseOrgProject){target=_blank} posts lots of content on cyberinfrastructure and data processing pipelines. Some of my favorite podcasts hosted on Youtube include [StarTalk](https://www.youtube.com/@StarTalk){target=_blank} and [Lex Fridman](https://www.youtube.com/@lexfridman){target=_blank}. + +!!! Warning "Important" + **Remember: Personal and Professional Accounts are Not Isolated** + + You decide what you post on the internet. Your scientist identity may be + a part of your personal identity on social media, it might be separate. + A future employer or current employer can see your old posts. What you + post in your personal accounts can be considered a reflection of the + organization you work for and may be used in decisions about hiring or + dismissal. + +--- + +## Hands-on: Building a GitHub Pages Website using MkDocs + +This section is built in order to educate on and simplify the steps necessary that newcomers need to take in order to build a successful GitHub Pages hosted website. + + This tutorial is inspired by [academicpages](https://academicpages.github.io/), a Jekyll themed template created in order to help scientists and academics build their own websites. + +The easy way would be to fork/import the [foss-reference-hub website](https://cyverse-learning-materials.github.io/foss-reference-hub/) ([repository](https://github.com/CyVerse-learning-materials/foss-reference-hub)) and modify it to reflect your requirements; this tutorial will cover the necessary files and repository structure you require in order to build a successful personal website. + +!!! info "Repository Explanation" + + A GitHub hosted website running the [MkDocs-material](https://squidfunk.github.io/mkdocs-material/getting-started/) theme requires the following files in order to function: + + - A `docs` folder: + - A folder that contains all the documents necessary to populate the website's pages. + - **All of the documents that the user needs to change are in here**. + - A `mkdocs.yml` file: + - A `yml` file which contains critical information on the website structure, including themes, fonts, and extensions. + - A `requirements.txt` file: + - A file with a list of software necessary to build the website, primilily used by GitHub Actions. + - A `.github/workflow` folder: + - Contains the `ghpages.yml` file that controls the GitHub Action. + + The structure of the basic repository is the following: + + ``` + . + ├── README.md + ├── mkdocs.yml <- Governing file for website building + ├── requirements.txt <- Requirements file for pip installation (required by website) + ├── docs + │ ├── assets <- Folder for images and additional graphic assets + │ └── index.md <- Main website home page + └── .github + └── workflows + └── ghpages.yml <- GitHub Actions controlling file + ``` + + Upon pushing changes, a `gh-pages` branch will be automatically created by the GitHub Action; it is where the website is rendered from. + + +### Directions A: forking or importing an existing repo + +!!! warning "Prerequisites" + You will require the following in case you want to add code locally. + + ??? Info "Create a GitHub account" + Navigate to the [GitHub website](https://github.com/) and click *Sign Up*, and follow the on screen instructions. + + Additionally, you can choose between Generating a Personal Access Token or using SSH keys. This is useful if you want to work locally and push your changes to GitHub. We are going to cover this further in next week's lesson on [Version Control](05_version_control.md). + + ??? Info "Choice A: Generate a Personal Access Token" + You can follow the official documentation on how to generate Tokens [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). We discussed how to generate tokens in [Week 0](https://foss.cyverse.org/00_basics/#adding-code-locally). Here's are quick steps you can follow in order to setup your account on your machine using tokens: + + 1. On your coumputer: + 1. Clone your repository (`git clone `) + 2. Make changes where necessary, and **add** (`git add `), **commit** (`git commit -m ""`) and **push** your changes (`git push origin`). + 3. You should be prompted to logging in your GitHub account. Put your email **but not your password**. Instead, open your web browser and follow the steps below: + 2. On GitHub: + 1. Navigate to your GitHub Settings (You can access your account Settings from the drop down menu where your account icon is, on the top right of the screen) + 2. Scroll to the bottom of the left hand side menu to find *Developer settings* and open it. + 3. Click *Personal access tokens* > *Tokens (classic)* + 4. Click *Generate new token* > *Generate new token (classic)*. You might need to input your Authentification code if you have enabled 2FA. + 5. Give it a name, and all the scopes you require (tip: select all scopes and *No Expiration*), then click *Generate Token*. **Copy the new generated Token** + 3. Back on your computer: + 1. If you have been following the steps above, you should still be in your shell with GitHub still asking for your password. + 2. **Paste** your Token here, and you should be logging in. Your changes should then be saved to GitHub. + + ??? Info "Choice B: Connecting via SSH" + The process of connecting your computer to GitHub using an SSH key is more expedited (and probably less confusing). + + As a setup step, see if your computer is already connected to GitHub by doing `ssh -T git@github.com`. If the response message is `git@github.com: Permission denied (publickey).` it signifies that your computer is not yet linked with GitHub. To link your computer to github to the following: + + 1. Generate an SSH key with a level of encryption that you prefer: `ssh-keygen -t ed25519 -C `. This command generates an SSH key with [ed25519](https://ed25519.cr.yp.to/) encryption (harder to crack!) and adds your email as "comment" (`-C`, will help recongizing the user adding the key). A number of additional questions are going to ask you where you'd like to save the key and whether you'd like to add a password for protection; unless you want to save it elsewhere, feel free to use the default options. Upon completion you should see something like this: + ``` + Your identification has been saved in /c/Users//.ssh/id_ed25519 + Your public key has been saved in /c/Users//.ssh/id_ed25519.pub + The key fingerprint is: + SHA256:SMSPIStNyA00KPxuYu94KpZgRAYjgt9g4BA4kFy3g1o + The key's randomart image is: + +--[ED25519 256]--+ + |^B== o. | + |%*=.*.+ | + |+=.E =.+ | + | .=.+.o.. | + |.... . S | + |.+ o | + |+ = | + |.o.o | + |oo+. | + +----[SHA256]-----+ + ``` + 2. Upon generating the ssh key, copy it. You can reveal it by doing `cat ~/.ssh/id_ed25519.pub`. + 3. In GitHub, go to your settings: click your account icon on top right, and from the drop down menu, select *Settings* and then *SSH and GPG keys*. Here, click on *New SSH Key*, where you can then paste the newly geneated key. Add a name reflecting your machine and save changes. + + Optional: if you want to check if you successfully linked your computer to GitHub, do `ssh -t git@github.com`. You should receive the following message: `Hi ! You've successfully authenticated, but GitHub does not provide shell access. + +1. [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) or [import](https://docs.github.com/en/get-started/importing-your-projects-to-github/importing-source-code-to-github/importing-a-repository-with-github-importer) the [FOSS Reference Hub website tutorial repository branch](https://github.com/CyVerse-learning-materials/foss-reference-hub/tree/week-4-pre) + - Forking or importing will allow you to have your own copy of a specific repository; Cloning a repository **without** forking/importing it first, will lead to the changes being applied to the original repository and not your own copy. You should clone your forked or imported repository, not the original! +2. Navigate to *Settings* > *Actions* > *General*: + - Under *Action Permissions* select *Allow all actions and reusalbe workflows* + - Under *Workflow permissions* select *Read and write permissions* and *Allow GitHub Actions to create and approve pull requests* +3. Edit the `mkdocs.yml` and push your changes + - The first changes you should be making are in the first few lines in the `mkdocs.yml` file in order to reflect your necessities: + - Line 1: `site_name:` change to any title you want for your website + - Line 2: `site_description:` give a short description of the website + - Line 3: `site_author: ` who you are + - Line 4: `site_url:` change it to the URL reflected in *Settings*, which will most likely be `https:///` + - Line 7: `repo_name: ` give the name of your repository (e.g., `academicpages-mkdocs` in this case) + - Line 8: `repo_url:` give the git repository URL + - Line 11: `copyright:` change `your name` to the maintainer of the website (likely to be you) + !!! warning "Workflow expectations" + The previos changes *should* trigger the GitHub action workflow, which is setup to apply changes to the website every time a commit is pushed. One of the first thing that `mkdocs-material` will do, is to create the `gh-pages` branch (in case you do not have it already). **The workflow will fail because the `ghpages.yml` in the `.github/workflows` directory is disabled (["commented out"](https://en.wiktionary.org/wiki/comment_out))**. To enable it, remove the `#` at the beginnig on each line and commit your changes. Upon changes, the workflow should go ahead and create the `gh-pages` branch. +4. Navigate to *Settings* > *Pages* and make sure that *Source* is *Deploy from a branch* and Branch is *gh-pages*, */(root)* + - You should be able to access your website at `https://.github.io/`. If you cannot find your website, go to the repository's settings page and navigate to *Pages*: your website address will be there. +5. Edit documents as necessary. + - Don't forget to **add**, **commit** and **push** changes! + - Changes will only be visible on the website after a successful push. + - After each push, next to the commit identifier GitHub will show either a yellow circle (:yellow_circle:, meaning building), green check (:material-check:, meaning success), or red cross (:x:, meaning failure). + ??? Tip "Failure? Try again!" + If you've been given the red cross :x:, GitHub will notify you with what went wrong. By clicking on the :x:, GitHub will open up a new page showing you the broken process. + +### Directions B: Creating your own + +!!! warning "Prerequisites" + You will require the following in case you want to add code locally. + + ??? Info "Create a GitHub account" + Navigate to the [GitHub website](https://github.com/) and click *Sign Up*, and follow the on screen instructions. + + Additionally, you can choose between Generating a Personal Access Token or using SSH keys. This is useful if you want to work locally and push your changes to GitHub. We are going to cover this further in next week's lesson on [Version Control](05_version_control.md). + + ??? Info "Choice A: Generate a Personal Access Token" + You can follow the official documentation on how to generate Tokens [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). We discussed how to generate tokens in [Week 0](https://foss.cyverse.org/00_basics/#adding-code-locally). Here's are quick steps you can follow in order to setup your account on your machine using tokens: + + 1. On your coumputer: + 1. Clone your repository (`git clone `) + 2. Make changes where necessary, and **add** (`git add `), **commit** (`git commit -m ""`) and **push** your changes (`git push origin`). + 3. You should be prompted to logging in your GitHub account. Put your email **but not your password**. Instead, open your web browser and follow the steps below: + 2. On GitHub: + 1. Navigate to your GitHub Settings (You can access your account Settings from the drop down menu where your account icon is, on the top right of the screen) + 2. Scroll to the bottom of the left hand side menu to find *Developer settings* and open it. + 3. Click *Personal access tokens* > *Tokens (classic)* + 4. Click *Generate new token* > *Generate new token (classic)*. You might need to input your Authentification code if you have enabled 2FA. + 5. Give it a name, and all the scopes you require (tip: select all scopes and *No Expiration*), then click *Generate Token*. **Copy the new generated Token** + 3. Back on your computer: + 1. If you have been following the steps above, you should still be in your shell with GitHub still asking for your password. + 2. **Paste** your Token here, and you should be logging in. Your changes should then be saved to GitHub. + + ??? Info "Choice B: Connecting via SSH" + The process of connecting your computer to GitHub using an SSH key is more expedited (and probably less confusing). + + As a setup step, see if your computer is already connected to GitHub by doing `ssh -T git@github.com`. If the response message is `git@github.com: Permission denied (publickey).` it signifies that your computer is not yet linked with GitHub. To link your computer to github to the following: + + 1. Generate an SSH key with a level of encryption that you prefer: `ssh-keygen -t ed25519 -C `. This command generates an SSH key with [ed25519](https://ed25519.cr.yp.to/) encryption (harder to crack!) and adds your email as "comment" (`-C`, will help recongizing the user adding the key). A number of additional questions are going to ask you where you'd like to save the key and whether you'd like to add a password for protection; unless you want to save it elsewhere, feel free to use the default options. Upon completion you should see something like this: + ``` + Your identification has been saved in /c/Users//.ssh/id_ed25519 + Your public key has been saved in /c/Users//.ssh/id_ed25519.pub + The key fingerprint is: + SHA256:SMSPIStNyA00KPxuYu94KpZgRAYjgt9g4BA4kFy3g1o + The key's randomart image is: + +--[ED25519 256]--+ + |^B== o. | + |%*=.*.+ | + |+=.E =.+ | + | .=.+.o.. | + |.... . S | + |.+ o | + |+ = | + |.o.o | + |oo+. | + +----[SHA256]-----+ + ``` + 2. Upon generating the ssh key, copy it. You can reveal it by doing `cat ~/.ssh/id_ed25519.pub`. + 3. In GitHub, go to your settings: click your account icon on top right, and from the drop down menu, select *Settings* and then *SSH and GPG keys*. Here, click on *New SSH Key*, where you can then paste the newly geneated key. Add a name reflecting your machine and save changes. + + Optional: if you want to check if you successfully linked your computer to GitHub, do `ssh -t git@github.com`. You should receive the following message: `Hi ! You've successfully authenticated, but GitHub does not provide shell access. + +1. Create your own repository + - Add a README and a license and keep the repository public +2. Create a `docs` folder + - Within the folder, create an `index.md` file +3. Navigate to *Settings* > *Actions* > *General*: + - Under *Action Permissions* select *Allow all actions and reusalbe workflows* + - Under *Workflow permissions* select *Read and write permissions* and *Allow GitHub Actions to create and approve pull requests* +4. Create an `requirements.txt` file and populate it with the following requirement list: + + ??? abstract "Expand for code!" + ``` + bump2version + coverage + flake8 + grip + ipykernel + livereload + nbconvert>=7 + pip + sphinx + tox + twine + watchdog + wheel + mkdocs-git-revision-date-plugin + mkdocs-jupyter + mkdocs-material + mkdocs-pdf-export-plugin + mkdocstrings + mkdocstrings-crystal + mkdocstrings-python-legacy + #pygments>=2.10,<2.12 + #pymdown-extensions<9.4 + + # Requirements for core + jinja2>=3.0.2 + markdown>=3.2 + mkdocs>=1.4.0 + mkdocs-material-extensions>=1.0.3 + pygments>=2.12 + pymdown-extensions>=9.4 + + # Requirements for plugins + requests>=2.26 + ``` + +5. Create an `mkdocs.yml` file and populate it with the following: + + ??? abstract "Expand for code!" + ``` + site_name: Name of your website + site_description: Tell people what this website is about + site_author: Who you are + site_url: The website URL + + # Repository + repo_name: The repository name + repo_url: The repository URL + edit_uri: edit/main/docs/ + # Copyright + copyright: 'Copyright © 2023 - 2024' + + + # Configuration + theme: + name: material + highlightjs: true + font: + text: Roboto + code: Regular + palette: + scheme: default + + # Features + features: + - navigation.instant + - navigation.tracking + - navigation.tabs + - navigation.tabs.sticky + - navigation.indexes + - navigation.top + - toc.follow + + # 404 page + static_templates: + - 404.html + + # Search feature + include_search_page: false + search_index_only: true + + # Palette and theme (uses personalized colours) + language: en + palette: + primary: custom + accent: custom + icon: + logo: material/cogs + favicon: material/cogs + + # Page tree + nav: + - Home: index.md + + # Extra Plugins + plugins: + - search + - mkdocstrings + - git-revision-date + - mkdocs-jupyter: + include_source: True + ignore_h1_titles: True + + # Extensions (leave as is) + markdown_extensions: + - admonition + - abbr + - attr_list + - def_list + - footnotes + - meta + - md_in_html + - toc: + permalink: true + title: On this page + - pymdownx.arithmatex: + generic: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.critic + - pymdownx.details + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg + - pymdownx.highlight + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.magiclink: + repo_url_shorthand: true + user: squidfunk + repo: mkdocs-material + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + ``` + +6. Create a `.github/workflows` folder and add a `ghpages.yml` with the following: + + ??? abstract "Expand for code!" + ``` + name: Publish docs via GitHub + on: + push: + branches: + - main + + jobs: + build: + name: Deploy docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: run requirements file + run: pip install -r requirements.txt + - name: Deploy docs + run: mkdocs gh-deploy --force + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ``` + +7. Navigate to *Settings* > *Pages* and make sure that *Source* is *Deploy from a branch* and Branch is *gh-pages*, */(root)* + - You should be able to access your website at `https://.github.io/`. If you cannot find your website, go to the repository's settings page and navigate to *Pages*: your website address will be there. +8. Edit documents as necessary. + - Don't forget to **add**, **commit** and **push** changes! + - Changes will only be visible on the website after a successful push. + - After each push, next to the commit identifier GitHub will show either a yellow circle (:yellow_circle:, meaning building), green check (:material-check:, meaning success), or red cross (:x:, meaning failure). + +--- + +### Further Documentation + +Here are some guides that you may find useful: + +- [MarkDown cheatsheet](https://www.markdownguide.org/cheat-sheet/): for correct MarkDown synthax. +- [MkDocs-material](https://squidfunk.github.io/mkdocs-material/getting-started/): a starting guide to MkDocs Material theme ([massive list of supported emojis here](https://squidfunk.github.io/mkdocs-material/reference/icons-emojis/#search)). +- [MkDocs-material References](https://squidfunk.github.io/mkdocs-material/reference/): more sophisticated documentation for MkDocs Material. +- [YouTube link to FOSS 2022](https://www.youtube.com/watch?v=UQZseJjR_OI&t=3750s&ab_channel=CyVerse.org): Michael explains (~1h mark) his Jekyll-based website and gives a tutorial on how to use [academicpages](https://academicpages.github.io/). + +--- + +## Self-Paced Material + +- [15 Data Science Communities to Join](https://towardsdatascience.com/15-data-science-slack-communities-to-join-8fac301bd6ce){target=_blank} +- [Python & Slack](https://towardsdatascience.com/python-and-slack-a-natural-match-60b136883d4d){target=_blank} +- [Slack CLI notifications](https://samapriya.github.io/projects/slack_notifier_cli_addon/){target=_blank} +- [Meetups](https://www.meetup.com/){target=_blank} + +### GitHub Pages Website Quickstarts + +- [:simple-github: *GitHub Pages*](https://pages.github.com/) + 1. Create a GitHub account + 2. Clone the repo `https://github.com/username/username.github.io` + 3. Create an `index.html` + 4. Push it back to GitHub + +- [:simple-readthedocs: *ReadTheDocs.org*](https://readthedocs.org/) + 1. [Install](https://docs.readthedocs.io/en/stable/install.html) + 2. [Use Github](https://github.com/rtfd/readthedocs.org) + 3. [Create a ReadTheDocs account](https://readthedocs.org/accounts/signup/) + +- [:simple-markdown: *Material MkDocs*](https://squidfunk.github.io/mkdocs-material/getting-started/) + 1. [Install Material](https://squidfunk.github.io/mkdocs-material/getting-started/#installation) + 1. use a [`reqirements.txt`](https://github.com/CyVerse-learning-materials/foss/blob/mkdocs/requirements.txt) + 2. or `pip install mkdocs-material` + 2. Clone a repository with an existing template or create a new repo with `mkdocs new .` + 3. Run `python -m mkdocs serve` to build and serve locally + 4. Open your browser to preview the build at https://localhost:8000` + +- [:material-book-arrow-down: *Bookdown*](https://bookdown.org/) + 1. [Install R and RStudio](https://www.rstudio.com/products/rstudio/download/) + 2. Install Bookdown package with `install.packages("bookdown", dependencies=TRUE)` + 3. Open the Bookdown demo and get started + +- [:simple-r: *Quarto*](https://quarto.org/) + - [Follow these instructions](https://quarto.org/docs/publishing/github-pages.html) + +- [:simple-jupyter: *JupyterBook*](https://jupyterbook.org/en/stable/intro.html) + - [Create your first book](https://jupyterbook.org/en/stable/start/your-first-book.html) + +- [:simple-git: *GitBook*](https://docs.gitbook.com/) + - [Follow Template builder](https://app.gitbook.com/join) + + +--- + +## Self Assessment + +??? Question "True or False: Tutorials and How-to-Guides are the same" + + !!! Success "False" + + Tutorials are in general introductory and longer than How-to-Guides and are intended for teaching learners a new concept by describing applications and providing justifications. + + How-to-Guides are more like cooking recipes which include step-by-step instructions for a specific task. + +??? Question "True or False: Teams should communicate over a single messaging platform." + + !!! Success "False" + + While it may be advisable to push informal communication toward a platform like SLACK or Microsoft Teams, there is no one-platform-fits-all solution for managing a diverse science team. + +??? Question "What is the best communication platform for team science?" + + !!! Info "There is no best platform, but there are some best practices" + + In general, communications amongst team members may be best suited for messaging services like SLACK, Teams, or Chat. + + For software development, GitHub Issues are one of the primary means of documenting changes and interactions on the web. + + Formal communication over email is preferred, and is necessary for legal, budgetary, and institutional interactions. diff --git a/03_documentation_communication/index.html b/03_documentation_communication/index.html new file mode 100644 index 000000000..d6e358fde --- /dev/null +++ b/03_documentation_communication/index.html @@ -0,0 +1,2072 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3. Documentation & Communication - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Documentation & Communication

+
+

Learning Objectives

+

After this lesson, you should be able to:

+
    +
  • Identify and explain different types of project documentation (both internal and external)
  • +
  • Describe tools and approaches to creating your own documentation
  • +
  • Describe best practices for maintaining documentation
  • +
  • Create your own GitHub Pages website (!)
  • +
+
+

Project Documentation

+

Documentation is the practice of recording, preserving, and organizing information, data, or details in a structured and systematic manner. Documentation is also essential to communicate with your future self, your collaborators, or the world on specific ideas and information. Effective documentation must take into consideration the following points:

+
    +
  • Clarity: Documentation should be easy to understand with clear language and no ambiguity.
  • +
  • Completeness: It must cover all essential details, leaving nothing crucial undocumented.
  • +
  • Accuracy: Information should be up-to-date and correct to prevent errors and misunderstandings.
  • +
  • Organization: A logical structure and clear organization make it easy to navigate and find information.
  • +
  • Relevance: Documentation should focus on what's pertinent to its intended audience or purpose, avoiding unnecessary information.
  • +
+

Not all documentation is the same. The documentation system, by Divio, categorizes the different types of documentation into 4 quadrants:

+
+

xkcd +

Read more in depth on the documentation system here: https://documentation.divio.com

+
+
+Explanining the quadrants +
    +
  • Tutorials: Lessons! Tutorials are lessons that take the reader by the hand to understand how the basics of a tool work. They are what your project needs in order to show a beginner that they can achieve something with it. The techical teaching we do in FOSS are mostly tutorials. For example, we do simple tutorials to teach the mechanics of version control.
  • +
  • How-to-guides: Recipes! How-to-guides take the reader through the steps required to acheive a specific outcome or answer a specific question. An example how-to-guide could be a guide on how to install a specific software on a specific operating system.
  • +
  • References: References offer technical descriptions of the machinery and how to operate it. References have one job only: to describe. They are code-determined, because ultimately that’s what they describe: key classes, functions, APIs, and so they should list things like functions, fields, attributes and methods, and set out how to use them.
  • +
  • Explanation: Discussions! The aims of explanations are to clarify and illuminate a particular topic by broadening the documentation’s coverage of a topic.
  • +
+
+

Public Repositories for Documentation

+

GitHub

+
    +
  • On Github, good documentation starts with a robust ReadMe file. The ReadMe file is the first thing that people see when they visit your repository. It is a good place to explain what your project does, how to use it, and how to contribute to it. Here is an example.
  • +
  • Also on Github, you can use the Wiki feature to create a separate space for documentation. The Wiki is a place to document your project in a way that is separate from the code. Here is an example
  • +
+

GitHub Pages

+
    +
  • You can pull templates from other GitHub users for your website, + e.g. Jekyll themes
  • +
  • GitHub pages are free, fast, and easy to build, but limited in use + of subdomain or URLs.
  • +
  • The FOSS website is rendered using GitHub Pages using MkDocs and the Material theme for MkDocs.
  • +
  • Other popular website generator for GitHub Pages is Bootstrap.js.
  • +
+

Material MkDocs

+
    +
  • Material Design theme for MkDocs, a static site generator geared towards (technical) project documentation.
  • +
  • publish via GitHub Actions
  • +
  • Uses open source Material or ReadTheDocs Themes
  • +
+

ReadTheDocs

+
    +
  • publishing websites via + ReadTheDocs.com costs money.
  • +
  • You can work in an offline state, where you develop the materials + and publish them to your localhost using + Sphinx
  • +
  • You can work on a website template in a GitHub repository, and + pushes are updated in near real time using ReadTheDocs.com.
  • +
  • Here is example documentation of Pytorch using ReadTheDocs: PyTorch.
  • +
+

Bookdown

+
    +
  • Bookdown is an open-source R package that facilitates writing books and long-form articles/reports with R Markdown.
  • +
  • Bookdown websites can be hosted by RStudio + Connect
  • +
  • You can publish a Bookdown website using Github + Pages
  • +
+

Quarto

+ +

JupyterBook

+ +

GitBook

+
    +
  • GitBook websites use MarkDown syntax
  • +
  • Free for open source projects, paid plans are available
  • +
+

Confluence Wikis

+ +
+

Things to remember about Documentation

+
    +
  • +

    Documentation should be written in such a way that people who did not write the documentation can read and then use or read and then teach others in the applications of the material.

    +
  • +
  • +

    Documentation is best treated as a living document, but version control is necessary to maintain it

    +
  • +
  • +

    Technology changes over time, expect to refresh documentation every 3-5 years as your projects age and progress.

    +
  • +
+
+

Websites to Host Methods & Protocols

+

Open Science Framework for free. OSF can be directly linked to your ORCID.

+
    +
  • Integrated project management tools
  • +
  • Uses templates to create a project website
  • +
  • Can publish preprints from within project management tools
  • +
+

Protocols.io - collaborative platform and preprint server for: science methods, computational workflows, clinical trials, operational procedures, safety checklists, and instructions / manuals.

+

QUBES - community of math and biology educators who share resources and methods for preparing students to tackle real, complex, biological problems.

+
+What are the benefits of using a GitHub.io website? +

Github Pages are hosted directly from your GitHub repository.

+

Just edit, push, and your changes are live.

+

You do not need to run your own web server!!

+
+
+

Communication

+

Internal Project

+

Choosing which software to use for your internal lab communication can be complicated by the cost of setting up, the cost of maintaining, and simply by the sheer number of platforms that are out there.

+

For this workshop, we use SLACK (Searchable Log of All Conversation & Knowledge). Microsoft's competitor to SLACK is Microsoft Teams.

+

Remember, the intention of these platforms are to improve productivity & not become a distraction.

+

SLACK

+
    +
  • Slack has plenty of apps for coordinating + multiple services, i.e. Calendars, Github, GoogleDrive, Box, etc.
  • +
  • Free Slack is limiting (e.g., 90 day history; limited connections across workspaces).
  • +
  • Paid Slack is $7.25 per user per month. (10 users for 1 year = $870)
  • +
+

Microsoft Teams

+
    +
  • Teams is used by many R1 research universities as part of their + campus wide license agreement for Office 365 Business and Education
  • +
  • For example, anyone with a arizona.edu email address can use Teams for free
  • +
  • Limitations:
      +
    • Not sure you can create your own Teams
    • +
    • Limited to messaging with people in your university Team
    • +
    +
  • +
+

Other popular alternatives

+ +
+

Useful links for creating a SLACK workspace

+
    +
  1. Create a new Workspace
  2. +
  3. Create channels, add apps & tools
  4. +
+
+
+

External (Public)

+

Communicating with the public and other members of your science community (in addition to traditional peer-review publications and conferences) is one of the most important parts of your science!

+

There are many ways scientists use social media and the web to share their data science ideas:

+
    +
  1. :simple-twitter: "Science Twitter" (now X) - + is really just regular Twitter, but with a focus on following other scientists and organizations, and tweeting about research you're interested in. By building up a significant following, more people will know you, know about your work, and you'll have a higher likelihood of meeting other new collaborators.
  2. +
  3. Blogging Platforms such as Medium are a great place to self publish your writing on just about any topic. It's free to sign up and start blogging, but does have a fee for accessing premium content. Some of my favorite blogs include Toward Data Science and Chris Holmes.
  4. +
  5. Community groups - There are lists (and lists of lists) of nationals research organizations, in which a researcher can become involved. These older organziations + still rely on official websites, science journal blogs, and email lists to communicate with their members. In the earth sciences there are open groups which focus on communication like the Earth Science Information Partners (ESIP) with progressive ideas about how data and science can be done. Other groups, like The Carpentries and Research Bazaar are focused on data science training and digital literacy.
  6. +
  7. Podcasts - Creating and distributing audio content to masses is easier than ever before. There are many podcast hosting platforms including Spotify, Podbean, Acast, and Libsyn. From there is it simple to make your podcast availble in the Google Podcast app or Apple Podcast app.
  8. +
  9. Webinars - With platforms such as Zoom, Microsoft Teams, and Google Meet, it is so easy nowadays to host a webinar touting and explaining your science.
  10. +
  11. Youtube - The king of video sharing platforms is a great place to post content promoting your science (and yourself!). For example, Cyverse posts lots of content on cyberinfrastructure and data processing pipelines. Some of my favorite podcasts hosted on Youtube include StarTalk and Lex Fridman.
  12. +
+
+

Important

+

Remember: Personal and Professional Accounts are Not Isolated

+

You decide what you post on the internet. Your scientist identity may be +a part of your personal identity on social media, it might be separate. +A future employer or current employer can see your old posts. What you +post in your personal accounts can be considered a reflection of the +organization you work for and may be used in decisions about hiring or +dismissal.

+
+
+

Hands-on: Building a GitHub Pages Website using MkDocs

+

This section is built in order to educate on and simplify the steps necessary that newcomers need to take in order to build a successful GitHub Pages hosted website.

+

This tutorial is inspired by academicpages, a Jekyll themed template created in order to help scientists and academics build their own websites.

+

The easy way would be to fork/import the foss-reference-hub website (repository) and modify it to reflect your requirements; this tutorial will cover the necessary files and repository structure you require in order to build a successful personal website.

+
+

Repository Explanation

+

A GitHub hosted website running the MkDocs-material theme requires the following files in order to function:

+
    +
  • A docs folder:
      +
    • A folder that contains all the documents necessary to populate the website's pages.
    • +
    • All of the documents that the user needs to change are in here.
    • +
    +
  • +
  • A mkdocs.yml file:
      +
    • A yml file which contains critical information on the website structure, including themes, fonts, and extensions.
    • +
    +
  • +
  • A requirements.txt file:
      +
    • A file with a list of software necessary to build the website, primilily used by GitHub Actions.
    • +
    +
  • +
  • A .github/workflow folder:
      +
    • Contains the ghpages.yml file that controls the GitHub Action.
    • +
    +
  • +
+

The structure of the basic repository is the following:

+
.
+├── README.md
+├── mkdocs.yml              <- Governing file for website building
+├── requirements.txt        <- Requirements file for pip installation (required by website)      
+├── docs                           
+│   ├── assets              <- Folder for images and additional graphic assets
+│   └── index.md            <- Main website home page
+└── .github
+    └── workflows
+        └── ghpages.yml     <- GitHub Actions controlling file
+
+

Upon pushing changes, a gh-pages branch will be automatically created by the GitHub Action; it is where the website is rendered from.

+
+

Directions A: forking or importing an existing repo

+
+

Prerequisites

+

You will require the following in case you want to add code locally.

+
+Create a GitHub account +

Navigate to the GitHub website and click Sign Up, and follow the on screen instructions.

+
+

Additionally, you can choose between Generating a Personal Access Token or using SSH keys. This is useful if you want to work locally and push your changes to GitHub. We are going to cover this further in next week's lesson on Version Control.

+
+Choice A: Generate a Personal Access Token +

You can follow the official documentation on how to generate Tokens here. We discussed how to generate tokens in Week 0. Here's are quick steps you can follow in order to setup your account on your machine using tokens:

+
    +
  1. On your coumputer:
      +
    1. Clone your repository (git clone <repository>)
    2. +
    3. Make changes where necessary, and add (git add <changed files>), commit (git commit -m "<message on changes>") and push your changes (git push origin).
    4. +
    5. You should be prompted to logging in your GitHub account. Put your email but not your password. Instead, open your web browser and follow the steps below:
    6. +
    +
  2. +
  3. On GitHub:
      +
    1. Navigate to your GitHub Settings (You can access your account Settings from the drop down menu where your account icon is, on the top right of the screen)
    2. +
    3. Scroll to the bottom of the left hand side menu to find Developer settings and open it.
    4. +
    5. Click Personal access tokens > Tokens (classic)
    6. +
    7. Click Generate new token > Generate new token (classic). You might need to input your Authentification code if you have enabled 2FA.
    8. +
    9. Give it a name, and all the scopes you require (tip: select all scopes and No Expiration), then click Generate Token. Copy the new generated Token
    10. +
    +
  4. +
  5. Back on your computer:
      +
    1. If you have been following the steps above, you should still be in your shell with GitHub still asking for your password.
    2. +
    3. Paste your Token here, and you should be logging in. Your changes should then be saved to GitHub.
    4. +
    +
  6. +
+
+
+Choice B: Connecting via SSH +

The process of connecting your computer to GitHub using an SSH key is more expedited (and probably less confusing).

+

As a setup step, see if your computer is already connected to GitHub by doing ssh -T git@github.com. If the response message is git@github.com: Permission denied (publickey). it signifies that your computer is not yet linked with GitHub. To link your computer to github to the following:

+
    +
  1. Generate an SSH key with a level of encryption that you prefer: ssh-keygen -t ed25519 -C <your github email>. This command generates an SSH key with ed25519 encryption (harder to crack!) and adds your email as "comment" (-C, will help recongizing the user adding the key). A number of additional questions are going to ask you where you'd like to save the key and whether you'd like to add a password for protection; unless you want to save it elsewhere, feel free to use the default options. Upon completion you should see something like this: +
    Your identification has been saved in /c/Users/<user>/.ssh/id_ed25519
    +Your public key has been saved in /c/Users/<user>/.ssh/id_ed25519.pub
    +The key fingerprint is:
    +SHA256:SMSPIStNyA00KPxuYu94KpZgRAYjgt9g4BA4kFy3g1o <your github email>
    +The key's randomart image is:
    ++--[ED25519 256]--+
    +|^B== o.          |
    +|%*=.*.+          |
    +|+=.E =.+         |
    +| .=.+.o..        |
    +|....  . S        |
    +|.+ o             |
    +|+ =              |
    +|.o.o             |
    +|oo+.             |
    ++----[SHA256]-----+
    +
  2. +
  3. Upon generating the ssh key, copy it. You can reveal it by doing cat ~/.ssh/id_ed25519.pub.
  4. +
  5. In GitHub, go to your settings: click your account icon on top right, and from the drop down menu, select Settings and then SSH and GPG keys. Here, click on New SSH Key, where you can then paste the newly geneated key. Add a name reflecting your machine and save changes.
  6. +
+

Optional: if you want to check if you successfully linked your computer to GitHub, do ssh -t git@github.com. You should receive the following message: `Hi ! You've successfully authenticated, but GitHub does not provide shell access.

+
+
+
    +
  1. Fork or import the FOSS Reference Hub website tutorial repository branch
      +
    • Forking or importing will allow you to have your own copy of a specific repository; Cloning a repository without forking/importing it first, will lead to the changes being applied to the original repository and not your own copy. You should clone your forked or imported repository, not the original!
    • +
    +
  2. +
  3. Navigate to Settings > Actions > General:
      +
    • Under Action Permissions select Allow all actions and reusalbe workflows
    • +
    • Under Workflow permissions select Read and write permissions and Allow GitHub Actions to create and approve pull requests
    • +
    +
  4. +
  5. Edit the mkdocs.yml and push your changes
      +
    • The first changes you should be making are in the first few lines in the mkdocs.yml file in order to reflect your necessities:
        +
      • Line 1: site_name: change to any title you want for your website
      • +
      • Line 2: site_description: give a short description of the website
      • +
      • Line 3: site_author: who you are
      • +
      • Line 4: site_url: change it to the URL reflected in Settings, which will most likely be https://<github-username.github.io>/
      • +
      • Line 7: repo_name: give the name of your repository (e.g., academicpages-mkdocs in this case)
      • +
      • Line 8: repo_url: give the git repository URL
      • +
      • Line 11: copyright: change your name to the maintainer of the website (likely to be you)
      • +
      +
    • +
    +
    +

    Workflow expectations

    +

    The previos changes should trigger the GitHub action workflow, which is setup to apply changes to the website every time a commit is pushed. One of the first thing that mkdocs-material will do, is to create the gh-pages branch (in case you do not have it already). The workflow will fail because the ghpages.yml in the .github/workflows directory is disabled ("commented out"). To enable it, remove the # at the beginnig on each line and commit your changes. Upon changes, the workflow should go ahead and create the gh-pages branch.

    +
    +
  6. +
  7. Navigate to Settings > Pages and make sure that Source is Deploy from a branch and Branch is gh-pages, /(root)
      +
    • You should be able to access your website at https://<github-username>.github.io/. If you cannot find your website, go to the repository's settings page and navigate to Pages: your website address will be there.
    • +
    +
  8. +
  9. Edit documents as necessary.
      +
    • Don't forget to add, commit and push changes!
    • +
    • Changes will only be visible on the website after a successful push.
    • +
    • After each push, next to the commit identifier GitHub will show either a yellow circle (🟡, meaning building), green check (, meaning success), or red cross (❌, meaning failure).
    • +
    +
    +Failure? Try again! +

    If you've been given the red cross ❌, GitHub will notify you with what went wrong. By clicking on the ❌, GitHub will open up a new page showing you the broken process.

    +
    +
  10. +
+

Directions B: Creating your own

+
+

Prerequisites

+

You will require the following in case you want to add code locally.

+
+Create a GitHub account +

Navigate to the GitHub website and click Sign Up, and follow the on screen instructions.

+
+

Additionally, you can choose between Generating a Personal Access Token or using SSH keys. This is useful if you want to work locally and push your changes to GitHub. We are going to cover this further in next week's lesson on Version Control.

+
+Choice A: Generate a Personal Access Token +

You can follow the official documentation on how to generate Tokens here. We discussed how to generate tokens in Week 0. Here's are quick steps you can follow in order to setup your account on your machine using tokens:

+
    +
  1. On your coumputer:
      +
    1. Clone your repository (git clone <repository>)
    2. +
    3. Make changes where necessary, and add (git add <changed files>), commit (git commit -m "<message on changes>") and push your changes (git push origin).
    4. +
    5. You should be prompted to logging in your GitHub account. Put your email but not your password. Instead, open your web browser and follow the steps below:
    6. +
    +
  2. +
  3. On GitHub:
      +
    1. Navigate to your GitHub Settings (You can access your account Settings from the drop down menu where your account icon is, on the top right of the screen)
    2. +
    3. Scroll to the bottom of the left hand side menu to find Developer settings and open it.
    4. +
    5. Click Personal access tokens > Tokens (classic)
    6. +
    7. Click Generate new token > Generate new token (classic). You might need to input your Authentification code if you have enabled 2FA.
    8. +
    9. Give it a name, and all the scopes you require (tip: select all scopes and No Expiration), then click Generate Token. Copy the new generated Token
    10. +
    +
  4. +
  5. Back on your computer:
      +
    1. If you have been following the steps above, you should still be in your shell with GitHub still asking for your password.
    2. +
    3. Paste your Token here, and you should be logging in. Your changes should then be saved to GitHub.
    4. +
    +
  6. +
+
+
+Choice B: Connecting via SSH +

The process of connecting your computer to GitHub using an SSH key is more expedited (and probably less confusing).

+

As a setup step, see if your computer is already connected to GitHub by doing ssh -T git@github.com. If the response message is git@github.com: Permission denied (publickey). it signifies that your computer is not yet linked with GitHub. To link your computer to github to the following:

+
    +
  1. Generate an SSH key with a level of encryption that you prefer: ssh-keygen -t ed25519 -C <your github email>. This command generates an SSH key with ed25519 encryption (harder to crack!) and adds your email as "comment" (-C, will help recongizing the user adding the key). A number of additional questions are going to ask you where you'd like to save the key and whether you'd like to add a password for protection; unless you want to save it elsewhere, feel free to use the default options. Upon completion you should see something like this: +
    Your identification has been saved in /c/Users/<user>/.ssh/id_ed25519
    +Your public key has been saved in /c/Users/<user>/.ssh/id_ed25519.pub
    +The key fingerprint is:
    +SHA256:SMSPIStNyA00KPxuYu94KpZgRAYjgt9g4BA4kFy3g1o <your github email>
    +The key's randomart image is:
    ++--[ED25519 256]--+
    +|^B== o.          |
    +|%*=.*.+          |
    +|+=.E =.+         |
    +| .=.+.o..        |
    +|....  . S        |
    +|.+ o             |
    +|+ =              |
    +|.o.o             |
    +|oo+.             |
    ++----[SHA256]-----+
    +
  2. +
  3. Upon generating the ssh key, copy it. You can reveal it by doing cat ~/.ssh/id_ed25519.pub.
  4. +
  5. In GitHub, go to your settings: click your account icon on top right, and from the drop down menu, select Settings and then SSH and GPG keys. Here, click on New SSH Key, where you can then paste the newly geneated key. Add a name reflecting your machine and save changes.
  6. +
+

Optional: if you want to check if you successfully linked your computer to GitHub, do ssh -t git@github.com. You should receive the following message: `Hi ! You've successfully authenticated, but GitHub does not provide shell access.

+
+
+
    +
  1. Create your own repository
      +
    • Add a README and a license and keep the repository public
    • +
    +
  2. +
  3. Create a docs folder
      +
    • Within the folder, create an index.md file
    • +
    +
  4. +
  5. Navigate to Settings > Actions > General:
      +
    • Under Action Permissions select Allow all actions and reusalbe workflows
    • +
    • Under Workflow permissions select Read and write permissions and Allow GitHub Actions to create and approve pull requests
    • +
    +
  6. +
  7. +

    Create an requirements.txt file and populate it with the following requirement list:

    +
    +Expand for code! +
    bump2version
    +coverage
    +flake8
    +grip
    +ipykernel
    +livereload
    +nbconvert>=7
    +pip
    +sphinx
    +tox
    +twine
    +watchdog
    +wheel
    +mkdocs-git-revision-date-plugin 
    +mkdocs-jupyter 
    +mkdocs-material 
    +mkdocs-pdf-export-plugin
    +mkdocstrings 
    +mkdocstrings-crystal
    +mkdocstrings-python-legacy
    +#pygments>=2.10,<2.12
    +#pymdown-extensions<9.4
    +
    +# Requirements for core
    +jinja2>=3.0.2
    +markdown>=3.2
    +mkdocs>=1.4.0
    +mkdocs-material-extensions>=1.0.3
    +pygments>=2.12
    +pymdown-extensions>=9.4
    +
    +# Requirements for plugins
    +requests>=2.26
    +
    +
    +
  8. +
  9. +

    Create an mkdocs.yml file and populate it with the following:

    +
    +Expand for code! +
    site_name: Name of your website
    +site_description: Tell people what this website is about
    +site_author: Who you are
    +site_url: The website URL
    +
    +# Repository
    +repo_name: The repository name
    +repo_url: The repository URL
    +edit_uri: edit/main/docs/
    +# Copyright
    +copyright: 'Copyright &copy; 2023 - 2024'
    +
    +
    +# Configuration
    +theme:
    +    name: material
    +highlightjs: true
    +font:
    +    text: Roboto
    +    code: Regular
    +palette:
    +    scheme: default
    +
    +# Features  
    +features:
    +- navigation.instant
    +- navigation.tracking
    +- navigation.tabs
    +- navigation.tabs.sticky
    +- navigation.indexes
    +- navigation.top
    +- toc.follow
    +
    +# 404 page
    +static_templates:
    +    - 404.html
    +
    +# Search feature
    +include_search_page: false
    +search_index_only: true
    +
    +# Palette and theme (uses personalized colours)
    +language: en
    +palette:
    +    primary: custom
    +    accent: custom
    +icon:
    +    logo: material/cogs
    +    favicon: material/cogs
    +
    +# Page tree
    +nav:
    +- Home: index.md
    +
    +# Extra Plugins
    +plugins:
    +    - search
    +    - mkdocstrings
    +    - git-revision-date
    +    - mkdocs-jupyter:
    +        include_source: True
    +        ignore_h1_titles: True
    +
    +# Extensions (leave as is)
    +markdown_extensions:
    +- admonition
    +- abbr
    +- attr_list
    +- def_list
    +- footnotes
    +- meta
    +- md_in_html
    +- toc:
    +    permalink: true
    +    title: On this page
    +- pymdownx.arithmatex:
    +    generic: true
    +- pymdownx.betterem:
    +    smart_enable: all
    +- pymdownx.caret
    +- pymdownx.critic
    +- pymdownx.details
    +- pymdownx.emoji:
    +    emoji_index: !!python/name:materialx.emoji.twemoji
    +    emoji_generator: !!python/name:materialx.emoji.to_svg
    +- pymdownx.highlight
    +- pymdownx.inlinehilite
    +- pymdownx.keys
    +- pymdownx.magiclink:
    +    repo_url_shorthand: true
    +    user: squidfunk
    +    repo: mkdocs-material
    +- pymdownx.mark
    +- pymdownx.smartsymbols
    +- pymdownx.superfences:
    +    custom_fences:
    +        - name: mermaid
    +        class: mermaid
    +        format: !!python/name:pymdownx.superfences.fence_code_format
    +- pymdownx.tabbed
    +- pymdownx.tasklist:
    +    custom_checkbox: true
    +- pymdownx.tilde
    +
    +
    +
  10. +
  11. +

    Create a .github/workflows folder and add a ghpages.yml with the following:

    +
    +Expand for code! +
    name: Publish docs via GitHub
    +on:
    +push:
    +    branches:
    +    - main
    +
    +jobs:
    +build:
    +    name: Deploy docs
    +    runs-on: ubuntu-latest
    +    steps:
    +    - uses: actions/checkout@v3
    +    - uses: actions/setup-python@v4
    +        with:
    +            python-version: 3.9
    +    - name: run requirements file
    +        run:  pip install -r requirements.txt 
    +    - name: Deploy docs
    +        run: mkdocs gh-deploy --force
    +        env:
    +            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    +
    +
    +
  12. +
  13. +

    Navigate to Settings > Pages and make sure that Source is Deploy from a branch and Branch is gh-pages, /(root)

    +
      +
    • You should be able to access your website at https://<github-username>.github.io/. If you cannot find your website, go to the repository's settings page and navigate to Pages: your website address will be there.
    • +
    +
  14. +
  15. Edit documents as necessary.
      +
    • Don't forget to add, commit and push changes!
    • +
    • Changes will only be visible on the website after a successful push.
    • +
    • After each push, next to the commit identifier GitHub will show either a yellow circle (🟡, meaning building), green check (, meaning success), or red cross (❌, meaning failure).
    • +
    +
  16. +
+
+

Further Documentation

+

Here are some guides that you may find useful:

+ +
+

Self-Paced Material

+ +

GitHub Pages Website Quickstarts

+ +
+

Self Assessment

+
+True or False: Tutorials and How-to-Guides are the same +
+

False

+

Tutorials are in general introductory and longer than How-to-Guides and are intended for teaching learners a new concept by describing applications and providing justifications.

+

How-to-Guides are more like cooking recipes which include step-by-step instructions for a specific task.

+
+
+
+True or False: Teams should communicate over a single messaging platform. +
+

False

+

While it may be advisable to push informal communication toward a platform like SLACK or Microsoft Teams, there is no one-platform-fits-all solution for managing a diverse science team.

+
+
+
+What is the best communication platform for team science? +
+

There is no best platform, but there are some best practices

+

In general, communications amongst team members may be best suited for messaging services like SLACK, Teams, or Chat.

+

For software development, GitHub Issues are one of the primary means of documenting changes and interactions on the web.

+

Formal communication over email is preferred, and is necessary for legal, budgetary, and institutional interactions.

+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/04_talk_to_computer/04_talk_to_computer.md b/04_talk_to_computer/04_talk_to_computer.md new file mode 100644 index 000000000..e1294a3cb --- /dev/null +++ b/04_talk_to_computer/04_talk_to_computer.md @@ -0,0 +1,680 @@ +# How to Talk to Computers + +## The Command Line Interface + +When using a computer, it is typical to use a keyboard and mouse to navigate a cursor across the screen or simply tap on the screens of our smart phones or tablets. Both of these methods make use of the Graphical User Interface (GUI) and have become central to the way we interact with computers. GUIs make computers so easy to use! + +However, for a more direct and powerful way to instruct your computer, you should learn to use the **Command Line Interface (CLI)**. CLIs are found throughout all operating systems (Windows, MacOS, Linux) though they might have different commands and syntax. + +For this FOSS lesson on CLI, we will focus on the Unix CLI which is present in MacOS and all Linux operating systems. + +
+
+ +!!! Warning "**Attention** :material-microsoft-windows: Windows users" + + Much of what we are going to be teaching is based on open-source software which operates on cloud and is incompatible with Windows OS. + + Unix-based systems such as Linux [:material-ubuntu: Ubuntu](https://ubuntu.com/){target=_blank} and [:material-apple: MacOS X](https://www.apple.com/macos/){target=_blank}, as many scientific tools require a Unix Operating System (OS). + + There are a number of software that allow :material-microsoft-windows: Windows users to execute Unix commands, however we recommend the use of [:simple-linux: Windows Subsystem for Linux (WSL) 2.0](https://docs.microsoft.com/en-us/windows/wsl/install){target=_blank}. + + ??? tip "Quickstart installation of Window's WSL" + + !!! warning "A system reboot is necessary" + + 1. Open :material-powershell: PowerShell in Administrator mode (open :octicons-search-16: Search and look for PowerShell, right click and select "Run as Administrator") + 2. type `wsl --install` + 3. Restart your machine + 4. Open :octicons-search-16: Search and open :simple-linux: WSL; create a username and password, wait for it to finish setting up (should take a few minutes) + 5. You're now ready to use :simple-linux: Linux on your Windows Machine! + + ??? question "Where is the WSL Home folder?" + + The Home folders for Linux and Windows are different. The Windows path to the :simple-linux: WSL home folder is `\\wsl$\Ubuntu\home\`. + + We suggest creating a bookmark in your Windows machine to allow quicker access to the :simple-linux: Linux partition (for quicker access to files). + + To quickly open the folder, open :simple-linux: WSL and execute `explorer.exe .`. This will open a folder in Windows at the Linux Home folder. + +
+
+ +--- + +
+
+ +## The Unix Shell + +The CLI sees the computer stripped down to only a [Terminal](https://en.wikipedia.org/wiki/Terminal_emulator) from where one can run powerful commands executed through the [Shell](https://en.wikipedia.org/wiki/Shell_(computing)). + + +Though there are technical differences between them, the terms **Command Line Interface**, **Terminal**, **Shell**, and **BASH** will be used more or less interchangeably throughout the lesson. + +
+ ![cli](../assets/cli.gif){width=500} +
The Terminal shell
+
+ +
+
+ +
+ +
Quick video on the shell.
+
+ + + +
+
+ +## Introductory Shell Commands + +The following tutorial material was taken from the [Carpentries' Shell Module](https://swcarpentry.github.io/shell-novice/). + +!!! info "Download Some Data from the Carpentries" + To follow along with the tutorial, please download and unzip this data. [shell-lesson-data.zip](https://swcarpentry.github.io/shell-novice/data/shell-lesson-data.zip) + + ??? Tip "The Command Line Way to Download and Unzip!" + Execute the following commands: + ``` + $ sudo apt install unzip + $ wget https://swcarpentry.github.io/shell-novice/data/shell-lesson-data.zip + $ unzip shell-lesson-data.zip + ``` + +
+
+
+ +??? info "Help with Commands" + For every command, typing `man` (manual) before the command, will open the manual for said command. + ``` + $ man ls + ``` + + - The above command will result in opening the *manual* for the `ls` command. You can exit the man page by pressing `q`. + + +??? info "Command Flags" + Each command has **flags**, or options that you can specify. which are summoned with a `-`, such as ` -`. + ``` + $ ls -a -l -h + ``` + + - The above command calls for the `-a` (all), `-l` (long), `-h` (human readable) flags. This causes `ls` to output a list of *all* files (inculding hidden files/folders) with human readable file size (e.g., it will list 3MB instead of 3000000), permissions, creator, and date of creation. + + - If you do not know what flags are available, you can refer to the `man` command (or for many tools, use the `-h` (help) flag). + +??? info "Tips for Directory Navigation" + `.` refers to *current* directory + + `..` refers to *above* directory + + `/` is the directory separator + + `~` indicates the home directory + + For example: + ``` + $ ls . # lists files and folders in the current directory + $ ls .. # lists files and folders in the above directory + $ ls ~ # lists files and folders in the home directory + $ ls ~/Documents # lists files and folders in Documents (a folder present in the home directory) + ``` + +
+
+ + +
+ ![open science](../assets/linux_structure.png){ width="300" } +
Linux Directory Structure
+
+ + + + +### Navigation + +| Command | Explanation | +|---|---| +|`pwd`| print working directory | +|`ls`| list content of folder | +|`cd`| change directory | + +
+ +By typing `pwd`, the current working directory is printed. + +``` +$ pwd + +/home/jgillan +``` +
+
+ +We can then use `ls` to see the contents of the current directory. By using the `-F` flag (`ls -F`) we can also see the type of file. **Note:** an asterisk (`*`) at the end of the object will denote a file, whilst a slash (`/`) will denote a folder. + +``` +$ ls -F +shell-lesson-data/ shell-lesson-data.zip* +``` +
+
+ +We can then move inside the folder of our choice doing `cd`. Doing `ls` following the opening of the folder of choice, will show the contents of the folder you just moved in. Feel free to explore the contents of the folders by using `cd` and `ls`. + +``` +$ cd shell-lesson-data +$ ls -F + +exercise-data/ north-pacific-gyre/ + +$ ls -F exercise-data/ + +animal-counts/ creatures/ numbers.txt* proteins/ writing/ +``` +
+
+ + +!!! Tip "Use the Tab key to autocomplete" + You do not need to type the entire name of a folder or file. By using the tab key, the Shell will autocomplete the name of the files or folders. For example, typing the following + + ``` + $ ls -F exer + ``` + + and pressing the tab key, will result in autocompletion. + + ``` + $ ls -F exercise-data/ + ``` + + You can then press tab twice, to print a list of the contents of the folder. + + ``` + $ ls -F exercise-data/ + animal-counts/ creatures/ numbers.txt proteins/ writing/ + ``` +
+
+ +#### Working with Files and Directories + +| Command | Explanation | +|---|---| +|`mkdir`| make a directory | +|`touch`| creat empty file | +|`nano` or `vim`| text editors | +|`mv`| move command | +|`cp`| copy command | +|`rm`| remove command | + +
+
+ +Return to `shell-lesson-data`, and create a directory with `mkdir `. + +``` +$ mkdir my_folder +$ ls -F + +exercise-data/ my_folder/ north-pacific-gyre/ +``` + +Notice the new `my_folder` directory. + +
+
+ +!!! danger "Naming your files" + It is strongly suggested that you avoid using spaces when naming your files. When using the Shell to communicate with your machine, a space can cause errors when loading or transferring files. Instead, use dashes (`-`), underscores (`_`), periods (`.`) and CamelCase when naming your files. + + Acceptable naming: + ``` + $ mkdir my_personal_folder + $ mkdir my_personal-folder + $ mkdir MyPersonal.Folder + ``` +
+ + ??? Question "What will happen if you create a directory with spaces?" + + You will obtain as many folders as typed words! + ``` + $ mkdir my folder + $ ls -F + exercise-data/ folder/ my/ north-pacific-gyre/ + ``` + Notice the two folders `my` and `folder`. + +
+
+ +Create an empty file with `touch ` + +``` +$ touch new_file.txt +``` + +`touch` will create an **empty** file + +
+ +Add text to the new file +``` +nano new_file.txt +``` + +
+
+ +Use `mv ` to move your newly created file to the directory you created previously (you can then use `ls` to check if you successully moved the file). + +``` +$ ls -F +exercise-data/ new_file* my_folder/ north-pacific-gyre/ + +$ mv new_file.txt my_folder/ +$ ls -F +exercise-data/ my_folder/ north-pacific-gyre/ + +$ ls -F my_folder/ +new_file.txt* +``` +`mv` can also be used to **rename** a file or folder with `mv `. + +``` +$ cd my_folder/ +$ mv new_file my_file +$ ls -F +my_file* +``` + +
+
+
+ +`cp` is the command to copy a file with the syntax `cp ` + +``` +$ cp my_file copy_my_file +$ ls -F +copy_my_file* my_file* +``` + +!!! note "Copying folders" + To copy folders and the content of these folders, you will have to use the `-r` flag (recursive) for `cp` in the following manner `cp -r ` (following example is from the `shell-lesson-data/` directory). + ``` + $ cp -r my_folder/ copy_my_folder + $ ls -F + copy_my_folder/ exercise-data/ my_folder/ north-pacific-gyre/ + + $ ls -F my_folder/ + copy_my_file* my_file* + + $ ls -F copy_my_folder/ + copy_my_file* my_file* + ``` + +
+
+
+ +To remove an unwanted file, use `rm `. + +``` +$ rm copy_my_file +$ ls -F +my_file +``` + +!!! note "Removing folders" + Save as the "Copying Folders" note, you have to use the `-r` flag to remove a folder `rm -r ` (following example is from the `shell-lesson-data/` directory). + ``` + $ rm -r copy_my_folder/ + $ ls -F + exercise-data/ my_folder/ north-pacific-gyre/ + ``` + +
+
+ + +### Shell Script + +Here we are going to show an example command line automation using a shell script. This is what makes the command line powerful! + +!!! tip "Shell Script" + + A shell script is a file with the extension '.sh'. It is essentially a text file that lists out multiple shell commands. When the shell script is run, the computer will run all of the commands in sequence in an automated way. + +
+ +Navigate to the `shell-lesson-data` directory + +``` +$ cd /home/jgillan/shell-lesson-data +``` + +
+ +Create the shell script + +``` +$ nano backup.sh +``` +The text editor Nano will pop up and it will be empty. + +
+ +Copy and paste the following commands into `backup.sh` + +``` +#use Bash shell to run the following commands +#!/bin/bash + +## Variables +#the directory you want to back up (e.g., shell-lesson-data) +SOURCE_DIR="$HOME/Documents/shell-lesson-data" + +#location where the backup will be stored +BACKUP_DIR="$HOME/Backup" + +#used to create a unique name for each backup based on the current date and time +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") + +# name of the compressed backup file +ARCHIVE_NAME="backup_$TIMESTAMP.tar.gz" + + +# Create backup directory if it doesn't exist +mkdir -p "$BACKUP_DIR" + +# Create a compressed archive of the source directory +tar -czf "$BACKUP_DIR/$ARCHIVE_NAME" -C "$SOURCE_DIR" . + +# Output the result +echo "Backup of $SOURCE_DIR completed!" +echo "Archive created at $BACKUP_DIR/$ARCHIVE_NAME" +``` +
+ +Exit nano with `ctrl + x` + +
+ +Modify permission to make the shell script executable +``` +$ chmod +x backup.sh +``` + +
+ +Run the shell script +``` +$ ./backup.sh +``` + +
+Go back to your home directory and look for the new backup directory +``` +$ cd ~ +$ cd ls +``` + +There should be a new directory called 'Backup' with a compressed file within it. + +
+
+ + +### More Carpentries Lessons on Linux Command line +- [Pipes and Filters](https://swcarpentry.github.io/shell-novice/04-pipefilter.html) +- [Loops](https://swcarpentry.github.io/shell-novice/05-loop.html) +- [Scripts](https://swcarpentry.github.io/shell-novice/06-script.html) +- [Finding Things](https://swcarpentry.github.io/shell-novice/07-find.html) + +
+
+
+ +--- + +
+
+
+ + + +## LLM Chatbots for Open Science + +Large Language Model (LLM) chatbots have fundamentally changed how we humans are going to interact with computers going forward. They provide a natural language interface to instruct computers to do many tasks including: + +- Read, write, and summarize text +- Analyze data +- Explain techical topics +- Search the web and retrieve information +- Generate, optimize, and explain many types of computer code +- Understand and generate images + +
+ +Current LLMs generally provide recommendation for how _you_ could do things. ie, they provide you code and text recommendations but don't actually execute anything. But these technologies are advancing quickly and new capabilities are developed and released constantly. Soon, [AI Agents](https://github.com/Significant-Gravitas/AutoGPT) could be everywhere executing on instructions in autonomous and semi-autonomous ways. + +
+ +### Commercial Chatbots + +
+ openai + gemini + foster + cos +
+ +
+
+ +- [:simple-openai: ChatGPT](https://openai.com/chatgpt) +- [:simple-google: Gemini](https://gemini.google.com/) +- [:simple-anthropic: Claude](https://www.anthropic.com/claude) +- [:octicons-copilot-16: Copilot](https://www.microsoft.com/en-us/bing?form=MA13FV) + +
+
+
+ + +### LLMs in 150 words (or less) + +**How they're made**: LLMs work by training on vast amounts of text from the internet. They learn patterns, grammar, and context from this data. When you give them a prompt, they generate text based on what they've learned. Imagine a super-smart autocomplete for text, but it can also create entire paragraphs or articles. + +**How they work**: LLMs don't understand like humans do. They predict what comes next in a sentence using math and probabilities. They don't have thoughts or feelings. They mimic human language but can make mistakes or write nonsense if not guided well. + +**How you can use them**: They're incredibly versatile. You can use them for answering questions, writing essays, coding help, and more. ***But you must be cautious because they can generate biased or false information if not used responsibly***. + +In a nutshell, LLMs are like super-powered text generators trained on the internet's vast knowledge. + +
+
+ +!!! Warning ":warning::warning: **VERIFY EVERTHING CHATBOTS TELL YOU!** :warning::warning:" + +
+
+
+ +### :simple-openai: Prompt Writing + +LLM Chatbots are meant to be conversational. In general, you are asking the Chatbot questions (known as **Prompts**) and the Chatbot will respond with answers. + +It is a bit of an artform to get the Chatbot to provide answers with the specificity and format that you want. An entire field of study has sprung up, called **Prompt Engineering**, which seeks to find the magic words that will elicit the best (and technically correct) responses from the Chatbot. + +
+
+ +#### **Prompt Priming** + +Provide lots of organized details to help the Chatbot understand the question and what it's task is. This could include adding a backstory or context for why you are asking the question. Be very specific in terms of what you want from the Chatbot and how you want it. + +Zero-shot unconditioned prompts are likely to return the least specific responses. Responses are more likely to be useful when multiple specific output types are defined. + +| Types of Priming | Example | +|------------------|---------| +| Zero (Shot) | "Write five examples of assessments for watershed health." | +| Single | "Write five examples of assessments for watershed health. Here is one example: Geomorphology" | +| Multiple | "Write five examples of assessments for watershed health related to geomorphology, water quality, and species diversity." | + +
+
+ +#### **Linked Prompts** + +Responses to prompts may not return the exact details or information that you are after the first time. Follow-up by rephrasing your prompts more carefully and continuing with iterative prompting can build upon your priors. + +"Chain prompting" or "Linked Prompting" brings multiple prompts together. + +| Linked Prompting | Examples | +|------------------|----------| +| Step 1: Priming | "I want you to act as an eminent hydrologist from CUASHI. Provide me with a list of the ten most important topics in hydrology over the last decade focused around research in the global south, working with indigenous communities, and traditional ecological knowledge systems." | +| Step 2: Summarizing | "Based on the list you just created, summarize the most pressing financial challenges faced by indigenous communities in the Global South, versus indigenous communities in North America, in less than 50 words." | +| Step 3: Try again with a web search | "Based on the results of web access, can you confirm the validity of the ten important topics and provide at least one reference to each." | + +!!! tip "Encouraging the Chatbot to do Better" + Chatbot responses can be missing information or just plain wrong. When this occurs, you can point out the mistake and ask the Chatbot to provide a more complete or better answer. Don't settle for poor responses! + +
+
+ +#### Role Playing + +Some people find that asking the Chatbot to adopt a persona will lead to better responses. + +"I want you to act as ..." will establish what type of conversation you are planning to have. + +| Types of Roles | +|---| +| Project Manager | +| Copywriter / Editor | +| Paper Reviewer | +| Teacher / Mentor / Advisor | +| Student / Learner / Participant | +| Software Engineer | +| DevOps Engineer | +| Linux Terminal | +| Python Interpreter | +| Web Browser | + +
+
+
+ +### Prompting Chatbots for FOSS + +
+ +#### Provide a general outline for a data management plan + +``` +I am writing a grant proposal to the National Science Foundation. +Could you please provide me a basic template for a data management plan (DMP) and +please provide url links to resources that can help me with NSF DMP requirements. +``` +
+
+ + +#### Provide a step-by-step recipe to create and serve an mkdocs website in Github + +``` +I would like to create a personal website using the MKdocs style +and host it on Github pages. + +Could you please write me a step-by-step guide starting +with importing an existing github repository that has the mkdocs material. +``` +
+
+ +#### Write shell commands and shell scripts + +``` +I would like to create a linux shell script to automate the backup of my working directory. +Could you please suggest a shell script that will copy my working directory +in a different directory and compress the file into an archive. +Please name the file based on the current time and date. +``` + +
+
+ +#### Write git commands + +``` +Could you please provide me a step-by-step workflow for using git with github? +I found a repository that I want to build on in Github. +I would like to work on the material on my local machine and then save it back up to github. +I would like to workflow to be for the linux command line. +``` + +
+
+ +#### Write download and conda commands +``` +I am writing a lot of scripts using python. I have heard that environment managers such as conda may be useful to me. +I don't know anything about conda, so can you explain some things? +1. Give me a high level overview of what environment managers are and what conda is specifically. +2. Please create a step-by-step guide for downloading conda on my machine, and how to use conda to create custom environments. +3. Please explain and give my steps to share my environment with colleagues. +``` +
+
+ +#### Write docker run commands + +``` +I would like to run a docker container that consists of a jupyter notebook. +Can you please suggest a docker run command that launches the jupyter notebook +and mounts a volume of data in it. +``` + +
+
+ +#### Write docker files + +``` +I would like to create a docker image that consists of R studio and +some customized Rcode. Can you tell me the steps to 1. make a dockerfile and +build the docker image; and 2. Upload the docker image to docker hub. +``` + + +
+
+
+
+ +??? Tip "ChatGPT :simple-awesomelists: Awesome Lists" + + There is an ever changing meta-list of :simple-awesomelists: Awesome lists curated around ChatGPT plugins and extensions. + + [:simple-github: search: `chatgpt+awesome`](https://github.com/search?q=awesome-chatgpt+&type=repositories&s=stars&o=desc) + + Check out lists around: + + [:simple-awesomelists: ChatGPT Prompts](https://github.com/f/awesome-chatgpt-prompts) + + [:simple-awesomelists: ChatGPT Data Science Prompts](https://github.com/travistangvh/ChatGPT-Data-Science-Prompts) + + [:simple-awesomelists: API plugins, extensions, & applications](https://github.com/humanloop/awesome-chatgpt) + + + diff --git a/04_talk_to_computer/index.html b/04_talk_to_computer/index.html new file mode 100644 index 000000000..a8049b8d1 --- /dev/null +++ b/04_talk_to_computer/index.html @@ -0,0 +1,2151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4. How to Talk to Computers - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

How to Talk to Computers

+

The Command Line Interface

+

When using a computer, it is typical to use a keyboard and mouse to navigate a cursor across the screen or simply tap on the screens of our smart phones or tablets. Both of these methods make use of the Graphical User Interface (GUI) and have become central to the way we interact with computers. GUIs make computers so easy to use!

+

However, for a more direct and powerful way to instruct your computer, you should learn to use the Command Line Interface (CLI). CLIs are found throughout all operating systems (Windows, MacOS, Linux) though they might have different commands and syntax.

+

For this FOSS lesson on CLI, we will focus on the Unix CLI which is present in MacOS and all Linux operating systems.

+


+

+
+

Attention Windows users

+

Much of what we are going to be teaching is based on open-source software which operates on cloud and is incompatible with Windows OS.

+

Unix-based systems such as Linux Ubuntu and MacOS X, as many scientific tools require a Unix Operating System (OS).

+

There are a number of software that allow Windows users to execute Unix commands, however we recommend the use of Windows Subsystem for Linux (WSL) 2.0.

+
+Quickstart installation of Window's WSL +
+

A system reboot is necessary

+
+
    +
  1. Open PowerShell in Administrator mode (open Search and look for PowerShell, right click and select "Run as Administrator")
  2. +
  3. type wsl --install
  4. +
  5. Restart your machine
  6. +
  7. Open Search and open WSL; create a username and password, wait for it to finish setting up (should take a few minutes)
  8. +
  9. You're now ready to use Linux on your Windows Machine!
  10. +
+
+Where is the WSL Home folder? +

The Home folders for Linux and Windows are different. The Windows path to the WSL home folder is \\wsl$\Ubuntu\home\<username>.

+

We suggest creating a bookmark in your Windows machine to allow quicker access to the Linux partition (for quicker access to files).

+

To quickly open the folder, open WSL and execute explorer.exe .. This will open a folder in Windows at the Linux Home folder.

+
+
+
+


+

+
+


+

+

The Unix Shell

+

The CLI sees the computer stripped down to only a Terminal from where one can run powerful commands executed through the Shell.

+

Though there are technical differences between them, the terms Command Line Interface, Terminal, Shell, and BASH will be used more or less interchangeably throughout the lesson.

+
+

cli +

The Terminal shell

+
+


+

+
+ +
Quick video on the shell.
+
+


+

+

Introductory Shell Commands

+

The following tutorial material was taken from the Carpentries' Shell Module.

+
+

Download Some Data from the Carpentries

+

To follow along with the tutorial, please download and unzip this data. shell-lesson-data.zip

+
+The Command Line Way to Download and Unzip! +

Execute the following commands: +

$ sudo apt install unzip
+$ wget https://swcarpentry.github.io/shell-novice/data/shell-lesson-data.zip
+$ unzip shell-lesson-data.zip
+

+
+
+


+
+

+
+Help with Commands +

For every command, typing man (manual) before the command, will open the manual for said command. +

$ man ls
+

+
    +
  • The above command will result in opening the manual for the ls command. You can exit the man page by pressing q.
  • +
+
+
+Command Flags +

Each command has flags, or options that you can specify. which are summoned with a -, such as <command> -<flag>. +

$ ls -a -l -h
+

+
    +
  • +

    The above command calls for the -a (all), -l (long), -h (human readable) flags. This causes ls to output a list of all files (inculding hidden files/folders) with human readable file size (e.g., it will list 3MB instead of 3000000), permissions, creator, and date of creation.

    +
  • +
  • +

    If you do not know what flags are available, you can refer to the man command (or for many tools, use the -h (help) flag).

    +
  • +
+
+
+Tips for Directory Navigation +

. refers to current directory

+

.. refers to above directory

+

/ is the directory separator

+

~ indicates the home directory

+

For example: +

$ ls .            # lists files and folders in the current directory
+$ ls ..           # lists files and folders in the above directory
+$ ls ~            # lists files and folders in the home directory
+$ ls ~/Documents  # lists files and folders in Documents (a folder present in the home directory)
+

+
+


+

+
+

open science +

Linux Directory Structure

+
+ + + + + + + + + + + + + + + + + + + + + + +
CommandExplanation
pwdprint working directory
lslist content of folder
cdchange directory
+


+

By typing pwd, the current working directory is printed.

+

$ pwd
+
+/home/jgillan
+
+
+

+

We can then use ls to see the contents of the current directory. By using the -F flag (ls -F) we can also see the type of file. Note: an asterisk (*) at the end of the object will denote a file, whilst a slash (/) will denote a folder.

+

$ ls -F 
+shell-lesson-data/   shell-lesson-data.zip*
+
+
+

+

We can then move inside the folder of our choice doing cd. Doing ls following the opening of the folder of choice, will show the contents of the folder you just moved in. Feel free to explore the contents of the folders by using cd and ls.

+

$ cd shell-lesson-data
+$ ls -F
+
+exercise-data/  north-pacific-gyre/
+
+$ ls -F exercise-data/
+
+animal-counts/  creatures/  numbers.txt*  proteins/  writing/
+
+
+

+
+

Use the Tab key to autocomplete

+

You do not need to type the entire name of a folder or file. By using the tab key, the Shell will autocomplete the name of the files or folders. For example, typing the following

+
$ ls -F exer
+
+

and pressing the tab key, will result in autocompletion.

+
$ ls -F exercise-data/
+
+

You can then press tab twice, to print a list of the contents of the folder.

+
$ ls -F exercise-data/
+animal-counts/ creatures/     numbers.txt    proteins/      writing/ 
+
+
+


+

+

Working with Files and Directories

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandExplanation
mkdirmake a directory
touchcreat empty file
nano or vimtext editors
mvmove command
cpcopy command
rmremove command
+


+

+

Return to shell-lesson-data, and create a directory with mkdir <name of folder>.

+
$ mkdir my_folder
+$ ls -F
+
+exercise-data/  my_folder/  north-pacific-gyre/
+
+

Notice the new my_folder directory.

+


+

+
+

Naming your files

+

It is strongly suggested that you avoid using spaces when naming your files. When using the Shell to communicate with your machine, a space can cause errors when loading or transferring files. Instead, use dashes (-), underscores (_), periods (.) and CamelCase when naming your files.

+

Acceptable naming: +

$ mkdir my_personal_folder
+$ mkdir my_personal-folder
+$ mkdir MyPersonal.Folder
+
+

+
+What will happen if you create a directory with spaces? +

You will obtain as many folders as typed words! +

$ mkdir my folder
+$ ls -F
+exercise-data/  folder/  my/  north-pacific-gyre/
+
+Notice the two folders my and folder.

+
+
+


+

+

Create an empty file with touch <name of file>

+
$ touch new_file.txt
+
+

touch will create an empty file

+


+

Add text to the new file +

nano new_file.txt 
+

+


+

+

Use mv <name of file or folder you want to move> <name of destination folder> to move your newly created file to the directory you created previously (you can then use ls to check if you successully moved the file).

+

$ ls -F
+exercise-data/  new_file*  my_folder/  north-pacific-gyre/
+
+$ mv new_file.txt my_folder/
+$ ls -F
+exercise-data/  my_folder/  north-pacific-gyre/
+
+$ ls -F my_folder/
+new_file.txt*
+
+mv can also be used to rename a file or folder with mv <name of file or folder you want to change> <new name>.

+
$ cd my_folder/
+$ mv new_file my_file
+$ ls -F
+my_file*
+
+


+
+

+

cp is the command to copy a file with the syntax cp <name of file you want to copy> <name of copy file>

+
$ cp my_file copy_my_file
+$ ls -F 
+copy_my_file*  my_file*
+
+
+

Copying folders

+

To copy folders and the content of these folders, you will have to use the -r flag (recursive) for cp in the following manner cp -r <name of folder you want to copy> <name of copy folder> (following example is from the shell-lesson-data/ directory). +

$ cp -r my_folder/ copy_my_folder
+$ ls -F
+copy_my_folder/  exercise-data/  my_folder/  north-pacific-gyre/
+
+$ ls -F my_folder/
+copy_my_file*  my_file*
+
+$ ls -F copy_my_folder/
+copy_my_file*  my_file*
+

+
+


+
+

+

To remove an unwanted file, use rm <name of file to remove>.

+
$ rm copy_my_file
+$ ls -F 
+my_file
+
+
+

Removing folders

+

Save as the "Copying Folders" note, you have to use the -r flag to remove a folder rm -r <name of folder you want to remove> (following example is from the shell-lesson-data/ directory). +

$ rm -r copy_my_folder/
+$ ls -F
+exercise-data/  my_folder/  north-pacific-gyre/
+

+
+


+

+

Shell Script

+

Here we are going to show an example command line automation using a shell script. This is what makes the command line powerful!

+
+

Shell Script

+

A shell script is a file with the extension '.sh'. It is essentially a text file that lists out multiple shell commands. When the shell script is run, the computer will run all of the commands in sequence in an automated way.

+
+


+

Navigate to the shell-lesson-data directory

+
$ cd /home/jgillan/shell-lesson-data
+
+


+

Create the shell script

+

$ nano backup.sh
+
+The text editor Nano will pop up and it will be empty.

+


+

Copy and paste the following commands into backup.sh

+

#use Bash shell to run the following commands
+#!/bin/bash
+
+## Variables
+#the directory you want to back up (e.g., shell-lesson-data)
+SOURCE_DIR="$HOME/Documents/shell-lesson-data"
+
+#location where the backup will be stored
+BACKUP_DIR="$HOME/Backup"
+
+#used to create a unique name for each backup based on the current date and time
+TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
+
+# name of the compressed backup file
+ARCHIVE_NAME="backup_$TIMESTAMP.tar.gz"
+
+
+# Create backup directory if it doesn't exist
+mkdir -p "$BACKUP_DIR"
+
+# Create a compressed archive of the source directory
+tar -czf "$BACKUP_DIR/$ARCHIVE_NAME" -C "$SOURCE_DIR" .
+
+# Output the result
+echo "Backup of $SOURCE_DIR completed!"
+echo "Archive created at $BACKUP_DIR/$ARCHIVE_NAME"
+
+

+

Exit nano with ctrl + x

+


+

Modify permission to make the shell script executable +

$ chmod +x backup.sh
+

+


+

Run the shell script +

$ ./backup.sh
+

+


+Go back to your home directory and look for the new backup directory +

$ cd ~
+$ cd ls
+

+

There should be a new directory called 'Backup' with a compressed file within it.

+


+

+

More Carpentries Lessons on Linux Command line

+ +


+
+

+
+


+
+

+

LLM Chatbots for Open Science

+

Large Language Model (LLM) chatbots have fundamentally changed how we humans are going to interact with computers going forward. They provide a natural language interface to instruct computers to do many tasks including:

+
    +
  • Read, write, and summarize text
  • +
  • Analyze data
  • +
  • Explain techical topics
  • +
  • Search the web and retrieve information
  • +
  • Generate, optimize, and explain many types of computer code
  • +
  • Understand and generate images
  • +
+


+

Current LLMs generally provide recommendation for how you could do things. ie, they provide you code and text recommendations but don't actually execute anything. But these technologies are advancing quickly and new capabilities are developed and released constantly. Soon, AI Agents could be everywhere executing on instructions in autonomous and semi-autonomous ways.

+


+

Commercial Chatbots

+
+ openai + gemini + foster + cos +
+ +


+

+ +


+
+

+

LLMs in 150 words (or less)

+

How they're made: LLMs work by training on vast amounts of text from the internet. They learn patterns, grammar, and context from this data. When you give them a prompt, they generate text based on what they've learned. Imagine a super-smart autocomplete for text, but it can also create entire paragraphs or articles.

+

How they work: LLMs don't understand like humans do. They predict what comes next in a sentence using math and probabilities. They don't have thoughts or feelings. They mimic human language but can make mistakes or write nonsense if not guided well.

+

How you can use them: They're incredibly versatile. You can use them for answering questions, writing essays, coding help, and more. But you must be cautious because they can generate biased or false information if not used responsibly.

+

In a nutshell, LLMs are like super-powered text generators trained on the internet's vast knowledge.

+


+

+
+

⚠⚠ VERIFY EVERTHING CHATBOTS TELL YOU! ⚠⚠

+
+


+
+

+

Prompt Writing

+

LLM Chatbots are meant to be conversational. In general, you are asking the Chatbot questions (known as Prompts) and the Chatbot will respond with answers.

+

It is a bit of an artform to get the Chatbot to provide answers with the specificity and format that you want. An entire field of study has sprung up, called Prompt Engineering, which seeks to find the magic words that will elicit the best (and technically correct) responses from the Chatbot.

+


+

+

Prompt Priming

+

Provide lots of organized details to help the Chatbot understand the question and what it's task is. This could include adding a backstory or context for why you are asking the question. Be very specific in terms of what you want from the Chatbot and how you want it.

+

Zero-shot unconditioned prompts are likely to return the least specific responses. Responses are more likely to be useful when multiple specific output types are defined.

+ + + + + + + + + + + + + + + + + + + + + +
Types of PrimingExample
Zero (Shot)"Write five examples of assessments for watershed health."
Single"Write five examples of assessments for watershed health. Here is one example: Geomorphology"
Multiple"Write five examples of assessments for watershed health related to geomorphology, water quality, and species diversity."
+


+

+

Linked Prompts

+

Responses to prompts may not return the exact details or information that you are after the first time. Follow-up by rephrasing your prompts more carefully and continuing with iterative prompting can build upon your priors.

+

"Chain prompting" or "Linked Prompting" brings multiple prompts together.

+ + + + + + + + + + + + + + + + + + + + + +
Linked PromptingExamples
Step 1: Priming"I want you to act as an eminent hydrologist from CUASHI. Provide me with a list of the ten most important topics in hydrology over the last decade focused around research in the global south, working with indigenous communities, and traditional ecological knowledge systems."
Step 2: Summarizing"Based on the list you just created, summarize the most pressing financial challenges faced by indigenous communities in the Global South, versus indigenous communities in North America, in less than 50 words."
Step 3: Try again with a web search"Based on the results of web access, can you confirm the validity of the ten important topics and provide at least one reference to each."
+
+

Encouraging the Chatbot to do Better

+

Chatbot responses can be missing information or just plain wrong. When this occurs, you can point out the mistake and ask the Chatbot to provide a more complete or better answer. Don't settle for poor responses!

+
+


+

+

Role Playing

+

Some people find that asking the Chatbot to adopt a persona will lead to better responses.

+

"I want you to act as ..." will establish what type of conversation you are planning to have.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Types of Roles
Project Manager
Copywriter / Editor
Paper Reviewer
Teacher / Mentor / Advisor
Student / Learner / Participant
Software Engineer
DevOps Engineer
Linux Terminal
Python Interpreter
Web Browser
+


+
+

+

Prompting Chatbots for FOSS

+


+

Provide a general outline for a data management plan

+

I am writing a grant proposal to the National Science Foundation. 
+Could you please provide me a basic template for a data management plan (DMP) and 
+please provide url links to resources that can help me with NSF DMP requirements.
+
+
+

+

Provide a step-by-step recipe to create and serve an mkdocs website in Github

+

I would like to create a personal website using the MKdocs style 
+and host it on Github pages.
+
+Could you please write me a step-by-step guide starting 
+with importing an existing github repository that has the mkdocs material.
+
+
+

+

Write shell commands and shell scripts

+
I would like to create a linux shell script to automate the backup of my working directory. 
+Could you please suggest a shell script that will copy my working directory 
+in a different directory and compress the file into an archive. 
+Please name the file based on the current time and date. 
+
+


+

+

Write git commands

+
Could you please provide me a step-by-step workflow for using git with github? 
+I found a repository that I want to build on in Github. 
+I would like to work on the material on my local machine and then save it back up to github. 
+I would like to workflow to be for the linux command line. 
+
+


+

+

Write download and conda commands

+

I am writing a lot of scripts using python. I have heard that environment managers such as conda may be useful to me. 
+I don't know anything about conda, so can you explain some things?
+1. Give me a high level overview of what environment managers are and what conda is specifically.
+2. Please create a step-by-step guide for downloading conda on my machine, and how to use conda to create custom environments. 
+3. Please explain and give my steps to share my environment with colleagues.
+
+
+

+

Write docker run commands

+
I would like to run a docker container that consists of a jupyter notebook. 
+Can you please suggest a docker run command that launches the jupyter notebook
+and mounts a volume of data in it. 
+
+


+

+

Write docker files

+
I would like to create a docker image that consists of R studio and 
+some customized Rcode. Can you tell me the steps to 1. make a dockerfile and 
+build the docker image; and 2. Upload the docker image to docker hub.
+
+


+
+
+

+
+ChatGPT Awesome Lists +

There is an ever changing meta-list of Awesome lists curated around ChatGPT plugins and extensions.

+

search: chatgpt+awesome

+

Check out lists around:

+

ChatGPT Prompts

+

ChatGPT Data Science Prompts

+

API plugins, extensions, & applications

+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/05_version_control/05_version_control.md b/05_version_control/05_version_control.md new file mode 100644 index 000000000..5ae486a04 --- /dev/null +++ b/05_version_control/05_version_control.md @@ -0,0 +1,628 @@ +# Version Control + + +!!! Success "Learning Objectives" + + After this lesson, you should be able to: + + * Understand the basics of `git` as a resource for reproducible programming + * Describe tools and approaches to creating your `git` Repositories + * Describe best practices for maintaining GitHub Organizations and Repositories + * Maintain own GitHub user profile and repositories + +Version control refers to keeping track of the version of a file, set of +files, or a whole project. + +Some version control tools: + +- :material-microsoft-office: Microsoft Office's [*Track Changes*](https://support.microsoft.com/en-au/office/track-changes-in-word-197ba630-0f5f-4a8e-9a77-3712475e806a) functionality +- :simple-apple: Apple's [*Time Machine*](https://support.apple.com/en-us/HT201250) +- :simple-googledocs: Google Docs' [*Version History*](https://support.google.com/docs/answer/190843?hl=en&co=GENIE.Platform%3DDesktop) +- :simple-git: [Git](https://git-scm.com/) + +Version control is as much a philosophy as a set of tools; you don't +need to master Git to utilize version control (though it is certainly a +worthwhile tool for many researchers). + + +
+ ![git_def](https://swcarpentry.github.io/git-novice/fig/phd101212s.png) +
We have all been here, taken by the [Software Carpentry Version Control lesson](https://swcarpentry.github.io/git-novice/01-basics.html).
+
+ +--- + +## :simple-git: Git vs. :simple-github: GitHub + +**Git** is a command-line program for version control of repositories. +It keeps track of changes you make to files in your repository and +stores those changes in a *.git* folder in that repository. +These changes happen whenever you make a **commit**. Git stores the +history of these commits in a "tree", so you can go back to any +previous commit. By keeping track of the **differences** between +commits, Git can be much more efficient than storing an entire copy of +each version in a document's history. + +You could utilize Git completely on its own, on your local computer, and +get a lot of benefits. You will have a history of the changes you made +to a project, allowing you to go back to any old version of your work. +However, where Git really shines is in *collaborative* work. In order to +effectively collaborate with others on a project, you need two basic +features: a way to allow people to work in parallel, and a way to host +repositories somewhere where everyone can access them. The first feature +is **branching**, which is part of Git, and the hosting part can be +taken care of by platforms like GitHub, GitLab, or Bitbucket. We will +focus on GitHub. + +GitHub is a site that can remotely host your Git repositories. By +putting your repository onto GitHub, you get a backup of the repository, +a way to collaborate with others, and a lot of other features. + +- **Git**: + - First developed in 2005, git is a version control software that allows users to make changes and add versions to their code. + - Changes and versions are saved locally. + - Accessible through the Shell. + +- **GitHub**: + - First launched in 2008, its main focus is hosting and sharing code. + - Uses Git version control software. + - Changes and versions are saved online (requires an account). + - Mainly administered through the web (it also has a [desktop app](https://desktop.github.com/){target=_blank}). + - Acquired by Microsoft in 2018. + + + +
+ ![git_v_github](https://devmountain.com/wp-content/uploads/2022/01/Gitvs_Github-1a-1.jpg) +
Git vs GitHub, simplified
+
+ +### Definitions + +!!! Info "Git-related Definitions" + + **Platforms**: + + - **Git**: tool for version control. + - **GitHub**: hosted server that is also interactive. + + **Locations and directions**: + + - **repo**: short for repository + - **local**: on your personal computer. + - **remote**: somewhere other than your computer. GitHub can host remote + repositories. + - **upstream**: primary or main branch of original repository. + - **downstream**: branch or fork of repository. + + **Actions**: + + - **clone**: copy of a repository that lives locally on your computer. + Pushing changes will affect the repository online. + - **pull**: getting latest changes to the repository on your local + computer. + - the **fetch** command does the same, however one needs to also **merge** the changes, whilst with pull, the merge action is automatic. + - **branch**: a history of changes to a repository. You can have parallel + branches with separate histories, allowing you to keep a "main" + version and development versions. + - **fork**: copy of someone else's repository stored locally on your + account. From forks, you can make pull requests to the main branch. + - **commit**: finalize a change. + - **push**: add changes back to the remote repository. + - **merge**: takes changes from a branch or fork and applies them to the + main. + + !!! tip "These are also commands when paired with `git`!" + Using the following synthax `git ` one can trigger an action. An example is `git pull`, which will pull all of the latest changes in the remote repository. + + **Funtional**: + + - **pull request**: proposed changes to/within a repository. + + - **issue**: suggestions or tasks needed for the repository. Allows you to + track decisions, bugs with the repository, etc. + +
+ ![git_def](https://www.c-sharpcorner.com/article/git-and-github-version-control-local-and-remote-repository/Images/Git%20And%20Github%20Version%20Control.png) +
Visualizing the commands through a workflow example
(graphic's correction: ~~marged~~ merged)
+
+ +--- + +## Practical Git Techniques + +
+ ![vc_path](https://content.cdntwrk.com/files/aHViPTg1NDMzJmNtZD1pdGVtZWRpdG9yaW1hZ2UmZmlsZW5hbWU9aXRlbWVkaXRvcmltYWdlXzYzOTkwY2I4OWU5YTUuanBnJnZlcnNpb249MDAwMCZzaWc9OWJjZTA5NDIxNzY4MWFhZjYyNmEwNWNhYmI1YTUzMWQ%253D) +
The version control path sofware takes before release
+
+ +!!! info "The basic Git life cycle" + + When using Git for your version control, the usual life cycle is the following: + + | Action| Explanation | + |---|---| + | 1. `git clone ` | Clones the target repository to your machine | + | 2. `git status` | Checks whether there are changes in the remote, original repository | + | 3. `git pull`| Pulls any change to your local repository | + | 4. `git add ` | Adds to a future commit any change | + | 5. `git commit -m ""` | Creates the commit and adds a descriptive message | + | 6. `git push` | Pushes the changes commited from local to the remote repository | + + If there are no branches or external pull requests, the *basic* Git life cycle is summarizable like this: + + ```mermaid + graph LR + A[1. git clone] --> B[2. git status] -->C([differences from origin?]):::colorclass; + C-->|yes| D[3. git pull]--> E; + C-->|no| E[4. git add]; + E-->F[5. git commit] -->G[6. git push]; + G-->B; + classDef colorclass fill:#f96 + ``` + +After learning the basics of using Git, which you can learn with the +[Software Carpentry Git Lesson](https://swcarpentry.github.io/git-novice/), there are some next +things that can be useful to learn. Here are a couple topics that are +worth digging into more: + +- **:octicons-log-24: Using the Git log** + - You can access using **git log** + - Will show you your commit history + - Useful for figuring out where you need to roll back to + +- **:material-keyboard-tab-reverse: Reverting** + - There are a lot of different ways to "undo" something in Git + - Some are safer, some are a bit riskier + - Depends on what stage of the commit process you're in + - **Here are some useful resources**: + - [*10 Common Git Problems and How to Fix Them*](https://www.codementor.io/@citizen428/git-tutorial-10-common-git-problems-and-how-to-fix-them-aajv0katd) + - [*"So you have a mess on your hands..."*](http://justinhileman.info/article/git-pretty/git-pretty.png) + - [*How to undo almost anything*](https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/) + +- **:octicons-git-branch-24: Branching** + - This is important to learn if you're going to be doing any sort of collaboration + - Here is a fantastic resource for learning how git branching really works: https://learngitbranching.js.org/ + - **you will probably have to deal with *merge conflicts* at some point** + - Merge conflicts happen when two branches are being merged, but they have *different* changes to the same part of a file + - Perhaps you are working on a feature branch, and you change line 61 in *file.R*, but someone else made a change to the main branch at line 61 in *file.R*. When you try to merge the feature and main branches, Git won't know which changes to line 61 in *file.R* are correct, and you will need to manually decide. + - Here are some good resources: + - [Resolving merge conflics](https://docs.github.com/en/github/collaborating-with-pull-requests/addressing-merge-conflicts/)resolving-a-merge-conflict-using-the-command-line + - [git - ours & theirs, a CLI resource to help with conflicts](https://nitaym.github.io/ourstheirs/) + +- **:simple-gitignoredotio: .gitignore** + - You often want Git to completely ignore certain files + - Generated files (like HTML files from Markdown docs) + - IDE-specific files like in *.RStudio* or *.vscode* folders + - **really big files, like data or images** + - If you accidentally commit a really big file, GitHub might not let you push that commit + - If you have a huge file in Git, your repository size can get way too big + - This is a pain to solve, so use the *.gitignore* file ahead of time, but if you need to fix this, here is a great resource: + - [Removing Large Files From git Using BFG and a Local Repository](https://necromuralist.github.io/posts/removing-large-files-from-git-using-bfg-and-a-local-repository/) + +--- + +## Large Data and GitHub + +GitHub allows commited files to be uploaded only if the file is of 100MB or less (with a warning being issued for files between 50MB and 100MB). Additionally, [GitHub recommends to keep repositories below the 1GB threshold](https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-large-files-on-github#repository-size-limits), as this also allows for quicker cloning and sharing of the repository. If a large file has been uploaded by mistake and you wish to remove it, [you can follow these instrutctions](https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-large-files-on-github#removing-files-from-a-repositorys-history). + +If you *do* have to work with large files and Git, here are some questions to ask yourself: + +- Is this data shareable? +- Are there alternative file hosting platforms I can use? +- How will this data impact the sharability of this repository? +- Am I using a .gitignore? + +GitHub now offers the [**Git Large File Storage (:simple-gitlfs: Git LFS)**](https://git-lfs.com/): the system works by storing references to the file in your repository, but not the file itself -- it creates a *pointer* file within the repo, and stores the file elsewhere. If you were to clone the repository, the pointer file will act as a map to show you how to obtain the original file. + +Git LFS data upload limits are based on your GitHub subscription: + +- 2 GB for GitHub free and GitHub Pro +- 4 GB for GitHub Team +- 5 GB for GitHub Enterprise Cloud + +
+ ![gitlfs](https://git-lfs.com/images/tweet-promo.png) +
A depiction of how the Git LFS pointer-repository relationship works.
+
+ +--- + +## Useful GitHub Features + +At its core, GitHub is just a place to host your Git repositories. +However, it offers a lot of functionality that has less to do with Git, +and more to do with [**Project Management**](02_project_management.md). We will +walk through a few of these useful features. + +- [**:octicons-issue-opened-16: Issues**](https://docs.github.com/en/issues) + - Issues let you plan out changes and suggestions to a repo + - Closing/reopening + - Labels + - Assigning + - Templates + - Numbering/mentioning + +- [**:material-source-pull: Pull Requests**](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) + - Pull requests are a way to request merging code from one branch to another + - typical workflow is for someone to fork a repo, then make a PR from that repo to another + - Reviews + - Commenting + - Merging + - Closing issues + +- [**:octicons-organization-16: Organizations**](https://docs.github.com/en/organizations) + - You can use Organizations to organize sets of repositories + - Roles + - Teams + - GitHub documentation: + +- **Other neat things** + - Permissions/collaborators + - GitHub Classroom + - Gists + - CSV and map rendering + - Code editor + +--- + +## Beyond Git and GitHub + +There are other platforms that address Version Control and have similar functionalities to GitHub: + +- **[:material-gitlab: GitLab](https://gitlab.com/)**: An alternative to GitHub, GitLab offers both a cloud-hosted platform and a self-hosted option ([GitLab CE/EE](https://about.gitlab.com/install/ce-or-ee/)). It provides a comprehensive DevOps platform with built-in CI/CD, container registry, and more. + +- **[:simple-bitbucket: Bitbucket](https://bitbucket.org/product/)**: Atlassian's Bitbucket is a Git repository hosting service that also supports Mercurial repositories. It offers integration with Jira, Confluence, and other Atlassian products. + +- **[:simple-sourceforge: SourceForge](https://sourceforge.net/)**: A platform that provides Git and Subversion hosting, as well as tools for project management, issue tracking, and collaboration. + +- **[:fontawesome-brands-aws: AWS CodeCommit](https://aws.amazon.com/codecommit/)**: Part of Amazon Web Services (AWS), CodeCommit is a managed Git service that integrates seamlessly with other AWS services. + +- **[:simple-azuredevops: Azure DevOps Services (formerly VSTS)](https://azure.microsoft.com/en-us/products/devops))**: Microsoft's Azure DevOps Services offers Git repository hosting along with a wide range of DevOps tools for planning, developing, testing, and deploying software. + +- **[:simple-mercurial: Mercurial](https://www.mercurial-scm.org/)**: Like Git, Mercurial is a distributed version control system, but with a different branching and merging model. It's an alternative to Git for version control. + +--- + +## Self Assessment + +??? Question "True or False: Using `Git` requires a GitHub account" + + !!! Failure "False" + + `Git` is open source software. + + GitHub is a privately owned (Microsoft) company + + Other platforms like [GitLab](https://gitlab.com){target=_blank}, [GitBucket](https://gitbucket.github.io/){target=_blank}, and [GNU Savannah](https://savannah.gnu.org/){target=_blank} all offer `Git` as a version control system service. + +??? Question "True or False: Using `Git` is easy" + + !!! Failure "False" + + Using `Git` can be frustrating to even the most experienced users + +??? Question "When you find a new repository on GitHub that you think can help your research, what are the first things you should do?" + + !!! Success "Look at the README.md" + + Most GitHub repositories have a README.md file which explains what you're looking at. + + !!! Success "Look at the LICENSE" + + Not all repositories are licensed the same way - be sure to check the LICENSE file to see whether the software is open source, or if it has specific requirements for reuse. + + +## Adding Code to Github Locally + +!!! warning "Prerequisites" + You will require the following in case you want to add code locally. + + ??? Info "Create a GitHub account" + Navigate to the [GitHub website](https://github.com/) and click *Sign Up*, and follow the on screen instructions. + + **Installing Git** + + You can follow the official guidelines here: https://github.com/git-guides/install-git. Here we recommend how to install Git on your local machine. + + ??? Info "Windows" + + !!! warning "These instructions are for Windows users **NOT** using [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install). If you do have WSL2, follow the **Unix** instructions." + + 1. Navigate to the [latest Git for Windows installer](https://gitforwindows.org/) and download the latest version. + 2. Once the installer has started, follow the instructions as provided in the Git Setup wizard screen until the installation is complete. + 3. Search and open **Git Bash**. From here, you should be able to run Git commands. + + ??? Info "MacOS" + + 1. Install [Homebrew](https://brew.sh/) (a package manager for MacOS): `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`` + 2. Install Git: `brew install git` + + ??? Info "Unix" + The following command will install git and all related packages on your Unix machine. + ``` + $ sudo apt-get install -y git-all + ``` + + Additionally, you can choose between Generating a Personal Access Token or using SSH keys. This is useful if you want to work locally and push your changes to GitHub. We are going to cover this further in next week's lesson on [Version Control](05_version_control.md). + + ??? Info "Choice A: Generate a Personal Access Token" + You can follow the official documentation on how to generate Tokens [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). We discussed how to generate tokens in [Week 0](https://foss.cyverse.org/00_basics/#adding-code-locally). Here's are quick steps you can follow in order to setup your account on your machine using tokens: + + 1. On your coumputer: + 1. Clone your repository (`git clone `) + 2. Make changes where necessary, and **add** (`git add `), **commit** (`git commit -m ""`) and **push** your changes (`git push origin`). + 3. You should be prompted to logging in your GitHub account. Put your email **but not your password**. Instead, open your web browser and follow the steps below: + 2. On GitHub: + 1. Navigate to your GitHub Settings (You can access your account Settings from the drop down menu where your account icon is, on the top right of the screen) + 2. Scroll to the bottom of the left hand side menu to find *Developer settings* and open it. + 3. Click *Personal access tokens* > *Tokens (classic)* + 4. Click *Generate new token* > *Generate new token (classic)*. You might need to input your Authentification code if you have enabled 2FA. + 5. Give it a name, and all the scopes you require (tip: select all scopes and *No Expiration*), then click *Generate Token*. **Copy the new generated Token** + 3. Back on your computer: + 1. If you have been following the steps above, you should still be in your shell with GitHub still asking for your password. + 2. **Paste** your Token here, and you should be logging in. Your changes should then be saved to GitHub. + + ??? Info "Choice B: Connecting via SSH" + The process of connecting your computer to GitHub using an SSH key is more expedited (and probably less confusing). + + As a setup step, see if your computer is already connected to GitHub by doing `ssh -T git@github.com`. If the response message is `git@github.com: Permission denied (publickey).` it signifies that your computer is not yet linked with GitHub. To link your computer to github to the following: + + 1. Generate an SSH key with a level of encryption that you prefer: `ssh-keygen -t ed25519 -C `. This command generates an SSH key with [ed25519](https://ed25519.cr.yp.to/) encryption (harder to crack!) and adds your email as "comment" (`-C`, will help recongizing the user adding the key). A number of additional questions are going to ask you where you'd like to save the key and whether you'd like to add a password for protection; unless you want to save it elsewhere, feel free to use the default options. Upon completion you should see something like this: + ``` + Your identification has been saved in /c/Users//.ssh/id_ed25519 + Your public key has been saved in /c/Users//.ssh/id_ed25519.pub + The key fingerprint is: + SHA256:SMSPIStNyA00KPxuYu94KpZgRAYjgt9g4BA4kFy3g1o + The key's randomart image is: + +--[ED25519 256]--+ + |^B== o. | + |%*=.*.+ | + |+=.E =.+ | + | .=.+.o.. | + |.... . S | + |.+ o | + |+ = | + |.o.o | + |oo+. | + +----[SHA256]-----+ + ``` + 2. Upon generating the ssh key, copy it. You can reveal it by doing `cat ~/.ssh/id_ed25519.pub`. + 3. In GitHub, go to your settings: click your account icon on top right, and from the drop down menu, select *Settings* and then *SSH and GPG keys*. Here, click on *New SSH Key*, where you can then paste the newly geneated key. Add a name reflecting your machine and save changes. + + Optional: if you want to check if you successfully linked your computer to GitHub, do `ssh -t git@github.com`. You should receive the following message: `Hi ! You've successfully authenticated, but GitHub does not provide shell access. + +Adding code locally is a more complex than adding code through the web page, but it allows for better control on what files you commit. + +- To add or modify code locally, you need to **clone** the repository on your computer. +- You can then clone the repository by clicking on the **Code** button, and copying the link shown +- ![git_04](assets/git_4.png) +- On your machine, open a terminal window and type the following command: +``` +$ git clone # Replace with the link you copied such as below + +$ git clone https://github.com/CosiMichele/3_git_tutorial.git +Cloning into 'foss23_git_tutorial'... +remote: Enumerating objects: 13, done. +remote: Counting objects: 100% (13/13), done. +remote: Compressing objects: 100% (12/12), done. +remote: Total 13 (delta 5), reused 0 (delta 0), pack-reused 0 +Unpacking objects: 100% (13/13), 14.47 KiB | 90.00 KiB/s, done. +``` +- Your code is now available to you on your machine, and you can add and modify files as needed. + +You have modified your code locally, however you still have to push it to the repository. Prior to doing so there are a couple of steps you should do: + +- `git status`: it checkes on the status of the repository (files that have been modified, deleted, added - from either local or in the online repository) +- `git pull`: it checks and "pulls" changes from the online repository to your local repository. It ensures that you are always updated on the repository files *and* it can save a lot of time in case there are clashing commits from different users. + +To do so: + +- **Add** all fiels you have modified and want to commit: +``` +$ git add . # Recall that "." (period) stands for all files in a folder +``` +- **Commit** the changes. When committing changes, you have to add a message (in quotation marks) with the `-m` flag. This message is a concise and descriptive few words about what you did: +``` +$ git commit -m "locally added and modified files" +[main 05f0ef6] locally added and modified files + 2 files changed, 11 insertions(+), 1 deletion(-) + create mode 100644 file_from_local.md +``` +- push your changes with **push**: +``` +$ git push +Enumerating objects: 6, done. +Counting objects: 100% (6/6), done. +Delta compression using up to 12 threads +Compressing objects: 100% (4/4), done. +Writing objects: 100% (4/4), 585 bytes | 32.00 KiB/s, done. +Total 4 (delta 0), reused 0 (delta 0) +To https://github.com/CosiMichele/foss22_git_tutorial.git + b649de3..05f0ef6 main -> main +``` + +??? Warning "First time Pushing a commit and using Tokens?" + GitHub is not going to blindly allow you to push changes to the repo, but it will be asking for you to log in. + + - When asked for the user name: + - Add the username that you use to login into GitHub + - When it asks you for the password: + - **DO NOT PUT YOUR PASSWORD**, you will require a **token** instead + - Generate the token by: + - On GitHub, click on your avatar (top right, and navigate to **Settings**) + - Scroll down to the bottom of the left hand menu, select **Developer settings**, and then **Personal access tokens** + - Now click on **Generate new token** (Enter password if requested) + - Choose the lenght of time for which this token is valid for, a note (for example, a reminder of what computer you're using this token on), and all the functionalities attached to it (as this is your private repository, you can select all the functionalities). Scroll to the bottom of the page and click **Generate token** + - Once created, the token is going to appear: **copy the token and paste it in the password field in your terminal instead of your password**. + +You can now see the changes you made locally on the GitHub repository page. + +![git_09](assets/git_9.png) + +## Branching + +Branching allows you to develop your code whilst in a contained environment separate from your **main** environment. You can view the list and number of branches on the top of your repository. + +![git_10](assets/git_10.png) + +!!! Info "Why working on branches?" + Branches allow you to add/remove/change exisiting code independently from your main branch. This code can include alphas, betas and different versions of your code. Branches can be used to develop documentation or include different functionalitiets focused on Operating Systems and/or clusters and job schedulers. If needed, you can add these codes to your main branch later using [**pull requests**](00_basics.md#pull-requests). + +To create a new branch select the :octicons-git-branch-16: branch icon (listing the number of branches). This will open the branch page, which will list all of the branches in this repository. + +![git_11](assets/git_11.png) + +Select **New Branch** on the top right. Give the new branch a name of your choice, select the source of code (in this case the only source of code can be the main branch) and select **Create branch**. + +![git_12](assets/git_12.png) + +You can now see the updated list of all your branches. + +![git_13](assets/git_13.png) + +You can now use this new branch to create changes you are not yet ready to put in your main branch. + +!!! warning "Want to delete a branch?" + You can delete a branch from the branch web page by clicking on the :octicons-trash-16: trash can icon. **Beware!** All the changes you've made on that branch will be deleted! + +!!! info "Working on your machine?" + Once you create a branch online, you can change to the desired branch on your machine with `git switch `. Don't forget to push your changes first! + + !!! tip "Pull and Tab" + - Don't forget to perform a `git pull`! + - Don't know your branches? Tab! When typing `git switch`, press tab to see the options of all the branches you've created. + +## Pull Requests + +Pull requests (PR) are proposed changes you can make on a repository. In this specific case, pull requests can be used to merge changes from a branch to another. Pull requests can also come from **forks** of your repository that another user or collaborator has made. + +Assuming you have made changes in your branch (added a file, for example), a pop up will notify you that a branch has pushed some changes. In case you want to merge the branch and the main repository, you can review and merge by clicking the **Compare & pull request** button. However, you may want to wait until more changes are made. + +![git_14](assets/git_14.png) + +Once you are ready to merge the changes onto your main branch, click on the :octicons-git-branch-16: branch icon, and select **New pull request** from the branch you have just made changes. This will open a new page which will list all the changes made showing all files that have been modified, added, or deleted. When you're done reviewing your changes, click **Create pull request**. + +![git_15](assets/git_15.png) + +!!! info "Pay attention to the information on the PR page!" + The PR page will not only show you what changes you've made, but also where the changes are coming from (which branch), as well as reviewers, assigneers, labels and other information necessary when working on a big project. It will also show whether the changes are **Able** to be merged (:material-check:) or not (:octicons-x-16:)! + +Upon createing the pull request, a new page will open which will test whether the changes can be merged automatically. Changes that are not able to be merged usually clash with other changes other collaborators have made - this will require your revision prior to merging the PR! After revision, select **Merge pull request** and **Confirm merge**. + +![git_16](assets/git_16.png) + +Your main repository should now have the files created in your other branch and merged through the PR! + +![git_17](assets/git_17.png) + + +--- +--- +--- + + +## Introducing GitHub + +Since we are talking about making science accessible, we invite you to use GitHub to save and share your code. Please start by creating a GitHub account at https://github.com/. +
+
+ +#### User Profile + +Just like in any other social media platform, you can create a profile for yourself. This is where you can add a picture, a description of yourself, and a link to your website. You can also add your location, your organization, and your pronouns. You can have a list of your most important repositories and show off your daily contributions. You are able to customize your profile to your liking. Check out [this profile](https://github.com/Gchism94) for fancy example. + +![github_shot1](assets/github_shot1.png) + +
+ +#### Search + +At the top of most pages, is a search bar. Use this to find repositories, users, and organizations. You can also use it to search for specific code within a repository. +![github_shot1](assets/github_shot2.png) + +
+ +#### Starring Repositories + +You can star repositories that you like. This is a way to bookmark repositories that you want to come back to later. You can also use this to show your appreciation for a repository. You can see all of your starred repositories by clicking on your profile picture and then clicking on **Your stars**. + +![github_shot1](assets/github_shot3.png) + +
+ +#### Create Your Own Repository + +Repositories are where your code is stored. A suggestion is to have *one* repository for *one* project. + +You can create repositories by clicking on the **Repositories** tab, and then clicking **New**. + +![git_1](assets/git_1.png) + +Here, you can choose the name of your own repository, choose to make it private or public, adding a README and a licence. It is **strongly** reccomended that you choose to add an empty README file. +
+
+ +![git_2](assets/git_2.png) + +
+ + +!!! Info "So, why a README?" + There are two main reasons why you would like a README file: + + 1. It adds structure to your repository *automatically* - otherwise you would need to create said structure by yourself (not recommended for beginners). + 2. It is the "default" file that GitHub reads upon opening the repository. It can be treated as the go-to file that explains what the repository is for, what each file does, how to cite your reasearch, amongst other things. + + +!!! Info "Adding a Licence" + The addition of a licence can heavily contribute to the shareability of your code. Make sure that whichever licence you choose is in line with your principals as well as your project's. GitHub comes with a list of licences which you can review. It is also common to choose a licence later on! We will cover licences in more depth [later](03_managing_data.md#licences) in the course. + +
+ + +Ultimately, your new repository should look like the following screenshot. Notice the **LICENCE** document and the **README.md** + +![git_3](assets/git_3.png) + +!!! Info "Editing the README.md" + The Github repository file has a .md extension which stands for Markdown. Markdown is a lightweight markup language for creating formatted text using a plain-text editor common throughout text files on the web. It uses symbols (*~-#`) for syntaxing text, and it is what GitHub (and this website!) use to format text. Markdown is easier to use than HTML. You can read more on Markdown on the [Markdown Guide](https://www.markdownguide.org/). + +
+
+ +### Adding and Modifying Code in Github + +GitHub allows you to add and modify code in two ways: 1. through the online portal (the webpage you're seeing), and 2. On your local computer. Throughout the following section, we will show you how to do it through the online portal. We will save the local computer for later for [Lesson 5](05_version_control.md) later in the course. + + +Adding code to your repository through the web page is suggested if what you want to add is simple (Like a README file!). + +- Click the **Add File** button, which will allow you to either create a new file, or upload files from your computer. Select **Create New File**. +- The editing page will open: choose a name and an extension on the top of the page. +- On the editing page you can modify code as you see necessary (writing, pasting) + +![git_05](assets/git_5.png) + +- You can also see your changes (if formatted) with the preview function (with the **Preview** button). +- To "Save" your changes, you will need to **commit** your changes: + - navigate at the bottom of the page, specify your commit with a name and add a description if necessary. + ![git_06](assets/git_6.png) +- You will be able to see your newly created file on your repository home after committing your changes. + +!!! info "Committing changes" + **Committing** is the term used for *saving* changes you've made to your code. Each **commit** can be accessed within the GitHub web interface, which will show you the code prior and after the changes you've made. To see a list of all commits you made, click on the :fontawesome-solid-clock-rotate-left: icon under the **Code** button. + + - You can see from the picture below the lines that have been removed (in red), and the lines that have been added (in green). + ![git_07](assets/git_7.png) + + - Additionally, you can also see the full list of commits made to the file or repository. + ![git_08](assets/git_8.png) +
+ +#### Hosting Web Pages in Github + +GitHub allows you to host web pages through the use of **GitHub Pages**. This is a free service that allows you to host a website directly from your GitHub repository. You can use this to host your personal website, or to host a website for your project. + +For example, the [FOSS website](https://foss.cyverse.org/) is hosted through GitHub Pages. The repository for the website can be found [here](https://github.com/CyVerse-learning-materials/foss) + diff --git a/05_version_control/index.html b/05_version_control/index.html new file mode 100644 index 000000000..5b16cdc8a --- /dev/null +++ b/05_version_control/index.html @@ -0,0 +1,2094 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5. Version Control - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Version Control

+
+

Learning Objectives

+

After this lesson, you should be able to:

+
    +
  • Understand the basics of git as a resource for reproducible programming
  • +
  • Describe tools and approaches to creating your git Repositories
  • +
  • Describe best practices for maintaining GitHub Organizations and Repositories
  • +
  • Maintain own GitHub user profile and repositories
  • +
+
+

Version control refers to keeping track of the version of a file, set of +files, or a whole project.

+

Some version control tools:

+ +

Version control is as much a philosophy as a set of tools; you don't +need to master Git to utilize version control (though it is certainly a +worthwhile tool for many researchers).

+
+

git_def +

We have all been here, taken by the Software Carpentry Version Control lesson.

+
+
+

Git vs. GitHub

+

Git is a command-line program for version control of repositories. +It keeps track of changes you make to files in your repository and +stores those changes in a .git folder in that repository. +These changes happen whenever you make a commit. Git stores the +history of these commits in a "tree", so you can go back to any +previous commit. By keeping track of the differences between +commits, Git can be much more efficient than storing an entire copy of +each version in a document's history.

+

You could utilize Git completely on its own, on your local computer, and +get a lot of benefits. You will have a history of the changes you made +to a project, allowing you to go back to any old version of your work. +However, where Git really shines is in collaborative work. In order to +effectively collaborate with others on a project, you need two basic +features: a way to allow people to work in parallel, and a way to host +repositories somewhere where everyone can access them. The first feature +is branching, which is part of Git, and the hosting part can be +taken care of by platforms like GitHub, GitLab, or Bitbucket. We will +focus on GitHub.

+

GitHub is a site that can remotely host your Git repositories. By +putting your repository onto GitHub, you get a backup of the repository, +a way to collaborate with others, and a lot of other features.

+
    +
  • +

    Git:

    +
      +
    • First developed in 2005, git is a version control software that allows users to make changes and add versions to their code.
    • +
    • Changes and versions are saved locally.
    • +
    • Accessible through the Shell.
    • +
    +
  • +
  • +

    GitHub:

    +
      +
    • First launched in 2008, its main focus is hosting and sharing code.
    • +
    • Uses Git version control software.
    • +
    • Changes and versions are saved online (requires an account).
    • +
    • Mainly administered through the web (it also has a desktop app).
    • +
    • Acquired by Microsoft in 2018.
    • +
    +
  • +
+
+

git_v_github +

Git vs GitHub, simplified

+
+

Definitions

+
+

Git-related Definitions

+

Platforms:

+
    +
  • Git: tool for version control.
  • +
  • GitHub: hosted server that is also interactive.
  • +
+

Locations and directions:

+
    +
  • repo: short for repository
  • +
  • local: on your personal computer.
  • +
  • remote: somewhere other than your computer. GitHub can host remote +repositories.
  • +
  • upstream: primary or main branch of original repository.
  • +
  • downstream: branch or fork of repository.
  • +
+

Actions:

+
    +
  • clone: copy of a repository that lives locally on your computer. +Pushing changes will affect the repository online.
  • +
  • pull: getting latest changes to the repository on your local +computer.
      +
    • the fetch command does the same, however one needs to also merge the changes, whilst with pull, the merge action is automatic.
    • +
    +
  • +
  • branch: a history of changes to a repository. You can have parallel +branches with separate histories, allowing you to keep a "main" +version and development versions.
  • +
  • fork: copy of someone else's repository stored locally on your +account. From forks, you can make pull requests to the main branch.
  • +
  • commit: finalize a change.
  • +
  • push: add changes back to the remote repository.
  • +
  • merge: takes changes from a branch or fork and applies them to the +main.
  • +
+
+

These are also commands when paired with git!

+

Using the following synthax git <command> one can trigger an action. An example is git pull, which will pull all of the latest changes in the remote repository.

+
+

Funtional:

+
    +
  • +

    pull request: proposed changes to/within a repository.

    +
  • +
  • +

    issue: suggestions or tasks needed for the repository. Allows you to +track decisions, bugs with the repository, etc.

    +
  • +
+
+
+

git_def +

Visualizing the commands through a workflow example
(graphic's correction: marged merged)

+
+
+

Practical Git Techniques

+
+

vc_path +

The version control path sofware takes before release

+
+
+

The basic Git life cycle

+

When using Git for your version control, the usual life cycle is the following:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ActionExplanation
1. git clone <repository>Clones the target repository to your machine
2. git statusChecks whether there are changes in the remote, original repository
3. git pullPulls any change to your local repository
4. git add <changes>Adds to a future commit any change
5. git commit -m "<message>"Creates the commit and adds a descriptive message
6. git pushPushes the changes commited from local to the remote repository
+

If there are no branches or external pull requests, the basic Git life cycle is summarizable like this:

+
graph LR
+A[1. git clone] --> B[2. git status] -->C([differences from origin?]):::colorclass;
+C-->|yes| D[3. git pull]--> E;
+C-->|no| E[4. git add];
+E-->F[5. git commit] -->G[6. git push];
+G-->B;
+classDef colorclass fill:#f96
+
+

After learning the basics of using Git, which you can learn with the +Software Carpentry Git Lesson, there are some next +things that can be useful to learn. Here are a couple topics that are +worth digging into more:

+
    +
  • +

    Using the Git log

    +
      +
    • You can access using git log
    • +
    • Will show you your commit history
    • +
    • Useful for figuring out where you need to roll back to
    • +
    +
  • +
  • +

    Reverting

    + +
  • +
  • +

    Branching

    +
      +
    • This is important to learn if you're going to be doing any sort of collaboration
    • +
    • Here is a fantastic resource for learning how git branching really works: https://learngitbranching.js.org/
    • +
    • you will probably have to deal with merge conflicts at some point
        +
      • Merge conflicts happen when two branches are being merged, but they have different changes to the same part of a file
      • +
      • Perhaps you are working on a feature branch, and you change line 61 in file.R, but someone else made a change to the main branch at line 61 in file.R. When you try to merge the feature and main branches, Git won't know which changes to line 61 in file.R are correct, and you will need to manually decide.
      • +
      • Here are some good resources: +
      • +
      +
    • +
    +
  • +
  • +

    .gitignore

    +
      +
    • You often want Git to completely ignore certain files
    • +
    • Generated files (like HTML files from Markdown docs)
    • +
    • IDE-specific files like in .RStudio or .vscode folders
    • +
    • really big files, like data or images
        +
      • If you accidentally commit a really big file, GitHub might not let you push that commit
      • +
      • If you have a huge file in Git, your repository size can get way too big
      • +
      • This is a pain to solve, so use the .gitignore file ahead of time, but if you need to fix this, here is a great resource:
      • +
      • Removing Large Files From git Using BFG and a Local Repository
      • +
      +
    • +
    +
  • +
+
+

Large Data and GitHub

+

GitHub allows commited files to be uploaded only if the file is of 100MB or less (with a warning being issued for files between 50MB and 100MB). Additionally, GitHub recommends to keep repositories below the 1GB threshold, as this also allows for quicker cloning and sharing of the repository. If a large file has been uploaded by mistake and you wish to remove it, you can follow these instrutctions.

+

If you do have to work with large files and Git, here are some questions to ask yourself:

+
    +
  • Is this data shareable?
  • +
  • Are there alternative file hosting platforms I can use?
  • +
  • How will this data impact the sharability of this repository?
  • +
  • Am I using a .gitignore?
  • +
+

GitHub now offers the Git Large File Storage ( Git LFS): the system works by storing references to the file in your repository, but not the file itself -- it creates a pointer file within the repo, and stores the file elsewhere. If you were to clone the repository, the pointer file will act as a map to show you how to obtain the original file.

+

Git LFS data upload limits are based on your GitHub subscription:

+
    +
  • 2 GB for GitHub free and GitHub Pro
  • +
  • 4 GB for GitHub Team
  • +
  • 5 GB for GitHub Enterprise Cloud
  • +
+
+

gitlfs +

A depiction of how the Git LFS pointer-repository relationship works.

+
+
+

Useful GitHub Features

+

At its core, GitHub is just a place to host your Git repositories. +However, it offers a lot of functionality that has less to do with Git, +and more to do with Project Management. We will +walk through a few of these useful features.

+
    +
  • +

    Issues

    +
      +
    • Issues let you plan out changes and suggestions to a repo
    • +
    • Closing/reopening
    • +
    • Labels
    • +
    • Assigning
    • +
    • Templates
    • +
    • Numbering/mentioning
    • +
    +
  • +
  • +

    Pull Requests

    +
      +
    • Pull requests are a way to request merging code from one branch to another
    • +
    • typical workflow is for someone to fork a repo, then make a PR from that repo to another
    • +
    • Reviews
    • +
    • Commenting
    • +
    • Merging
    • +
    • Closing issues
    • +
    +
  • +
  • +

    Organizations

    +
      +
    • You can use Organizations to organize sets of repositories
    • +
    • Roles
    • +
    • Teams
    • +
    • GitHub documentation:
    • +
    +
  • +
  • +

    Other neat things

    +
      +
    • Permissions/collaborators
    • +
    • GitHub Classroom
    • +
    • Gists
    • +
    • CSV and map rendering
    • +
    • Code editor
    • +
    +
  • +
+
+

Beyond Git and GitHub

+

There are other platforms that address Version Control and have similar functionalities to GitHub:

+
    +
  • +

    GitLab: An alternative to GitHub, GitLab offers both a cloud-hosted platform and a self-hosted option (GitLab CE/EE). It provides a comprehensive DevOps platform with built-in CI/CD, container registry, and more.

    +
  • +
  • +

    Bitbucket: Atlassian's Bitbucket is a Git repository hosting service that also supports Mercurial repositories. It offers integration with Jira, Confluence, and other Atlassian products.

    +
  • +
  • +

    SourceForge: A platform that provides Git and Subversion hosting, as well as tools for project management, issue tracking, and collaboration.

    +
  • +
  • +

    AWS CodeCommit: Part of Amazon Web Services (AWS), CodeCommit is a managed Git service that integrates seamlessly with other AWS services.

    +
  • +
  • +

    :simple-azuredevops: Azure DevOps Services (formerly VSTS)): Microsoft's Azure DevOps Services offers Git repository hosting along with a wide range of DevOps tools for planning, developing, testing, and deploying software.

    +
  • +
  • +

    Mercurial: Like Git, Mercurial is a distributed version control system, but with a different branching and merging model. It's an alternative to Git for version control.

    +
  • +
+
+

Self Assessment

+
+True or False: Using Git requires a GitHub account +
+

False

+

Git is open source software.

+

GitHub is a privately owned (Microsoft) company

+

Other platforms like GitLab, GitBucket, and GNU Savannah all offer Git as a version control system service.

+
+
+
+True or False: Using Git is easy +
+

False

+

Using Git can be frustrating to even the most experienced users

+
+
+
+When you find a new repository on GitHub that you think can help your research, what are the first things you should do? +
+

Look at the README.md

+

Most GitHub repositories have a README.md file which explains what you're looking at.

+
+
+

Look at the LICENSE

+

Not all repositories are licensed the same way - be sure to check the LICENSE file to see whether the software is open source, or if it has specific requirements for reuse.

+
+
+

Adding Code to Github Locally

+
+

Prerequisites

+

You will require the following in case you want to add code locally.

+
+Create a GitHub account +

Navigate to the GitHub website and click Sign Up, and follow the on screen instructions.

+
+

Installing Git

+

You can follow the official guidelines here: https://github.com/git-guides/install-git. Here we recommend how to install Git on your local machine.

+
+Windows +
+

These instructions are for Windows users NOT using WSL2. If you do have WSL2, follow the Unix instructions.

+
+
    +
  1. Navigate to the latest Git for Windows installer and download the latest version.
  2. +
  3. Once the installer has started, follow the instructions as provided in the Git Setup wizard screen until the installation is complete.
  4. +
  5. Search and open Git Bash. From here, you should be able to run Git commands.
  6. +
+
+
+MacOS +
    +
  1. Install Homebrew (a package manager for MacOS): `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"``
  2. +
  3. Install Git: brew install git
  4. +
+
+
+Unix +

The following command will install git and all related packages on your Unix machine. +

$ sudo apt-get install -y git-all
+

+
+

Additionally, you can choose between Generating a Personal Access Token or using SSH keys. This is useful if you want to work locally and push your changes to GitHub. We are going to cover this further in next week's lesson on Version Control.

+
+Choice A: Generate a Personal Access Token +

You can follow the official documentation on how to generate Tokens here. We discussed how to generate tokens in Week 0. Here's are quick steps you can follow in order to setup your account on your machine using tokens:

+
    +
  1. On your coumputer:
      +
    1. Clone your repository (git clone <repository>)
    2. +
    3. Make changes where necessary, and add (git add <changed files>), commit (git commit -m "<message on changes>") and push your changes (git push origin).
    4. +
    5. You should be prompted to logging in your GitHub account. Put your email but not your password. Instead, open your web browser and follow the steps below:
    6. +
    +
  2. +
  3. On GitHub:
      +
    1. Navigate to your GitHub Settings (You can access your account Settings from the drop down menu where your account icon is, on the top right of the screen)
    2. +
    3. Scroll to the bottom of the left hand side menu to find Developer settings and open it.
    4. +
    5. Click Personal access tokens > Tokens (classic)
    6. +
    7. Click Generate new token > Generate new token (classic). You might need to input your Authentification code if you have enabled 2FA.
    8. +
    9. Give it a name, and all the scopes you require (tip: select all scopes and No Expiration), then click Generate Token. Copy the new generated Token
    10. +
    +
  4. +
  5. Back on your computer:
      +
    1. If you have been following the steps above, you should still be in your shell with GitHub still asking for your password.
    2. +
    3. Paste your Token here, and you should be logging in. Your changes should then be saved to GitHub.
    4. +
    +
  6. +
+
+
+Choice B: Connecting via SSH +

The process of connecting your computer to GitHub using an SSH key is more expedited (and probably less confusing).

+

As a setup step, see if your computer is already connected to GitHub by doing ssh -T git@github.com. If the response message is git@github.com: Permission denied (publickey). it signifies that your computer is not yet linked with GitHub. To link your computer to github to the following:

+
    +
  1. Generate an SSH key with a level of encryption that you prefer: ssh-keygen -t ed25519 -C <your github email>. This command generates an SSH key with ed25519 encryption (harder to crack!) and adds your email as "comment" (-C, will help recongizing the user adding the key). A number of additional questions are going to ask you where you'd like to save the key and whether you'd like to add a password for protection; unless you want to save it elsewhere, feel free to use the default options. Upon completion you should see something like this: +
    Your identification has been saved in /c/Users/<user>/.ssh/id_ed25519
    +Your public key has been saved in /c/Users/<user>/.ssh/id_ed25519.pub
    +The key fingerprint is:
    +SHA256:SMSPIStNyA00KPxuYu94KpZgRAYjgt9g4BA4kFy3g1o <your github email>
    +The key's randomart image is:
    ++--[ED25519 256]--+
    +|^B== o.          |
    +|%*=.*.+          |
    +|+=.E =.+         |
    +| .=.+.o..        |
    +|....  . S        |
    +|.+ o             |
    +|+ =              |
    +|.o.o             |
    +|oo+.             |
    ++----[SHA256]-----+
    +
  2. +
  3. Upon generating the ssh key, copy it. You can reveal it by doing cat ~/.ssh/id_ed25519.pub.
  4. +
  5. In GitHub, go to your settings: click your account icon on top right, and from the drop down menu, select Settings and then SSH and GPG keys. Here, click on New SSH Key, where you can then paste the newly geneated key. Add a name reflecting your machine and save changes.
  6. +
+

Optional: if you want to check if you successfully linked your computer to GitHub, do ssh -t git@github.com. You should receive the following message: `Hi ! You've successfully authenticated, but GitHub does not provide shell access.

+
+
+

Adding code locally is a more complex than adding code through the web page, but it allows for better control on what files you commit.

+
    +
  • To add or modify code locally, you need to clone the repository on your computer.
  • +
  • You can then clone the repository by clicking on the Code button, and copying the link shown
  • +
  • git_04
  • +
  • On your machine, open a terminal window and type the following command: +
    $ git clone <repository address>     # Replace <repository address> with the link you copied such as below
    +
    +$ git clone https://github.com/CosiMichele/3_git_tutorial.git
    +Cloning into 'foss23_git_tutorial'...
    +remote: Enumerating objects: 13, done.
    +remote: Counting objects: 100% (13/13), done.
    +remote: Compressing objects: 100% (12/12), done.
    +remote: Total 13 (delta 5), reused 0 (delta 0), pack-reused 0
    +Unpacking objects: 100% (13/13), 14.47 KiB | 90.00 KiB/s, done.
    +
  • +
  • Your code is now available to you on your machine, and you can add and modify files as needed.
  • +
+

You have modified your code locally, however you still have to push it to the repository. Prior to doing so there are a couple of steps you should do:

+
    +
  • git status: it checkes on the status of the repository (files that have been modified, deleted, added - from either local or in the online repository)
  • +
  • git pull: it checks and "pulls" changes from the online repository to your local repository. It ensures that you are always updated on the repository files and it can save a lot of time in case there are clashing commits from different users.
  • +
+

To do so:

+
    +
  • Add all fiels you have modified and want to commit: +
    $ git add .    # Recall that "." (period) stands for all files in a folder 
    +
  • +
  • Commit the changes. When committing changes, you have to add a message (in quotation marks) with the -m flag. This message is a concise and descriptive few words about what you did: +
    $ git commit -m "locally added and modified files"
    +[main 05f0ef6] locally added and modified files
    + 2 files changed, 11 insertions(+), 1 deletion(-)
    + create mode 100644 file_from_local.md
    +
  • +
  • push your changes with push: +
    $ git push
    +Enumerating objects: 6, done.
    +Counting objects: 100% (6/6), done.
    +Delta compression using up to 12 threads
    +Compressing objects: 100% (4/4), done.
    +Writing objects: 100% (4/4), 585 bytes | 32.00 KiB/s, done.
    +Total 4 (delta 0), reused 0 (delta 0)
    +To https://github.com/CosiMichele/foss22_git_tutorial.git
    +   b649de3..05f0ef6  main -> main
    +
  • +
+
+First time Pushing a commit and using Tokens? +

GitHub is not going to blindly allow you to push changes to the repo, but it will be asking for you to log in.

+
    +
  • When asked for the user name:
  • +
  • Add the username that you use to login into GitHub
  • +
  • When it asks you for the password:
  • +
  • DO NOT PUT YOUR PASSWORD, you will require a token instead
  • +
  • Generate the token by:
      +
    • On GitHub, click on your avatar (top right, and navigate to Settings)
    • +
    • Scroll down to the bottom of the left hand menu, select Developer settings, and then Personal access tokens
    • +
    • Now click on Generate new token (Enter password if requested)
    • +
    • Choose the lenght of time for which this token is valid for, a note (for example, a reminder of what computer you're using this token on), and all the functionalities attached to it (as this is your private repository, you can select all the functionalities). Scroll to the bottom of the page and click Generate token
    • +
    • Once created, the token is going to appear: copy the token and paste it in the password field in your terminal instead of your password.
    • +
    +
  • +
+
+

You can now see the changes you made locally on the GitHub repository page.

+

git_09

+

Branching

+

Branching allows you to develop your code whilst in a contained environment separate from your main environment. You can view the list and number of branches on the top of your repository.

+

git_10

+
+

Why working on branches?

+

Branches allow you to add/remove/change exisiting code independently from your main branch. This code can include alphas, betas and different versions of your code. Branches can be used to develop documentation or include different functionalitiets focused on Operating Systems and/or clusters and job schedulers. If needed, you can add these codes to your main branch later using pull requests.

+
+

To create a new branch select the branch icon (listing the number of branches). This will open the branch page, which will list all of the branches in this repository.

+

git_11

+

Select New Branch on the top right. Give the new branch a name of your choice, select the source of code (in this case the only source of code can be the main branch) and select Create branch.

+

git_12

+

You can now see the updated list of all your branches.

+

git_13

+

You can now use this new branch to create changes you are not yet ready to put in your main branch.

+
+

Want to delete a branch?

+

You can delete a branch from the branch web page by clicking on the trash can icon. Beware! All the changes you've made on that branch will be deleted!

+
+
+

Working on your machine?

+

Once you create a branch online, you can change to the desired branch on your machine with git switch <branch>. Don't forget to push your changes first!

+
+

Pull and Tab

+
    +
  • Don't forget to perform a git pull!
  • +
  • Don't know your branches? Tab! When typing git switch, press tab to see the options of all the branches you've created.
  • +
+
+
+

Pull Requests

+

Pull requests (PR) are proposed changes you can make on a repository. In this specific case, pull requests can be used to merge changes from a branch to another. Pull requests can also come from forks of your repository that another user or collaborator has made.

+

Assuming you have made changes in your branch (added a file, for example), a pop up will notify you that a branch has pushed some changes. In case you want to merge the branch and the main repository, you can review and merge by clicking the Compare & pull request button. However, you may want to wait until more changes are made.

+

git_14

+

Once you are ready to merge the changes onto your main branch, click on the branch icon, and select New pull request from the branch you have just made changes. This will open a new page which will list all the changes made showing all files that have been modified, added, or deleted. When you're done reviewing your changes, click Create pull request.

+

git_15

+
+

Pay attention to the information on the PR page!

+

The PR page will not only show you what changes you've made, but also where the changes are coming from (which branch), as well as reviewers, assigneers, labels and other information necessary when working on a big project. It will also show whether the changes are Able to be merged () or not ()!

+
+

Upon createing the pull request, a new page will open which will test whether the changes can be merged automatically. Changes that are not able to be merged usually clash with other changes other collaborators have made - this will require your revision prior to merging the PR! After revision, select Merge pull request and Confirm merge.

+

git_16

+

Your main repository should now have the files created in your other branch and merged through the PR!

+

git_17

+

---

+
+

Introducing GitHub

+

Since we are talking about making science accessible, we invite you to use GitHub to save and share your code. Please start by creating a GitHub account at https://github.com/. +
+

+

User Profile

+

Just like in any other social media platform, you can create a profile for yourself. This is where you can add a picture, a description of yourself, and a link to your website. You can also add your location, your organization, and your pronouns. You can have a list of your most important repositories and show off your daily contributions. You are able to customize your profile to your liking. Check out this profile for fancy example.

+

github_shot1

+


+ +

At the top of most pages, is a search bar. Use this to find repositories, users, and organizations. You can also use it to search for specific code within a repository. +github_shot1

+


+

Starring Repositories

+

You can star repositories that you like. This is a way to bookmark repositories that you want to come back to later. You can also use this to show your appreciation for a repository. You can see all of your starred repositories by clicking on your profile picture and then clicking on Your stars.

+

github_shot1

+


+

Create Your Own Repository

+

Repositories are where your code is stored. A suggestion is to have one repository for one project.

+

You can create repositories by clicking on the Repositories tab, and then clicking New.

+

git_1

+

Here, you can choose the name of your own repository, choose to make it private or public, adding a README and a licence. It is strongly reccomended that you choose to add an empty README file. +
+

+

git_2

+


+
+

So, why a README?

+

There are two main reasons why you would like a README file:

+
    +
  1. It adds structure to your repository automatically - otherwise you would need to create said structure by yourself (not recommended for beginners).
  2. +
  3. It is the "default" file that GitHub reads upon opening the repository. It can be treated as the go-to file that explains what the repository is for, what each file does, how to cite your reasearch, amongst other things.
  4. +
+
+
+

Adding a Licence

+

The addition of a licence can heavily contribute to the shareability of your code. Make sure that whichever licence you choose is in line with your principals as well as your project's. GitHub comes with a list of licences which you can review. It is also common to choose a licence later on! We will cover licences in more depth later in the course.

+
+


+

Ultimately, your new repository should look like the following screenshot. Notice the LICENCE document and the README.md

+

git_3

+
+

Editing the README.md

+

The Github repository file has a .md extension which stands for Markdown. Markdown is a lightweight markup language for creating formatted text using a plain-text editor common throughout text files on the web. It uses symbols (*~-#`) for syntaxing text, and it is what GitHub (and this website!) use to format text. Markdown is easier to use than HTML. You can read more on Markdown on the Markdown Guide.

+
+


+

+

Adding and Modifying Code in Github

+

GitHub allows you to add and modify code in two ways: 1. through the online portal (the webpage you're seeing), and 2. On your local computer. Throughout the following section, we will show you how to do it through the online portal. We will save the local computer for later for Lesson 5 later in the course.

+

Adding code to your repository through the web page is suggested if what you want to add is simple (Like a README file!).

+
    +
  • Click the Add File button, which will allow you to either create a new file, or upload files from your computer. Select Create New File.
  • +
  • The editing page will open: choose a name and an extension on the top of the page.
  • +
  • On the editing page you can modify code as you see necessary (writing, pasting)
  • +
+

git_05

+
    +
  • You can also see your changes (if formatted) with the preview function (with the Preview button).
  • +
  • To "Save" your changes, you will need to commit your changes:
      +
    • navigate at the bottom of the page, specify your commit with a name and add a description if necessary. + git_06
    • +
    +
  • +
  • You will be able to see your newly created file on your repository home after committing your changes.
  • +
+
+

Committing changes

+

Committing is the term used for saving changes you've made to your code. Each commit can be accessed within the GitHub web interface, which will show you the code prior and after the changes you've made. To see a list of all commits you made, click on the icon under the Code button.

+
    +
  • +

    You can see from the picture below the lines that have been removed (in red), and the lines that have been added (in green). +git_07

    +
  • +
  • +

    Additionally, you can also see the full list of commits made to the file or repository. +git_08

    +
  • +
+
+


+

Hosting Web Pages in Github

+

GitHub allows you to host web pages through the use of GitHub Pages. This is a free service that allows you to host a website directly from your GitHub repository. You can use this to host your personal website, or to host a website for your project.

+

For example, the FOSS website is hosted through GitHub Pages. The repository for the website can be found here

+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/06_reproducibility_I/06_reproducibility_I.md b/06_reproducibility_I/06_reproducibility_I.md new file mode 100644 index 000000000..356027bd2 --- /dev/null +++ b/06_reproducibility_I/06_reproducibility_I.md @@ -0,0 +1,591 @@ +# Reproducibility I: Software Environments + +!!! Success "Learning Objectives" + + After this lesson, you should be able to: + + * Understand the value of reproducible computing + * Know the challenges of reproducible computing + * Define a computing environment + * Share a software environment with a colleague + * Set up a software project with an environment + + + +
+
+
+ + +## Reproducible Scientific Computing + +
+ +!!! Quote "Defining Reproducibility" + + "Reproducing the result of a computation means running the same software on the same input data and obtaining the same results." Rougier et al. 2016 + + "Getting someone else's code to run on my computer" - Anonymous + + +
+
+
+ +As the graphic below suggests, _Reproducibility_ is a spectrum of sharing behaviors. + +
+ ![open science](../assets/reproducibility-spectrum.png){ width="600" } +
[Peng 2011](https://science.sciencemag.org/content/334/6060/1226)
+
+ +
+
+
+
+ +### Interactive (ie, point-and-click) Computing + +
+ ![open science](assets/pointer.png){ width="150" } +
+
+ +!!! quote "Definition" + + Manually navigating a mouse across a graphical user interface (GUI) and running commands by selecting from menu options. + +#### Advantages + +- Inuitive and easy to navigate a GUI and click buttons + +#### Limitations + +- It can be slow to sequence through hundreds of clicks to accomplish an analysis. +- Less reproducible - Cumbersome to write and follow a click-by-click tutorial + +
+
+
+
+ +### Scripted Computing + +
+ ![open science](assets/code.jpg){ width="400" } +
+
+ + +!!! quote "Definition" + + Removing the GUI and instead instructing the computer to run a series of custom commands using a scripting/coding language. + + We are **automating** what used to take many manual clicks. + + We can write scripts to install software, clean data, run analyses, and generate figures. + +
+ +#### Advantages + +- Much faster to run through commands +- The script runs identically every time, reducing the human element +- Easy for someone else to quickly reproduce the exact analysis and result +- Enables analysis tasks to scale up + +
+ +#### Challenges + +- Requires deeper computer knowledge +- More upfront effort to produce the script + +
+
+ +!!! question "Discussion Question" + **What are some tasks you have automated or want to automate?** + + - Have you ever successfully automated a task? + - Found a way to make something scale or take less time? + - What was the task, and how did you do it? + - Are there any things you wish you could automate? + - What are some barriers to automating them? + +
+
+
+
+ +## Scripting Languages + +The two most common open-source scripting languages (for science) are Python and R. + +
+ python + r +
+ +Both languages consist of base software (Python Standard Library or R Base Package) and MANY additional packages that can be downloaded and installed for increased capabilities. + +
+
+
+
+ +## Software Installation + +When you download and install software onto your computer, it will typically install it in a set of specific directories that we call the **System Path**. + +### System Path + +In the context of computing, the **system path**, often referred to simply as **PATH**, is the set of directories in which the operating system looks for executable files when a command is issued. + +When you go to launch an application by clicking on a desktop icon or with a CLI command, the computer will search for the application within the PATH directories. If it finds the executable, it will launch. + +!!! Tip "Find the PATH on your computer" + + In Linux and Mac Terminal + + `echo $PATH` + +
+ + In Windows Terminal + + `echo %PATH%` + +
+
+ +
+ +
Nice and Short Video Describing the PATH.
+
+ +
+
+ +!!! Warning "The PATH prefers one version of any given software." + +
+
+
+
+ +## Computing Environment + +A computing environment is the combination of hardware, software, and network resources that provide the infrastructure for computing operations and user interactions. + +- **Hardware**: CPUs, GPUs, RAM +- **Operating system & version**: many flavors of Linux, MacOS, Windows +- **Software versions:** R, Python, etc. +- **Package versions:** specific R or Python packages, which often depend on other packages + +
+ ![open science](assets/dependency.png){ width="400" } +
Python Package Dependency
+
+ +
+
+ +#### The scripts you create: + +* Were designed to work in _your_ specific computing environment +* May not work on your computer in the future, because your computing enviroment will probably change (eg., updated software versions) +* May not work on someone else's computer because their computing environment is different + +
+
+
+
+ +### Software Dependency Hell + +Sometimes, it can be _nearly impossible_ to get your computing environment correct enough to run someone else's code. + +This can caused by incorrect software versions of the packages you are using or their dependencies. + +Updating software installed in the **system path** - to make new code work - can break old code! + +
+
+
+ +## Environment Managers + +One solution to software dependency hell is to use an Environment Manager + +An environment manager allows you to create software installation directories (similar to PATH) that are **isolated** your computer's PATH. You can create unique environments and install specific software version to run specific scripts. + +
+ +### :simple-anaconda: Conda - Open Source Environment Manager + +[Conda](https://docs.conda.io/en/latest/) is a popular and open source environment manager tool that can be installed on any operating system (Windows, MacOS, Linux). + +* Users can create environments that have their own set of packages, dependencies, and even their own version of Python. +* Projects can have their own specific requirements without interfering with each other +* It allows for consistent and reproducible results across different systems and setups + +
+ ![open science](assets/envs.png){ width="450" } +
+
+
+
+ +### :simple-r: [Renv](https://rstudio.github.io/renv/articles/renv.html) + +* R package that allows you to create unique environments for an R project + +
+
+
+ +## Package Managers + +A software tool to find, download, and install software packages to PATH or virtual environment + +### :simple-anaconda: [Conda](https://docs.conda.io/en/latest/) +**Software:** Python, R, Django, Celery, PostgreSQL, nginx, Node.js, Java programs, C and C++, Perl, and command line tools + +**Repository:** [Conda-Forge](https://conda-forge.org/). + + +
+ +### :simple-python: [Pip](https://pypi.org/project/pip/) + +**Software:** python + +**Repository:** [PyPi](https://pypi.org/) + +**Note:** Pip can be used together with Conda environment manager. + +
+ +### :simple-r: R +With the R language, a package manager is built directly into the R Base Package. +``` +install.packages('ggplot2') +``` + +**Repository:** [R Comprehensive R Archive Network (CRAN)](https://cran.r-project.org/) + + + + +
+
+ +## Sharing your Environment with Colleagues + +Whether you are using Conda, Pip, or Renv, you should be able to share the specifications of your software environment so colleagues can reproduce the environment. + +The general sharing workflow: + +1. Output an _environment file_ that lists the software and versions of the environment + +2. Share the file with colleagues through a platform like Github + +3. Colleagues create an empty environment on their computer and populate it with the contents of the _environment file_ + +
+ +### :simple-anaconda: [Conda](https://docs.conda.io/en/latest/) + +1. Export your Conda Environment + ``` + conda env export > my_conda_env.yml + ``` + +2. Share the .yml file through Github + + +3. Reproduce the Environment on a Different Computer + ``` + conda env create --file environment.yml + ``` +!!! Success "Conda exports your Pip environment as well" + Exporting your environment using Conda (`conda env export > my_conda_env.yml`) will **ALSO** export your pip environment! +
+
+ +### :simple-python: Python + +1. Export python libraries present in your environment + ``` + pip3 freeze > requirements.txt + ``` + +2. Share the `requirements.txt` on Github + +3. Reproduce the Environment on a Different Computer + ``` + pip install -r requirements.txt + ``` + + +
+
+ +### :simple-r: [Renv](https://rstudio.github.io/renv/articles/renv.html) + +1. Create an isolated environment + ``` + renv::init() + ``` + +2. Export R packages to the renv.lock file + ``` + renv:snapshot() + ``` +3. Share the `renv.lock`, `.Rprofile`, `renv/settings.json` and `renv/activate.R` files to Github + +4. Reproduce the Environment on a Different Computer + ``` + renv::restore() + ``` + +
+
+ +--- + +
+
+
+
+ +--- + +## Reproducibility Tutorial + +
+ +### Installing Conda + +When you download and install Conda it comes in two different flavors: + +[Miniconda](https://docs.anaconda.com/free/miniconda/) - lightweight (500 mb) program that includes _Conda_, the environment and package manager, as well as a recent version of the Standard Python Library. + +[Anaconda](https://anaconda.org/) - a larger (2.5GB) program that includes _Conda_ and many more python libraries pre-installed (in Conda base environment), as well as graphical user interface, acccess to jupyter notebooks, and support for easily integrating the R language. + +
+ ![conda](https://miro.medium.com/v2/resize:fit:720/format:webp/0*ElVyaAsDHkIpNgxk.png) +
Conda, Miniconda, and Anaconda.
[Taken from *Getting Started with Conda, Medium*](https://medium.com/hydroinformatics/getting-started-with-conda-environment-332182d1e937).
+
+ +??? Tip "Installing Conda" + + + For the appropriate installation package, visit https://docs.conda.io/en/latest/miniconda.html. :warning: Note: **If you are using the WSL, install the Linux version!!** + + ``` + # Download conda and add right permissions + wget https://repo.anaconda.com/miniconda/Miniconda3-py39_4.12.0-Linux-x86_64.sh # Modify this to match the OS you're using. + chmod +x Miniconda3-py39_4.12.0-Linux-x86_64.sh + + # install conda silenty (-b) and update (-u) and initial conda run + ./Miniconda3-py39_4.12.0-Linux-x86_64.sh -b -u + ~/miniconda3/bin/conda init + + # Restart bash so that conda is activated + source ~/.bashrc + ``` + + You'll be able to tell when conda is active when next `(base)` is present next to the to the shell prompt such as + + ``` + (base) user@machine + ``` + + Conda should now be installed and can be used to install other necessary packages! + +
+ +??? tip "Tip: slow Conda? Try Mamba." + + Conda is known to take time processing some software installation. A solution is to use [Mamba](https://github.com/mamba-org/mamba), a reimplementation of Conda in C++ for quicker queries and installations. Mamba is then invoked by using `mamba` instead of `conda` (whilst keeping options and the rest of the command synthax the same). + + The quickest way to install mamba is with `conda install -c conda-forge mamba`, or follow the official installation documentation [here](https://mamba.readthedocs.io/en/latest/installation/mamba-installation.html). + +
+
+
+
+
+ + +### Conda on Cyverse + +!!! Note "OS of choice" + + This tutorial will be performed using the [CyVerse CLI (Command Line Interface)](https://de.cyverse.org/apps/de/5f2f1824-57b3-11ec-8180-008cfa5ae621) which is a Linux Command Line. This requires a [Cyverse account](https://user.cyverse.org/signup). + + However, if you'd like to use your own computer feel free to! If you're on Mac or Linux, open your terminal; If you're on Windows, please use the Windows Subsystem for Linux (WSL) so you can follow along. + + ??? Tip "How to Scroll in Cyverse (Tmux) Cloud Shell" + + If you're using the Cyverse Cloud Shell, you can scroll up and down by pressing `Ctrl + b` and then `[` to enter scroll mode. You can then use the arrow keys to scroll up and down. Press `q` to exit scroll mode. + + !!! Note "The CLI in CyVerse is controlled with [Tmux](https://en.wikipedia.org/wiki/Tmux), a software that allows to "window" the CLI; Here is a [cheat sheet](https://tmuxcheatsheet.com/) that will teach you more Tmux tricks!" + +
+
+ +### Environment Management with Conda + +When you start a Cyverse Cloud shell, the prompt will look something like this: + +``` +(base) jovyan@a12b272e0:/home/user/data-store$ +``` +
+Miniconda has already been pre-installed, and by default, you are started in a base Conda directory (base) + +
+ + + +View the list of conda environments +``` +conda env list +``` +
+ +View the software installed in the base directory. Notice the version of Python. +``` +conda list +``` +
+
+ +Create our own custom environment (type `y` when prompted). + +``` +conda create --name myenv +``` + +
+
+ +Activate your new environment with + +``` +conda activate myenv +``` +
+You will notice that the prompt changed to `(myenv)` + +
+ +View the software that is installed in your new custom environment. It should be empty! + +``` +conda list +``` + + +
+
+
+
+ +### Package management with Conda + +Within your new custom environment (ie, myenv) download and install a specific version of python. This may take a few minutes to complete. + +``` +conda install python=3.9 +``` + +
+ +View the new software that has been install +``` +conda list +``` + + +
+
+
+ +Install Salmon and FastQC (genomics software) using Conda + +``` +conda install -c bioconda salmon fastqc +``` + +
+ +!!! Info "Conda channels" + + Conda operates through **channels**, specififc repositories where packages are stored. Specific packages sometimes may appear in multiple channels, however it is always helpful to specify a channel with the `-c` flag. + +
+
+
+ +### Share and Reproduce a Conda Environment + +Export all of the software in your custom environment to a file + +``` +conda env export > myenv.yml +``` +
+Let's view the contents of the .yml file +``` +nano myenv.yml +``` +
+ + +Now we are going to pretend that we are reproducing a conda environment from a .yml file shared by a collegue. +
+ +Change the `name` of the environement within the .yml file from `myenv` to `myenv2` + + +
+Create a new environment and populate it with the .yml environment file + +``` +conda env create --file myenv2.yml +``` + + +
+
+
+
+ +### Package management with Pip + +Pip works similarly to Conda, as Pip is the package management supported by the Python Software foundation. If you use Python for your work it is likely you have installed packages using Pip. + +We only have to install a single package required for this tutorial, MultiQC. To install MultiQC using Pip, do: + +``` +pip install multiqc +``` + +Similar to Conda, you can export your pip environment by doing + +``` +pip3 freeze > requirements.txt +``` +
+ +!!! Note "Why `pip3`?" + `pip3 freeze > requirements.txt` is used to export the pip environment such that it is readable for Python 3. If you want to export an environment for Python 2, you can use `pip freeze > requirements.txt`. +
+
+
+
+ diff --git a/06_reproducibility_I/index.html b/06_reproducibility_I/index.html new file mode 100644 index 000000000..67d92f755 --- /dev/null +++ b/06_reproducibility_I/index.html @@ -0,0 +1,2198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6. Reproducibility I: Software Environments - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Reproducibility I: Software Environments

+
+

Learning Objectives

+

After this lesson, you should be able to:

+
    +
  • Understand the value of reproducible computing
  • +
  • Know the challenges of reproducible computing
  • +
  • Define a computing environment
  • +
  • Share a software environment with a colleague
  • +
  • Set up a software project with an environment
  • +
+
+


+
+

+

Reproducible Scientific Computing

+


+
+

Defining Reproducibility

+

"Reproducing the result of a computation means running the same software on the same input data and obtaining the same results." Rougier et al. 2016

+

"Getting someone else's code to run on my computer" - Anonymous

+
+


+
+

+

As the graphic below suggests, Reproducibility is a spectrum of sharing behaviors.

+
+

open science +

Peng 2011

+
+


+
+
+

+

Interactive (ie, point-and-click) Computing

+
+

open science +

+
+
+

Definition

+

Manually navigating a mouse across a graphical user interface (GUI) and running commands by selecting from menu options.

+
+

Advantages

+
    +
  • Inuitive and easy to navigate a GUI and click buttons
  • +
+

Limitations

+
    +
  • It can be slow to sequence through hundreds of clicks to accomplish an analysis.
  • +
  • Less reproducible - Cumbersome to write and follow a click-by-click tutorial
  • +
+


+
+
+

+

Scripted Computing

+
+

open science +

+
+
+

Definition

+

Removing the GUI and instead instructing the computer to run a series of custom commands using a scripting/coding language.

+

We are automating what used to take many manual clicks.

+

We can write scripts to install software, clean data, run analyses, and generate figures.

+
+


+

Advantages

+
    +
  • Much faster to run through commands
  • +
  • The script runs identically every time, reducing the human element
  • +
  • Easy for someone else to quickly reproduce the exact analysis and result
  • +
  • Enables analysis tasks to scale up
  • +
+


+

Challenges

+
    +
  • Requires deeper computer knowledge
  • +
  • More upfront effort to produce the script
  • +
+


+

+
+

Discussion Question

+

What are some tasks you have automated or want to automate?

+
    +
  • Have you ever successfully automated a task?
  • +
  • Found a way to make something scale or take less time?
  • +
  • What was the task, and how did you do it?
  • +
  • Are there any things you wish you could automate?
  • +
  • What are some barriers to automating them?
  • +
+
+


+
+
+

+

Scripting Languages

+

The two most common open-source scripting languages (for science) are Python and R.

+
+ python + r +
+ +

Both languages consist of base software (Python Standard Library or R Base Package) and MANY additional packages that can be downloaded and installed for increased capabilities.

+


+
+
+

+

Software Installation

+

When you download and install software onto your computer, it will typically install it in a set of specific directories that we call the System Path.

+

System Path

+

In the context of computing, the system path, often referred to simply as PATH, is the set of directories in which the operating system looks for executable files when a command is issued.

+

When you go to launch an application by clicking on a desktop icon or with a CLI command, the computer will search for the application within the PATH directories. If it finds the executable, it will launch.

+
+

Find the PATH on your computer

+

In Linux and Mac Terminal

+

echo $PATH

+


+

In Windows Terminal

+

echo %PATH%

+
+


+

+
+ +
Nice and Short Video Describing the PATH.
+
+


+

+
+

The PATH prefers one version of any given software.

+
+


+
+
+

+

Computing Environment

+

A computing environment is the combination of hardware, software, and network resources that provide the infrastructure for computing operations and user interactions.

+
    +
  • Hardware: CPUs, GPUs, RAM
  • +
  • Operating system & version: many flavors of Linux, MacOS, Windows
  • +
  • Software versions: R, Python, etc.
  • +
  • Package versions: specific R or Python packages, which often depend on other packages
  • +
+
+

open science +

Python Package Dependency

+
+


+

+

The scripts you create:

+
    +
  • Were designed to work in your specific computing environment
  • +
  • May not work on your computer in the future, because your computing enviroment will probably change (eg., updated software versions)
  • +
  • May not work on someone else's computer because their computing environment is different
  • +
+


+
+
+

+

Software Dependency Hell

+

Sometimes, it can be nearly impossible to get your computing environment correct enough to run someone else's code.

+

This can caused by incorrect software versions of the packages you are using or their dependencies.

+

Updating software installed in the system path - to make new code work - can break old code!

+


+
+

+

Environment Managers

+

One solution to software dependency hell is to use an Environment Manager

+

An environment manager allows you to create software installation directories (similar to PATH) that are isolated your computer's PATH. You can create unique environments and install specific software version to run specific scripts.

+


+

Conda - Open Source Environment Manager

+

Conda is a popular and open source environment manager tool that can be installed on any operating system (Windows, MacOS, Linux).

+
    +
  • Users can create environments that have their own set of packages, dependencies, and even their own version of Python.
  • +
  • Projects can have their own specific requirements without interfering with each other
  • +
  • It allows for consistent and reproducible results across different systems and setups
  • +
+
+

open science +

+
+


+

+

Renv

+
    +
  • R package that allows you to create unique environments for an R project
  • +
+


+
+

+

Package Managers

+

A software tool to find, download, and install software packages to PATH or virtual environment

+

Conda

+

Software: Python, R, Django, Celery, PostgreSQL, nginx, Node.js, Java programs, C and C++, Perl, and command line tools

+

Repository: Conda-Forge.

+


+

Pip

+

Software: python

+

Repository: PyPi

+

Note: Pip can be used together with Conda environment manager.

+


+

R

+

With the R language, a package manager is built directly into the R Base Package. +

install.packages('ggplot2')
+

+

Repository: R Comprehensive R Archive Network (CRAN)

+


+

+

Sharing your Environment with Colleagues

+

Whether you are using Conda, Pip, or Renv, you should be able to share the specifications of your software environment so colleagues can reproduce the environment.

+

The general sharing workflow:

+
    +
  1. +

    Output an environment file that lists the software and versions of the environment

    +
  2. +
  3. +

    Share the file with colleagues through a platform like Github

    +
  4. +
  5. +

    Colleagues create an empty environment on their computer and populate it with the contents of the environment file

    +
  6. +
+


+

Conda

+
    +
  1. +

    Export your Conda Environment +

    conda env export > my_conda_env.yml
    +

    +
  2. +
  3. +

    Share the .yml file through Github

    +
  4. +
  5. +

    Reproduce the Environment on a Different Computer +

    conda env create --file environment.yml
    +

    +
  6. +
+
+

Conda exports your Pip environment as well

+

Exporting your environment using Conda (conda env export > my_conda_env.yml) will ALSO export your pip environment!

+
+


+

+

Python

+
    +
  1. +

    Export python libraries present in your environment +

    pip3 freeze > requirements.txt 
    +

    +
  2. +
  3. +

    Share the requirements.txt on Github

    +
  4. +
  5. +

    Reproduce the Environment on a Different Computer +

    pip install -r requirements.txt
    +

    +
  6. +
+


+

+

Renv

+
    +
  1. +

    Create an isolated environment +

    renv::init()
    +

    +
  2. +
  3. +

    Export R packages to the renv.lock file +

    renv:snapshot()
    +

    +
  4. +
  5. +

    Share the renv.lock, .Rprofile, renv/settings.json and renv/activate.R files to Github

    +
  6. +
  7. +

    Reproduce the Environment on a Different Computer +

    renv::restore()
    +

    +
  8. +
+


+

+
+


+
+
+

+
+

Reproducibility Tutorial

+


+

Installing Conda

+

When you download and install Conda it comes in two different flavors:

+

Miniconda - lightweight (500 mb) program that includes Conda, the environment and package manager, as well as a recent version of the Standard Python Library.

+

Anaconda - a larger (2.5GB) program that includes Conda and many more python libraries pre-installed (in Conda base environment), as well as graphical user interface, acccess to jupyter notebooks, and support for easily integrating the R language.

+
+

conda +

Conda, Miniconda, and Anaconda.
Taken from Getting Started with Conda, Medium.

+
+
+Installing Conda +

For the appropriate installation package, visit https://docs.conda.io/en/latest/miniconda.html. ⚠ Note: If you are using the WSL, install the Linux version!!

+
# Download conda and add right permissions
+wget https://repo.anaconda.com/miniconda/Miniconda3-py39_4.12.0-Linux-x86_64.sh     # Modify this to match the OS you're using.
+chmod +x Miniconda3-py39_4.12.0-Linux-x86_64.sh
+
+# install conda silenty (-b) and update (-u) and initial conda run
+./Miniconda3-py39_4.12.0-Linux-x86_64.sh -b -u
+~/miniconda3/bin/conda init
+
+# Restart bash so that conda is activated
+source ~/.bashrc
+
+

You'll be able to tell when conda is active when next (base) is present next to the to the shell prompt such as

+
(base) user@machine
+
+

Conda should now be installed and can be used to install other necessary packages!

+
+


+
+Tip: slow Conda? Try Mamba. +

Conda is known to take time processing some software installation. A solution is to use Mamba, a reimplementation of Conda in C++ for quicker queries and installations. Mamba is then invoked by using mamba instead of conda (whilst keeping options and the rest of the command synthax the same).

+

The quickest way to install mamba is with conda install -c conda-forge mamba, or follow the official installation documentation here.

+
+


+
+
+
+

+

Conda on Cyverse

+
+

OS of choice

+

This tutorial will be performed using the CyVerse CLI (Command Line Interface) which is a Linux Command Line. This requires a Cyverse account.

+

However, if you'd like to use your own computer feel free to! If you're on Mac or Linux, open your terminal; If you're on Windows, please use the Windows Subsystem for Linux (WSL) so you can follow along.

+
+How to Scroll in Cyverse (Tmux) Cloud Shell +

If you're using the Cyverse Cloud Shell, you can scroll up and down by pressing Ctrl + b and then [ to enter scroll mode. You can then use the arrow keys to scroll up and down. Press q to exit scroll mode.

+
+

The CLI in CyVerse is controlled with Tmux, a software that allows to "window" the CLI; Here is a cheat sheet that will teach you more Tmux tricks!

+
+
+
+


+

+

Environment Management with Conda

+

When you start a Cyverse Cloud shell, the prompt will look something like this:

+

(base) jovyan@a12b272e0:/home/user/data-store$
+
+
+Miniconda has already been pre-installed, and by default, you are started in a base Conda directory (base)

+


+

View the list of conda environments +

conda env list
+
+

+

View the software installed in the base directory. Notice the version of Python. +

conda list
+
+
+

+

Create our own custom environment (type y when prompted).

+
conda create --name myenv
+
+


+

+

Activate your new environment with

+

conda activate myenv
+
+
+You will notice that the prompt changed to (myenv)

+


+

View the software that is installed in your new custom environment. It should be empty!

+
conda list
+
+


+
+
+

+

Package management with Conda

+

Within your new custom environment (ie, myenv) download and install a specific version of python. This may take a few minutes to complete.

+
conda install python=3.9
+
+


+

View the new software that has been install +

conda list
+

+


+
+

+

Install Salmon and FastQC (genomics software) using Conda

+
conda install -c bioconda salmon fastqc
+
+


+
+

Conda channels

+

Conda operates through channels, specififc repositories where packages are stored. Specific packages sometimes may appear in multiple channels, however it is always helpful to specify a channel with the -c flag.

+
+


+
+

+

Share and Reproduce a Conda Environment

+

Export all of the software in your custom environment to a file

+

conda env export > myenv.yml
+
+
+Let's view the contents of the .yml file +
nano myenv.yml
+
+

+

Now we are going to pretend that we are reproducing a conda environment from a .yml file shared by a collegue. +

+

Change the name of the environement within the .yml file from myenv to myenv2

+


+Create a new environment and populate it with the .yml environment file

+
conda env create --file myenv2.yml
+
+


+
+
+

+

Package management with Pip

+

Pip works similarly to Conda, as Pip is the package management supported by the Python Software foundation. If you use Python for your work it is likely you have installed packages using Pip.

+

We only have to install a single package required for this tutorial, MultiQC. To install MultiQC using Pip, do:

+
pip install multiqc
+
+

Similar to Conda, you can export your pip environment by doing

+

pip3 freeze > requirements.txt
+
+

+
+

Why pip3?

+

pip3 freeze > requirements.txt is used to export the pip environment such that it is readable for Python 3. If you want to export an environment for Python 2, you can use pip freeze > requirements.txt.

+
+


+
+
+

+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/07_reproducibility_II/07_reproducibility_II.md b/07_reproducibility_II/07_reproducibility_II.md new file mode 100644 index 000000000..57cba074c --- /dev/null +++ b/07_reproducibility_II/07_reproducibility_II.md @@ -0,0 +1,743 @@ +# Reproducibility II: Run Containers + +!!! Success "Learning Objectives" + + After this lesson, you should be able to: + + * Explain what containers are used for in reproducible research contexts + * Search for and run a Docker container locally or on a remote system + * Understand how version control and data can be used inside a container + +## Reproducible Computer Code + +Sharing your scientific analysis code with your colleagues is an essential pillar of Open Science that will help push your field forward. + +There are, however, technical challenges that may prevent your colleagues from effectively running your code on their computers. In the previous lesson [Reproducibility I](06_reproducibility_I.md) we described [computing environments](06_reproducibility_I.md/#computing-environment) and how they can make it difficult to run code from one computer to another. + +
+ +### Solutions for Sharing Computer Code + +1. Create a custom environment and share the recipe so your colleauges can replicate on their computer (as shown in [Reproducibility I](06_reproducibility_I.md)) + +2. Package up the code _and_ all of the software and send it to your colleague as a **Container** + +
+ +## What *are* containers? + +A [container](https://www.docker.com/resources/what-container/) is a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another + +* Container images are a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings + +* Each of these elements are specifically versioned and do not change + +* The recipient does not need to *install* the software in the traditional sense + +
+ +A useful analogy is to think of software containers as shipping containers. It allows us move cargo (software) around the world in standard way. The shipping container can be offloading and executed anywhere, as long the destination has a shipping port (i.e., Docker) + +
+ ![open science](../assets/shipping.jpg){ width="500" } +
Software Shipping Containers
+
+ +
+ +Containers are similar to virtual machines (VMs), but are smaller and easier to share. A big distinction between Containers and VMs is what is within each environment: VMs require the OS to be present within the image, whilst containers rely on the host OS and the container engine (e.g., Docker Engine). + +
+ ![containerexp](https://cloudblogs.microsoft.com/wp-content/uploads/sites/37/2019/07/Demystifying-containers_image1.png) +
Difference between Virtual Machines and Containers. Containers are a lot more portable as these do not require an OS to be bundled with the software. Figure source: [Microsoft Cloudblogs](https://cloudblogs.microsoft.com/opensource/2019/07/15/how-to-get-started-containers-docker-kubernetes/).
+
+ +
+ +## Containers for Reproducible Science +Software containers, such as those managed by Docker or Singularity, are incredibly useful for reproducible science for several reasons: + +#### Environment Consistency: +Containers encapsulate the software environment, ensuring that the same versions of software, libraries, and dependencies are used every time, reducing the "it works on my machine" problem. + +#### Ease of Sharing: + +Containers can be easily shared with other researchers, allowing them to replicate the exact software environment used in a study. + +#### Platform Independence: + +Containers can run on different operating systems and cloud platforms, allowing for consistency across different hardware and infrastructure. + +#### Version Control: + +Containers can be versioned, making it easy to keep track of changes in the software environment over time. + +#### Scalability: + +Containers can be easily scaled and deployed on cloud infrastructure, allowing for reproducible science at scale. + +#### Isolation: + +Containers isolate the software environment from the host system, reducing the risk of conflicts with other software and ensuring a clean and controlled environment. + +
+
+ +* The most common container software is [:material-docker: Docker](https://www.docker.com/){target=_blank}, which is a platform for developers and sysadmins to develop, deploy, and run applications with containers. [Apptainer](https://apptainer.org/docs/user/main/) (formerly, Singularity), is another popular container engine, which allows you to deploy containers on HPC clusters. + +* [DockerHub](https://hub.docker.com/) is the world's largest respository of container images. Think of it as the 'Github' of container images. It facilitates collaboration amongst developers and allows you to share your container images with the world. Dockerhub allows users to maintain different versions of container images. + +!!! Warning + + While Docker allows you to quickly run software from other people, it may not work across every platform. There are different CPU architectures (`arm`, `amd64`, `x64, `x86`) deployed across cloud, computer workstations, laptops, and cellular phones. Docker containers and their software can be cross-compiled across architectures, but this must be done by the creators. + +
+ + + +--- + +
+ +## Introduction to :material-docker: Docker + +
+ ![gitlfs](https://cc.cyverse.org/assets/docker/docker.png) +
+ +#### Prerequisites + +In order to complete these exercises we **STRONGLY** recommend that you set up a personal [:material-github: GitHub](https://github.com){target=_blank} and [:material-docker: DockerHub](https://hub.docker.com){target=_blank} account (account creation for both services is free). + +There are no specific skills needed for this tutorial beyond elementary command line ability and using a text editor. + +We are going to be using [:material-github: GitHub CodeSpaces](https://github.com/features/codespaces){target=_blank} for the hands on portion of the workshop, which features [:material-microsoft-visual-studio-code: VS Code](https://code.visualstudio.com/){target=_blank} as a fully enabled development environment with Docker already installed. + + +Our instructions on starting a new CodeSpace are [here](https://cc.cyverse.org/cloud/codespaces/){target=_blank}. + +??? Info "Installing Docker on your personal computer" + + We are going to be using virtual machines on the cloud for this course, and we will explain why this is a good thing, but there may be a time when you want to run Docker on your own computer. + + Installing Docker takes a little time but it is reasonably straight forward and it is a one-time setup. + + Installation instructions from Docker Official Docs for common OS and chip architectures: + + - [:fontawesome-brands-apple: Mac OS X](https://docs.docker.com/docker-for-mac/){target=_blank} + - [:fontawesome-brands-windows: Windows](https://docs.docker.com/docker-for-windows){target=_blank} + - [:fontawesome-brands-ubuntu: Ubuntu Linux](https://docs.docker.com/install/linux/docker-ce/ubuntu/){target=_blank} + +??? Failure "Never used a terminal before?" + + Before venturing much further, you should review the [Software Carpentry](https://software-carpentry.org/lessons/){target=_blank} lessons on "The Unix Shell" and "Version Control with Git" -- these are great introductory lessons related to the skills we're teaching here. + + You've given up on ever using a terminal? No problem, Docker can be used from graphic interfaces, like [Docker Desktop](https://www.docker.com/products/docker-desktop/){target=_blank}, or platforms like [Portainer](https://www.portainer.io/){target=_blank}. We suggest you read through their documentation on how to use Docker. + +
+
+ +## Fundamental Docker Commands :octicons-terminal-16: + +Docker commands in the terminal use the prefix `docker`. + +!!! Note "For every command listed, the correct execution of the commands through the command line is by using `docker` in front of the command: for example `docker help` or `docker search`. Thus, every :material-docker: = `docker`." + +### :material-docker: help + +Like many other command line applications the most helpful flag is the `help` command which can be used with the Management Commands: + +``` +$ docker +$ docker --help +``` + +### :material-docker: search + +We talk about the concept of [Docker Registries](https://cc.cyverse.org/docker/registry/){target=_blank} in the next section, but you can search the public list of registeries by using the `docker search` command to find public containers on the Official [Docker Hub Registry](https://hub.docker.com): + +``` +$ docker search +``` + +### :material-docker: pull + +Go to the [Docker Hub](https://hub.docker.com) and type `hello-world` in the search bar at the top of the page. + +Click on the 'tag' tab to see all the available 'hello-world' images. + +Click the 'copy' icon at the right to copy the `docker pull` command, or type it into your terminal: + +``` +$ docker pull hello-world +``` + +!!! Note + If you leave off the `:` and the tag name, it will by default pull the `latest` image + +``` +$ docker pull hello-world +Using default tag: latest +latest: Pulling from library/hello-world +2db29710123e: Pull complete +Digest: sha256:bfea6278a0a267fad2634554f4f0c6f31981eea41c553fdf5a83e95a41d40c38 +Status: Downloaded newer image for hello-world:latest +docker.io/library/hello-world:latest +``` + +Now try to list the files in your current working directory: + +``` +$ ls -l +``` + +??? Question "Where is the image you just pulled?" + + Docker saves container images to the Docker directory (where Docker is installed). + + You won't ever see them in your working directory. + + Use 'docker images' to see all the images on your computer: + + ``` + $ docker images + ``` + +### :material-docker: run + +The single most common command that you'll use with Docker is `docker run` ([see official help manual](https://docs.docker.com/engine/reference/commandline/run/) for more details). + + +``` +$ docker run hello-world:latest +``` + +In the demo above, you used the `docker pull` command to download the `hello-world:latest` image. + +What about if you run a container that you haven't downloaded? + + +``` +$ docker run alpine:latest +``` + +When you executed the command `docker run alpine:latest`, Docker first looked for the cached image locally, but did not find it, it then ran a `docker pull` behind the scenes to download the `alpine:latest` image and then execute your command. + + +### :material-docker: images + +You can now use the `docker images` command to see a list of all the cached images on your system: + +``` +$ docker images +REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE +alpine latest c51f86c28340 4 weeks ago 1.109 MB +hello-world latest 690ed74de00f 5 months ago 960 B +``` + +??? Info "Inspecting your containers" + + To find out more about a Docker images, run `docker inspect hello-world:latest` + +### :material-docker: ps + +Now it's time to see the `docker ps` command which shows you all containers that are currently running on your machine. + +``` +docker ps +``` + +Since no containers are running, you see a blank line. + +``` +$ docker ps +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +``` + +Let's try a more useful variant: `docker ps --all` + +``` +$ docker ps --all +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +a5eab9243a15 hello-world "/hello" 5 seconds ago Exited (0) 3 seconds ago loving_mcnulty +3bb4e26d2e0c alpine:latest "/bin/sh" 17 seconds ago Exited (0) 16 seconds ago objective_meninsky +192ffdf0cbae opensearchproject/opensearch-dashboards:latest "./opensearch-dashbo…" 3 days ago Exited (0) 3 days ago opensearch-dashboards +a10d47d3b6de opensearchproject/opensearch:latest "./opensearch-docker…" 3 days ago Exited (0) 3 days ago opensearch-node1 + +``` + +What you see above is a list of all containers that you have run. + +Notice that the `STATUS` column shows the current condition of the container: running, or as shown in the example, when the container was exited. + +### :material-docker: stop + +The `stop` command is used for containers that are actively running, either as a foreground process or as a detached background one. + +You can find a running container using the `docker ps` command. + +### :material-docker: rm + +You can remove individual stopped containers by using the `rm` command. Use the `ps` command to see all your stopped contiainers: + +``` +@user ➜ /workspaces $ docker ps -a +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +03542eaac9dc hello-world "/hello" About a minute ago Exited (0) About a minute ago unruffled_nobel +``` + +Use the first few unique alphanumerics in the CONTAINER ID to remove the stopped container: + +``` +@user ➜ /workspaces (mkdocs ✗) $ docker rm 0354 +0354 +``` + +Check to see that the container is gone using `ps -a` a second time (`-a` is shorthand for `--all`; the full command is `docker ps -a` or `docker ps --all`). + +### :material-docker: rmi + +The `rmi` command is similar to `rm` but it will remove the cached images. Used in combination with `docker images` or `docker system df` you can clean up a full cache + +``` +docker rmi +``` + +``` +@user ➜ /workspaces/ (mkdocs ✗) $ docker images +REPOSITORY TAG IMAGE ID CREATED SIZE +opendronemap/webodm_webapp latest e075d13aaf35 21 hours ago 1.62GB +redis latest a10f849e1540 5 days ago 117MB +opendronemap/nodeodm latest b4c50165f838 6 days ago 1.77GB +hello-world latest feb5d9fea6a5 7 months ago 13.3kB +opendronemap/webodm_db latest e40c0f274bba 8 months ago 695MB +@user ➜ /workspaces (mkdocs ✗) $ docker rmi hello-world +Untagged: hello-world:latest +Untagged: hello-world@sha256:10d7d58d5ebd2a652f4d93fdd86da8f265f5318c6a73cc5b6a9798ff6d2b2e67 +Deleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412 +Deleted: sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359 +@user ➜ /workspaces (mkdocs ✗) $ +``` + +### :material-docker: system + +The `system` command can be used to view information about containers on your cache, you can view your total disk usage, view events or info. + +You can also use it to `prune` unused data and image layers. + +To remove all cached layers, images, and data you can use the `-af` flag for `all` and `force` + +``` +docker system prune -af +``` + +### :material-docker: tag + +By default an image will recieve the tag `latest` when it is not specified during the `docker build` + +Image names and tags can be created or changed using the `docker tag` command. + +``` +docker tag imagename:oldtag imagename:newtag +``` + +You can also change the registry name used in the tag: + +``` +docker tag docker.io/username/imagename:oldtag harbor.cyverse.org/project/imagename:newtag +``` + +The cached image laters will not change their `sha256` and both image tags will still be present after the new tag name is generated. + +--- + +## Example of Running a Container + +This is a tutorial to demonstrate how to run PDAL using Docker. [PDAL](https://pdal.io/en/2.6.0/) is a stand-alone software package that can analyze and manipulate point cloud data files such as .las and .laz. In this tutorial, we will convert a LiDAR .laz file into a [Cloud-optimized Point Cloud format (.copc.laz)](https://www.gillanscience.com/cloud-native-geospatial/copc/). + +#### 1. Clone this repository to your local machine + +`git clone https://github.com/jeffgillan/pdal_copc.git` + +#### 2. Change directories into the newly cloned repository + +`cd pdal_copc` + +#### 3. Run the Container + +`docker run -v $(pwd):/data jeffgillan/pdal_copc:1.0` + +You are mounting a local volume (-v) directory to the container (`/data`). This local directory should have all of the point clouds files you want to convert. `$(pwd)` is telling it that the point clouds are in the current working directory. + +```jeffgillan/pdal_copc:1.0``` is the name of the container image you want to run. + +```jeffgillan``` = the Dockerhub account name + +```pdal_copc``` = the name of the image + +```1.0``` = the tag name + + +Your if everything worked correctly, you should have a new file `tree.copc.laz` in your present working directory. + +--- + +## Working with Interactive Containers + +Let's go ahead and run some Integrated Development Environment images from "trusted" organizations on the Docker Hub Registry. + +### :material-language-python: Jupyter Lab + +In this section, let's find a Docker image which can run a Jupyter Notebook + +Search for official images on Docker Hub which contain the string 'jupyter' + +``` +$ docker search jupyter +``` + +It should return something like: + +``` +NAME DESCRIPTION STARS OFFICIAL AUTOMATED +jupyter/datascience-notebook Jupyter Notebook Data Science Stack from htt… 912 +jupyter/all-spark-notebook Jupyter Notebook Python, Scala, R, Spark, Me… 374 +jupyter/scipy-notebook Jupyter Notebook Scientific Python Stack fro… 337 +jupyterhub/jupyterhub JupyterHub: multi-user Jupyter notebook serv… 307 [OK] +jupyter/tensorflow-notebook Jupyter Notebook Scientific Python Stack w/ … 298 +jupyter/pyspark-notebook Jupyter Notebook Python, Spark, Mesos Stack … 224 +jupyter/base-notebook Small base image for Jupyter Notebook stacks… 168 +jupyter/minimal-notebook Minimal Jupyter Notebook Stack from https://… 150 +jupyter/r-notebook Jupyter Notebook R Stack from https://github… 44 +jupyterhub/singleuser single-user docker images for use with Jupyt… 43 [OK] +jupyter/nbviewer Jupyter Notebook Viewer 27 [OK] +``` + +??? Warning "Untrusted community images" + + An important thing to note: None of these Jupyter or RStudio images are 'official' Docker images, meaning they could be trojans for spyware, malware, or other nasty warez. + +--- + +### Understanding PORTS + +When we want to run a container that runs on the open internet, we need to add a [TCP or UDP port number](https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers){target=_blank} from which we can access the application in a browser using the machine's IP (Internet Protocol) address or DNS (Domain Name Service) location. + +To do this, we need to access the container over a separate port address on the machine we're working on. + +Docker uses the flag `--port` or `-p` for short followed by two sets of port numbers. + +??? Note "Exposing Ports" + + Docker can in fact expose all ports to a container using the capital `-P` flag + + For security purposes, it is generally NEVER a good idea to expose all ports. + +Typically these numbers can be the same, but in some cases your machine may already be running another program (or container) on that open port. + +The port has two sides `left:right` separated by a colon. The left side port number is the INTERNAL port that the container software thinks its using. The right side number is the EXTERNAL port that you can access on your computer (or virtual machine). + +Here are some examples to run basic RStudio and Jupyter Lab: + +``` +$ docker run --rm -p 8787:8787 -e PASSWORD=cc2022 rocker/rstudio +``` + +**note: on CodeSpaces, the reverse proxy for the DNS requires you to turn off authentication** + +``` +$ docker run --rm -p 8787:8787 -e DISABLE_AUTH=true rocker/rstudio +``` + +``` +$ docker run --rm -p 8888:8888 jupyter/base-notebook +``` +``` +docker run --rm -p 8888:8888 jupyter/base-notebook start-notebook.sh --NotebookApp.token='' --NotebookApp.password='' +``` +??? Note "Preempting stale containers from your cache" + + We've added the `--rm` flag, which means the container will automatically removed from the cache when the container is exited. + + When you start an IDE in a terminal, the terminal connection must stay active to keep the container alive. + +### Detaching your container while it is running + +If we want to keep our window in the foreground we can use the `-d` - the *detached* flag will run the container as a background process, rather than in the foreground. + +When you run a container with this flag, it will start, run, telling you the container ID: + +``` +$ docker run --rm -d -p 8888:8888 jupyter/base-notebook +``` +Note, that your terminal is still active and you can use it to launch more containers. + +To view the running container, use the `docker ps` command. + +--- + +## Interactive Commands with Containers + +Lets try another command, this time to access the container as a shell: + +``` +$ docker run alpine:latest sh +``` + +Wait, nothing happened, right? + +Is that a bug? + +Well, no. + +The container will exit after running any scripted commands such as `sh`, unless they are run in an "interactive" terminal (TTY) - so for this example to not exit, you need to add the `-i` for interactive and `-t` for TTY. You can run them both in a single flag as `-it`, which is the more common way of adding the flag: + +``` +$ docker run -it alpine:latest sh +``` + +The prompt should change to something more like `/ # `. + +You are now running a shell inside the container! + +Try out a few commands like `ls -l`, `uname -a` and others. + +Exit out of the container by giving the `exit` command. + +``` +/ # exit +``` + +??? Warning "Making sure you've exited the container" + + If you type `exit` your **container** will exit and is no longer active. To check that, try the following: + + ``` + $ docker ps --latest + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + de4bbc3eeaec alpine "/bin/sh" 3 minutes ago Exited (0) About a minute ago pensive_leavitt + ``` + + If you want to keep the container active, then you can use keys `ctrl +p` `ctrl +q`. To make sure that it is not exited run the same `docker ps --latest` command again: + + ``` + $ docker ps --latest + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + 0db38ea51a48 alpine "sh" 3 minutes ago Up 3 minutes elastic_lewin + ``` + + Now if you want to get back into that container, then you can type `docker attach `. This way you can save your container: + + ``` + $ docker attach 0db38ea51a48 + ``` + +--- + +## :material-home: House Keeping and :material-broom: Cleaning Up Exited Containers + +### Managing Docker Images + +In the previous example, you pulled the `alpine` image from the registry and asked the Docker client to run a container based on that image. To see the list of images that are available locally on your system, run the `docker images` command. + +``` +$ docker images +REPOSITORY TAG IMAGE ID CREATED SIZE +ubuntu bionic 47b19964fb50 4 weeks ago 88.1MB +alpine latest caf27325b298 4 weeks ago 5.53MB +hello-world latest fce289e99eb9 2 months ago 1.84kB +``` + +Above is a list of images that I've pulled from the registry. You will have a different list of images on your machine. The **TAG** refers to a particular snapshot of the image and the **ID** is the corresponding unique identifier for that image. + +For simplicity, you can think of an image akin to a Git repository - images can be committed with changes and have multiple versions. When you do not provide a specific version number, the client defaults to latest. + +### Clutter and Cache + +Docker images are **cached** on your machine in the location where Docker was installed. These image files are not visible in the same directory where you might have used `docker pull `. + +Some Docker images can be large. Especially data science images with many scientific programming libraries and packages pre-installed. + +Pulling many images from the Docker Registries may fill up your hard disk! To inspect your system and disk use: + +``` +$ docker system info +$ docker system df +``` + +To find out how many images are on your machine, type: + +``` +$ docker images +``` + +To remove images that you no longer need, type: + +``` +$ docker system prune +``` + +This is where it becomes important to differentiate between *images*, *containers*, and *volumes* (which we'll get to more in a bit). You can take care of all of the dangling images and containers on your system. + +Note, that `prune` will not remove your cached *images* + +``` +$ docker system prune + WARNING! This will remove: + - all stopped containers + - all networks not used by at least one container + - all dangling images + - all dangling build cache + +Are you sure you want to continue? [y/N] +``` + +If you added the `-af` flag it will remove "all" `-a` dangling images, empty containers, AND ALL CACHED IMAGES with "force" `-f`. + +--- + +## Managing Data in Docker + +It is possible to store data within the writable layer of a container, but there are some limitations: + +!!! warning "" + - **The data doesn’t persist when that container is no longer running**, and it can be difficult to get the data out of the container if another process needs it. + - **A container’s writable layer is tightly coupled to the host machine where the container is running**. You can’t easily move the data somewhere else. + - **Its better to put your data into the container *AFTER* it is built** - this keeps the container size smaller and easier to move across networks. + +Docker offers three different ways to mount data into a container from the Docker host: **Volumes**, **tmpfs mounts** and **bind mounts**. Here, we will only be exploring *Volumes*. + +
+ ![vol](https://docs.docker.com/storage/images/types-of-mounts-volume.webp?w=450&h=300) +
The various methods for accessing data using containers. tmpfs mounts store data directly in memory, bind mounts and volumes use the host's file system. Volumes are flexible and only attach a specific directory to the container, whilst bind mounts require the user to share the full path to a file in order to allow the container to access it. Taken from the official Docker documentation on [data management with docker](https://docs.docker.com/storage/volumes/).
+
+ +!!! info "Why Volumes?" + + Volumes are often a better choice than persisting data in a container’s writable layer, because using a volume does not increase the size of containers using it, and the volume’s contents exist outside the lifecycle of a given container. Some of the advantages of volumes include: + + - Volumes are easier to back up or migrate. + - You can manage volumes using Docker CLI commands or the Docker API. + - Volumes work on both UNIX and Windows containers. + - Volumes can be more safely shared among multiple containers. + - A new volume’s contents can be pre-populated by a container. + +The `-v` flag is used for mounting volumes: + +`-v` or `--volume`: Consists of three fields, separated by colon characters (:). + +The fields must be in the correct order, and the meaning of each field is not immediately obvious. + +Required: + +- The **first** field is the path on your **local machine** that where the data are. +- The **second** field is the path where the file or directory are **mounted in the container**. + +Optional: + +- The third field is optional, and is a comma-separated list of options, such as `ro` (read only). + +The synthax looks like the following: +``` +-v /home/username/your_data_folder:/container_folder +``` + +This is what a full docker command with a mounted volume looks like: +``` +$ docker run -v /home/$USER/read_cleanup:/work alpine:latest ls -l /work +``` + +So what if we wanted to work interactively inside the container? + +``` +$ docker run -it -v /home/$USER/read_cleanup:/work alpine:latest sh +``` + +Once you're in the container, you will see that the `/work` directory is mounted in the working directory. + +Any data that you add to that folder outside the container will appear **INSIDE** the container. And any work you do inside the container saved in that folder will be saved OUTSIDE the container as well. + +--- + + + +--- + +## Docker Commands + +Here is a compiled list of fundamental Docker Commands: + +| Command | Usage | Example | +|---------|-------|---------| +| `pull` | Downloads an image from Docker Hub | `docker pull hello-world:latest` | +| `run` | runs a container with entrypoint | `docker run -it user/image:tag` | +| `build` | Builds a docker image from a Dockerfile in current working directory | `docker build -t user/image:tag .` | +| `images` | List all images on the local machine | `docker images list` | +| `tag` | Adds a different tag name to an image | `docker tag hello-world:latest hello-world:new-tag-name` | +| `login` | Authenticate to the Docker Hub (requires username and password) | `docker login` | +| `push` | Upload your new image to the Docker Hub | `docker push user/image:tag` | +| `inspect` | Provide detailed information on constructs controlled by Docker | `docker inspect containerID` | +| `ps` | List all containers on your system | `docker ps -a` | +| `rm` | Delete a stopped or running container |`docker rm -f ` | +| `rmi` | Delete an *image* from your cache | `docker rmi hello-world:latest` | +| `stop` | Stop a running container | `docker stop alpine:latest` | +| `system` | View system details, remove old images and containers with `prune` |`docker system prune` | +| `push` | Uploads an image to the Docker Hub (or other private registry) | `docker push username/image:tag` | + +--- + +## Self Assessment + +??? Question "A Docker container with the tagname `latest` ensures old code and data will work on a new computer setup?" + + !!! Success "Answer" + + Never use the `latest` tag for a publication or archival. + + The `latest` version is always being updated and should be considered "cutting edge". + + `latest` is the default tag name of all Docker images + + `latest` versions MAY have backward compatibility with older code and data, but this is not always a given + +??? Question "When are containers the right solution?" + + !!! Success + + Containers are valuable when you need to run an analyses on a remote platform, and you need it to work every time. + + !!! Failure + + You need to do some simple text file editing + + You need to run a web service + + + +??? Question "True or False: Docker containers allow for reproducibility across all computing platforms" + + !!! Success "False" + + While Docker allows you to quickly run software from other people, it may not work across every platform. + + There are different CPU architectures (`arm`, `amd64`, `x64, `x86`) deployed across cloud, computer workstations, laptops, and cellular phones. + + Docker containers and their software can be cross-compiled across architectures, but this must be done by the creators. + +??? Question "When is it advisable to not trust a Docker image?" + + !!! Success "When you cannot view its Dockerfile" + + Featured and Verified Docker images can be trusted, in general. + + User generated images should not be trusted unless you can view their Dockerfile, or build logs to determine what is actually in the container you are attempting to run. + +--- + +## Additional Resources + +Deeper Exploration of Containers and how to create them can be found [here](https://cc.cyverse.org/docker/intro/){target=_blank} + + +The Carpentries have an incubator workshop on [Docker Containers](https://carpentries-incubator.github.io/docker-introduction/){target=_blank} + diff --git a/07_reproducibility_II/index.html b/07_reproducibility_II/index.html new file mode 100644 index 000000000..51c669438 --- /dev/null +++ b/07_reproducibility_II/index.html @@ -0,0 +1,2454 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 7. Reproducibility II: Run Containers - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+ + + + + + + +

Reproducibility II: Run Containers

+
+

Learning Objectives

+

After this lesson, you should be able to:

+
    +
  • Explain what containers are used for in reproducible research contexts
  • +
  • Search for and run a Docker container locally or on a remote system
  • +
  • Understand how version control and data can be used inside a container
  • +
+
+

Reproducible Computer Code

+

Sharing your scientific analysis code with your colleagues is an essential pillar of Open Science that will help push your field forward.

+

There are, however, technical challenges that may prevent your colleagues from effectively running your code on their computers. In the previous lesson Reproducibility I we described computing environments and how they can make it difficult to run code from one computer to another.

+


+

Solutions for Sharing Computer Code

+
    +
  1. +

    Create a custom environment and share the recipe so your colleauges can replicate on their computer (as shown in Reproducibility I)

    +
  2. +
  3. +

    Package up the code and all of the software and send it to your colleague as a Container

    +
  4. +
+


+

What are containers?

+

A container is a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another

+
    +
  • +

    Container images are a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings

    +
  • +
  • +

    Each of these elements are specifically versioned and do not change

    +
  • +
  • +

    The recipient does not need to install the software in the traditional sense

    +
  • +
+


+

A useful analogy is to think of software containers as shipping containers. It allows us move cargo (software) around the world in standard way. The shipping container can be offloading and executed anywhere, as long the destination has a shipping port (i.e., Docker)

+
+

open science +

Software Shipping Containers

+
+


+

Containers are similar to virtual machines (VMs), but are smaller and easier to share. A big distinction between Containers and VMs is what is within each environment: VMs require the OS to be present within the image, whilst containers rely on the host OS and the container engine (e.g., Docker Engine).

+
+

containerexp +

Difference between Virtual Machines and Containers. Containers are a lot more portable as these do not require an OS to be bundled with the software. Figure source: Microsoft Cloudblogs.

+
+


+

Containers for Reproducible Science

+

Software containers, such as those managed by Docker or Singularity, are incredibly useful for reproducible science for several reasons:

+

Environment Consistency:

+

Containers encapsulate the software environment, ensuring that the same versions of software, libraries, and dependencies are used every time, reducing the "it works on my machine" problem.

+

Ease of Sharing:

+

Containers can be easily shared with other researchers, allowing them to replicate the exact software environment used in a study.

+

Platform Independence:

+

Containers can run on different operating systems and cloud platforms, allowing for consistency across different hardware and infrastructure.

+

Version Control:

+

Containers can be versioned, making it easy to keep track of changes in the software environment over time.

+

Scalability:

+

Containers can be easily scaled and deployed on cloud infrastructure, allowing for reproducible science at scale.

+

Isolation:

+

Containers isolate the software environment from the host system, reducing the risk of conflicts with other software and ensuring a clean and controlled environment.

+


+

+
    +
  • +

    The most common container software is Docker, which is a platform for developers and sysadmins to develop, deploy, and run applications with containers. Apptainer (formerly, Singularity), is another popular container engine, which allows you to deploy containers on HPC clusters.

    +
  • +
  • +

    DockerHub is the world's largest respository of container images. Think of it as the 'Github' of container images. It facilitates collaboration amongst developers and allows you to share your container images with the world. Dockerhub allows users to maintain different versions of container images.

    +
  • +
+
+

Warning

+

While Docker allows you to quickly run software from other people, it may not work across every platform. There are different CPU architectures (arm, amd64, x64,x86`) deployed across cloud, computer workstations, laptops, and cellular phones. Docker containers and their software can be cross-compiled across architectures, but this must be done by the creators.

+
+


+
+


+

Introduction to Docker

+
+

gitlfs

+
+

Prerequisites

+

In order to complete these exercises we STRONGLY recommend that you set up a personal GitHub and DockerHub account (account creation for both services is free).

+

There are no specific skills needed for this tutorial beyond elementary command line ability and using a text editor.

+

We are going to be using GitHub CodeSpaces for the hands on portion of the workshop, which features VS Code as a fully enabled development environment with Docker already installed.

+

Our instructions on starting a new CodeSpace are here.

+
+Installing Docker on your personal computer +

We are going to be using virtual machines on the cloud for this course, and we will explain why this is a good thing, but there may be a time when you want to run Docker on your own computer.

+

Installing Docker takes a little time but it is reasonably straight forward and it is a one-time setup.

+

Installation instructions from Docker Official Docs for common OS and chip architectures:

+ +
+
+Never used a terminal before? +

Before venturing much further, you should review the Software Carpentry lessons on "The Unix Shell" and "Version Control with Git" -- these are great introductory lessons related to the skills we're teaching here.

+

You've given up on ever using a terminal? No problem, Docker can be used from graphic interfaces, like Docker Desktop, or platforms like Portainer. We suggest you read through their documentation on how to use Docker.

+
+


+

+

Fundamental Docker Commands

+

Docker commands in the terminal use the prefix docker.

+
+

For every command listed, the correct execution of the commands through the command line is by using docker in front of the command: for example docker help or docker search. Thus, every = docker.

+
+

help

+

Like many other command line applications the most helpful flag is the help command which can be used with the Management Commands:

+
$ docker 
+$ docker --help
+
+ +

We talk about the concept of Docker Registries in the next section, but you can search the public list of registeries by using the docker search command to find public containers on the Official Docker Hub Registry:

+
$ docker search  
+
+

pull

+

Go to the Docker Hub and type hello-world in the search bar at the top of the page.

+

Click on the 'tag' tab to see all the available 'hello-world' images.

+

Click the 'copy' icon at the right to copy the docker pull command, or type it into your terminal:

+
$ docker pull hello-world
+
+
+

Note

+
If you leave off the `:` and the tag name, it will by default pull the `latest` image
+
+
+
$ docker pull hello-world
+Using default tag: latest
+latest: Pulling from library/hello-world
+2db29710123e: Pull complete 
+Digest: sha256:bfea6278a0a267fad2634554f4f0c6f31981eea41c553fdf5a83e95a41d40c38
+Status: Downloaded newer image for hello-world:latest
+docker.io/library/hello-world:latest
+
+

Now try to list the files in your current working directory:

+
$ ls -l
+
+
+Where is the image you just pulled? +

Docker saves container images to the Docker directory (where Docker is installed).

+

You won't ever see them in your working directory.

+

Use 'docker images' to see all the images on your computer:

+
$ docker images
+
+
+

run

+

The single most common command that you'll use with Docker is docker run (see official help manual for more details).

+
$ docker run hello-world:latest
+
+

In the demo above, you used the docker pull command to download the hello-world:latest image.

+

What about if you run a container that you haven't downloaded?

+
$ docker run alpine:latest
+
+

When you executed the command docker run alpine:latest, Docker first looked for the cached image locally, but did not find it, it then ran a docker pull behind the scenes to download the alpine:latest image and then execute your command.

+

images

+

You can now use the docker images command to see a list of all the cached images on your system:

+
$ docker images 
+REPOSITORY              TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
+alpine                  latest              c51f86c28340        4 weeks ago         1.109 MB
+hello-world             latest              690ed74de00f        5 months ago        960 B
+
+
+Inspecting your containers +

To find out more about a Docker images, run docker inspect hello-world:latest

+
+

ps

+

Now it's time to see the docker ps command which shows you all containers that are currently running on your machine.

+
docker ps
+
+

Since no containers are running, you see a blank line.

+
$ docker ps
+CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
+
+

Let's try a more useful variant: docker ps --all

+
$ docker ps --all
+CONTAINER ID   IMAGE                                            COMMAND                  CREATED          STATUS                      PORTS     NAMES
+a5eab9243a15   hello-world                                      "/hello"                 5 seconds ago    Exited (0) 3 seconds ago              loving_mcnulty
+3bb4e26d2e0c   alpine:latest                                    "/bin/sh"                17 seconds ago   Exited (0) 16 seconds ago             objective_meninsky
+192ffdf0cbae   opensearchproject/opensearch-dashboards:latest   "./opensearch-dashbo…"   3 days ago       Exited (0) 3 days ago                 opensearch-dashboards
+a10d47d3b6de   opensearchproject/opensearch:latest              "./opensearch-docker…"   3 days ago       Exited (0) 3 days ago                 opensearch-node1
+
+

What you see above is a list of all containers that you have run.

+

Notice that the STATUS column shows the current condition of the container: running, or as shown in the example, when the container was exited.

+

stop

+

The stop command is used for containers that are actively running, either as a foreground process or as a detached background one.

+

You can find a running container using the docker ps command.

+

rm

+

You can remove individual stopped containers by using the rm command. Use the ps command to see all your stopped contiainers:

+
@user ➜ /workspaces $ docker ps -a
+CONTAINER ID   IMAGE                        COMMAND                  CREATED              STATUS                          PORTS     NAMES
+03542eaac9dc   hello-world                  "/hello"                 About a minute ago   Exited (0) About a minute ago             unruffled_nobel
+
+

Use the first few unique alphanumerics in the CONTAINER ID to remove the stopped container:

+
@user ➜ /workspaces (mkdocs ✗) $ docker rm 0354
+0354
+
+

Check to see that the container is gone using ps -a a second time (-a is shorthand for --all; the full command is docker ps -a or docker ps --all).

+

rmi

+

The rmi command is similar to rm but it will remove the cached images. Used in combination with docker images or docker system df you can clean up a full cache

+
docker rmi
+
+
@user ➜ /workspaces/ (mkdocs ✗) $ docker images
+REPOSITORY                   TAG       IMAGE ID       CREATED        SIZE
+opendronemap/webodm_webapp   latest    e075d13aaf35   21 hours ago   1.62GB
+redis                        latest    a10f849e1540   5 days ago     117MB
+opendronemap/nodeodm         latest    b4c50165f838   6 days ago     1.77GB
+hello-world                  latest    feb5d9fea6a5   7 months ago   13.3kB
+opendronemap/webodm_db       latest    e40c0f274bba   8 months ago   695MB
+@user ➜ /workspaces (mkdocs ✗) $ docker rmi hello-world
+Untagged: hello-world:latest
+Untagged: hello-world@sha256:10d7d58d5ebd2a652f4d93fdd86da8f265f5318c6a73cc5b6a9798ff6d2b2e67
+Deleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412
+Deleted: sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359
+@user ➜ /workspaces (mkdocs ✗) $ 
+
+

system

+

The system command can be used to view information about containers on your cache, you can view your total disk usage, view events or info.

+

You can also use it to prune unused data and image layers.

+

To remove all cached layers, images, and data you can use the -af flag for all and force

+
docker system prune -af
+
+

tag

+

By default an image will recieve the tag latest when it is not specified during the docker build

+

Image names and tags can be created or changed using the docker tag command.

+
docker tag imagename:oldtag imagename:newtag
+
+

You can also change the registry name used in the tag:

+
docker tag docker.io/username/imagename:oldtag harbor.cyverse.org/project/imagename:newtag
+
+

The cached image laters will not change their sha256 and both image tags will still be present after the new tag name is generated.

+
+

Example of Running a Container

+

This is a tutorial to demonstrate how to run PDAL using Docker. PDAL is a stand-alone software package that can analyze and manipulate point cloud data files such as .las and .laz. In this tutorial, we will convert a LiDAR .laz file into a Cloud-optimized Point Cloud format (.copc.laz).

+

1. Clone this repository to your local machine

+

git clone https://github.com/jeffgillan/pdal_copc.git

+

2. Change directories into the newly cloned repository

+

cd pdal_copc

+

3. Run the Container

+

docker run -v $(pwd):/data jeffgillan/pdal_copc:1.0

+

You are mounting a local volume (-v) directory to the container (/data). This local directory should have all of the point clouds files you want to convert. $(pwd) is telling it that the point clouds are in the current working directory.

+

jeffgillan/pdal_copc:1.0 is the name of the container image you want to run.

+

jeffgillan = the Dockerhub account name

+

pdal_copc = the name of the image

+

1.0 = the tag name

+

Your if everything worked correctly, you should have a new file tree.copc.laz in your present working directory.

+
+

Working with Interactive Containers

+

Let's go ahead and run some Integrated Development Environment images from "trusted" organizations on the Docker Hub Registry.

+

Jupyter Lab

+

In this section, let's find a Docker image which can run a Jupyter Notebook

+

Search for official images on Docker Hub which contain the string 'jupyter'

+
$ docker search jupyter
+
+

It should return something like:

+
NAME                                   DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
+jupyter/datascience-notebook           Jupyter Notebook Data Science Stack from htt…   912                  
+jupyter/all-spark-notebook             Jupyter Notebook Python, Scala, R, Spark, Me…   374                  
+jupyter/scipy-notebook                 Jupyter Notebook Scientific Python Stack fro…   337                  
+jupyterhub/jupyterhub                  JupyterHub: multi-user Jupyter notebook serv…   307                  [OK]
+jupyter/tensorflow-notebook            Jupyter Notebook Scientific Python Stack w/ …   298                  
+jupyter/pyspark-notebook               Jupyter Notebook Python, Spark, Mesos Stack …   224                  
+jupyter/base-notebook                  Small base image for Jupyter Notebook stacks…   168                  
+jupyter/minimal-notebook               Minimal Jupyter Notebook Stack from https://…   150                  
+jupyter/r-notebook                     Jupyter Notebook R Stack from https://github…   44                   
+jupyterhub/singleuser                  single-user docker images for use with Jupyt…   43                   [OK]
+jupyter/nbviewer                       Jupyter Notebook Viewer                         27                   [OK]
+
+
+Untrusted community images +

An important thing to note: None of these Jupyter or RStudio images are 'official' Docker images, meaning they could be trojans for spyware, malware, or other nasty warez.

+
+
+

Understanding PORTS

+

When we want to run a container that runs on the open internet, we need to add a TCP or UDP port number from which we can access the application in a browser using the machine's IP (Internet Protocol) address or DNS (Domain Name Service) location.

+

To do this, we need to access the container over a separate port address on the machine we're working on.

+

Docker uses the flag --port or -p for short followed by two sets of port numbers.

+
+Exposing Ports +

Docker can in fact expose all ports to a container using the capital -P flag

+

For security purposes, it is generally NEVER a good idea to expose all ports.

+
+

Typically these numbers can be the same, but in some cases your machine may already be running another program (or container) on that open port.

+

The port has two sides left:right separated by a colon. The left side port number is the INTERNAL port that the container software thinks its using. The right side number is the EXTERNAL port that you can access on your computer (or virtual machine).

+

Here are some examples to run basic RStudio and Jupyter Lab:

+
$ docker run --rm -p 8787:8787 -e PASSWORD=cc2022 rocker/rstudio
+
+

note: on CodeSpaces, the reverse proxy for the DNS requires you to turn off authentication

+
$ docker run --rm -p 8787:8787 -e DISABLE_AUTH=true rocker/rstudio
+
+

$ docker run --rm -p 8888:8888 jupyter/base-notebook
+
+
docker run --rm -p 8888:8888 jupyter/base-notebook start-notebook.sh --NotebookApp.token='' --NotebookApp.password=''
+

+
+Preempting stale containers from your cache +

We've added the --rm flag, which means the container will automatically removed from the cache when the container is exited.

+

When you start an IDE in a terminal, the terminal connection must stay active to keep the container alive.

+
+

Detaching your container while it is running

+

If we want to keep our window in the foreground we can use the -d - the detached flag will run the container as a background process, rather than in the foreground.

+

When you run a container with this flag, it will start, run, telling you the container ID:

+

$ docker run --rm -d -p 8888:8888 jupyter/base-notebook
+
+Note, that your terminal is still active and you can use it to launch more containers.

+

To view the running container, use the docker ps command.

+
+

Interactive Commands with Containers

+

Lets try another command, this time to access the container as a shell:

+
$ docker run alpine:latest sh
+
+

Wait, nothing happened, right?

+

Is that a bug?

+

Well, no.

+

The container will exit after running any scripted commands such as sh, unless they are run in an "interactive" terminal (TTY) - so for this example to not exit, you need to add the -i for interactive and -t for TTY. You can run them both in a single flag as -it, which is the more common way of adding the flag:

+
$ docker run -it alpine:latest sh
+
+

The prompt should change to something more like / #.

+

You are now running a shell inside the container!

+

Try out a few commands like ls -l, uname -a and others.

+

Exit out of the container by giving the exit command.

+
/ # exit
+
+
+Making sure you've exited the container +

If you type exit your container will exit and is no longer active. To check that, try the following:

+
$ docker ps --latest
+CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                          PORTS                    NAMES
+de4bbc3eeaec        alpine                "/bin/sh"                3 minutes ago       Exited (0) About a minute ago                            pensive_leavitt
+
+

If you want to keep the container active, then you can use keys ctrl +p ctrl +q. To make sure that it is not exited run the same docker ps --latest command again:

+
$ docker ps --latest
+CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                         PORTS                    NAMES
+0db38ea51a48        alpine                "sh"                     3 minutes ago       Up 3 minutes                                            elastic_lewin
+
+

Now if you want to get back into that container, then you can type docker attach <container id>. This way you can save your container:

+
$ docker attach 0db38ea51a48
+
+
+
+

House Keeping and Cleaning Up Exited Containers

+

Managing Docker Images

+

In the previous example, you pulled the alpine image from the registry and asked the Docker client to run a container based on that image. To see the list of images that are available locally on your system, run the docker images command.

+
$ docker images
+REPOSITORY                 TAG                 IMAGE ID            CREATED             SIZE
+ubuntu                     bionic              47b19964fb50        4 weeks ago         88.1MB
+alpine                     latest              caf27325b298        4 weeks ago         5.53MB
+hello-world                latest              fce289e99eb9        2 months ago        1.84kB
+
+

Above is a list of images that I've pulled from the registry. You will have a different list of images on your machine. The TAG refers to a particular snapshot of the image and the ID is the corresponding unique identifier for that image.

+

For simplicity, you can think of an image akin to a Git repository - images can be committed with changes and have multiple versions. When you do not provide a specific version number, the client defaults to latest.

+

Clutter and Cache

+

Docker images are cached on your machine in the location where Docker was installed. These image files are not visible in the same directory where you might have used docker pull <imagename>.

+

Some Docker images can be large. Especially data science images with many scientific programming libraries and packages pre-installed.

+

Pulling many images from the Docker Registries may fill up your hard disk! To inspect your system and disk use:

+
$ docker system info
+$ docker system df
+
+

To find out how many images are on your machine, type:

+
$ docker images
+
+

To remove images that you no longer need, type:

+
$ docker system prune
+
+

This is where it becomes important to differentiate between images, containers, and volumes (which we'll get to more in a bit). You can take care of all of the dangling images and containers on your system.

+

Note, that prune will not remove your cached images

+
$ docker system prune
+    WARNING! This will remove:
+    - all stopped containers
+    - all networks not used by at least one container
+    - all dangling images
+    - all dangling build cache
+
+Are you sure you want to continue? [y/N]
+
+

If you added the -af flag it will remove "all" -a dangling images, empty containers, AND ALL CACHED IMAGES with "force" -f.

+
+

Managing Data in Docker

+

It is possible to store data within the writable layer of a container, but there are some limitations:

+
+
    +
  • The data doesn’t persist when that container is no longer running, and it can be difficult to get the data out of the container if another process needs it.
  • +
  • A container’s writable layer is tightly coupled to the host machine where the container is running. You can’t easily move the data somewhere else.
  • +
  • Its better to put your data into the container AFTER it is built - this keeps the container size smaller and easier to move across networks.
  • +
+
+

Docker offers three different ways to mount data into a container from the Docker host: Volumes, tmpfs mounts and bind mounts. Here, we will only be exploring Volumes.

+
+

vol +

The various methods for accessing data using containers. tmpfs mounts store data directly in memory, bind mounts and volumes use the host's file system. Volumes are flexible and only attach a specific directory to the container, whilst bind mounts require the user to share the full path to a file in order to allow the container to access it. Taken from the official Docker documentation on data management with docker.

+
+
+

Why Volumes?

+

Volumes are often a better choice than persisting data in a container’s writable layer, because using a volume does not increase the size of containers using it, and the volume’s contents exist outside the lifecycle of a given container. Some of the advantages of volumes include:

+
    +
  • Volumes are easier to back up or migrate.
  • +
  • You can manage volumes using Docker CLI commands or the Docker API.
  • +
  • Volumes work on both UNIX and Windows containers.
  • +
  • Volumes can be more safely shared among multiple containers.
  • +
  • A new volume’s contents can be pre-populated by a container.
  • +
+
+

The -v flag is used for mounting volumes:

+

-v or --volume: Consists of three fields, separated by colon characters (:).

+

The fields must be in the correct order, and the meaning of each field is not immediately obvious.

+

Required:

+
    +
  • The first field is the path on your local machine that where the data are.
  • +
  • The second field is the path where the file or directory are mounted in the container.
  • +
+

Optional:

+
    +
  • The third field is optional, and is a comma-separated list of options, such as ro (read only).
  • +
+

The synthax looks like the following: +

-v /home/username/your_data_folder:/container_folder
+

+

This is what a full docker command with a mounted volume looks like: +

$ docker run -v /home/$USER/read_cleanup:/work alpine:latest ls -l /work
+

+

So what if we wanted to work interactively inside the container?

+
$ docker run -it -v /home/$USER/read_cleanup:/work alpine:latest sh
+
+

Once you're in the container, you will see that the /work directory is mounted in the working directory.

+

Any data that you add to that folder outside the container will appear INSIDE the container. And any work you do inside the container saved in that folder will be saved OUTSIDE the container as well.

+
+
+

Docker Commands

+

Here is a compiled list of fundamental Docker Commands:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandUsageExample
pullDownloads an image from Docker Hubdocker pull hello-world:latest
runruns a container with entrypointdocker run -it user/image:tag
buildBuilds a docker image from a Dockerfile in current working directorydocker build -t user/image:tag .
imagesList all images on the local machinedocker images list
tagAdds a different tag name to an imagedocker tag hello-world:latest hello-world:new-tag-name
loginAuthenticate to the Docker Hub (requires username and password)docker login
pushUpload your new image to the Docker Hubdocker push user/image:tag
inspectProvide detailed information on constructs controlled by Dockerdocker inspect containerID
psList all containers on your systemdocker ps -a
rmDelete a stopped or running containerdocker rm -f <container ID>
rmiDelete an image from your cachedocker rmi hello-world:latest
stopStop a running containerdocker stop alpine:latest
systemView system details, remove old images and containers with prunedocker system prune
pushUploads an image to the Docker Hub (or other private registry)docker push username/image:tag
+
+

Self Assessment

+
+A Docker container with the tagname latest ensures old code and data will work on a new computer setup? +
+

Answer

+

Never use the latest tag for a publication or archival.

+

The latest version is always being updated and should be considered "cutting edge".

+

latest is the default tag name of all Docker images

+

latest versions MAY have backward compatibility with older code and data, but this is not always a given

+
+
+
+When are containers the right solution? +
+

Success

+

Containers are valuable when you need to run an analyses on a remote platform, and you need it to work every time.

+
+
+

Failure

+

You need to do some simple text file editing

+

You need to run a web service

+
+
+
+True or False: Docker containers allow for reproducibility across all computing platforms +
+

False

+

While Docker allows you to quickly run software from other people, it may not work across every platform.

+

There are different CPU architectures (arm, amd64, x64,x86`) deployed across cloud, computer workstations, laptops, and cellular phones.

+

Docker containers and their software can be cross-compiled across architectures, but this must be done by the creators.

+
+
+
+When is it advisable to not trust a Docker image? +
+

When you cannot view its Dockerfile

+

Featured and Verified Docker images can be trusted, in general.

+

User generated images should not be trusted unless you can view their Dockerfile, or build logs to determine what is actually in the container you are attempting to run.

+
+
+
+

Additional Resources

+

Deeper Exploration of Containers and how to create them can be found here

+

The Carpentries have an incubator workshop on Docker Containers

+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/08_reproducibility_III/08_reproducibility_III.md b/08_reproducibility_III/08_reproducibility_III.md new file mode 100644 index 000000000..444775599 --- /dev/null +++ b/08_reproducibility_III/08_reproducibility_III.md @@ -0,0 +1,402 @@ +# Reproducibility III: Building Docker Containers + +!!! Success "Learning Objectives" + + After this lesson, you should be able to: + + * Understand the Dockerfile structure and fields + * Build, execute and push your own Docker image + +In [Reproducibility II](./07_reproducibility_ii.md) we saw how we can access and execute Docker containers. In this lesson, we continue to explore containerization, covering how we can create our own container and the various commands necessary in order to create them. + +## Images vs Containers + +As mentioned before, there are differences between what an Image is and what Containers are. Here's a table that addresses some of these differences: + +|| Image | Container | +|:---:|---|---| +| **What** | A snapshot of an application
containing code, libraries, dependencides
and files needed for the application to run | A runtime instance of the Docker image | +| **When** | Created through a Dockerfile | Runtime executed after Image is created | +| **Why** | Reproducibility and consistency! | Reproducibility and consistency! | +| **Where** | Stored in an online registry (e.g., [Docker Hub](https://hub.docker.com/)) | Executed on your machine | + +Once an Image is created, one can run as many Containers of it as required. Here's a Dockerfile-to-container diagram: + +``` mermaid +graph LR + A[Dockerfile] --> |Local build| B{Image}:::colorclass; + B --> |storage| C[Online Registry]; + C ---> |Image pull to local
and Execution| D[Container]; + B ----> |Local Execution| E[Container]; + B ----> |Local Execution| F[Container]; + B ----> |Local Execution| G[Container] + classDef colorclass fill:#f96 +``` + +## Dockerfiles and Instructions + +`docker run` starts a container and executes the default "entrypoint", or any other "command" that follows `run` and any optional flags. These commands are specified within a Dockerfile. + +!!! Tip "What is a Dockerfile?" + + + A Dockerfile is a text file that contains a list of commands, known as **instructions** used by Docker to build an image. + + These commands can include specifying the base image to use, copying files into the image, setting environment variables, running commands, and defining entry points and default commands to run when a container is started from the image. The Dockerfile is processed by the docker build command, which creates a Docker image that can be used to run containers. + + ``` + FROM pdal/pdal:latest # Tells the Dockerfile which image to pull from + # + WORKDIR /app # Sets the initial working directory + # + COPY pdal_copc.sh /app/pdal_copc.sh # Copies a certain file from your directory to the container + # + COPY copc.json /app/copc.json # Copies a certain file from your directory to the container + # + RUN chmod +x pdal_copc.sh # Runs a specific command (in this case, adds specific permissions) + # + ENTRYPOINT ["/app/pdal_copc.sh"] # Sets the first command activating the container + ``` + +??? Tip "What is an *entrypoint*?" + + An entrypoint is the initial command(s) executed upon starting the Docker container. It is listed in the `Dockerfile` as `ENTRYPOINT` and can take 2 forms: as commands followed by parameters (`ENTRYPOINT command param1 param2`) or as an executable (`ENTRYPOINT [“executable”, “param1”, “param2”]`) + +## Building Docker Images + +Now that we are relatively comfortable with running Docker, we can look at some advanced Docker topics, such as: + +- Building our own Docker images from the `Dockerfile` +- Modify an existing Dockerfile and create a new image +- Push an image to a Registry + +### Requirements + +Clone our example repository with pre-written Dockerfiles From your CodeSpace, we are going to copy a second GitHub repository onto our VM. If you are working locally, make sure that you change directories away from any other Git repository that you may have been working in. + +``` +$ cd /workspaces + +$ git clone https://github.com/cyverse-education/intro2docker + +$ cd intro2docker/ + +``` + +--- + +## Writing a Dockerfile + +!!! Note "Important" + + `Dockerfile` must be capitalized. It does not have a file extension. + +Create a file called `Dockerfile`, and add content to it as described below, e.g. + +``` +$ touch Dockerfile +``` + +!!! Note "Formatting in the `Dockerfile`" + + We use a code line escape character `\` to allow single line scripts to be written on multiple lines in the Dockerfile. + + We also use the double characters `&&` which essentially mean “if true, then do this” while executing the code. The `&&` can come at the beginning of a line or the end when used with `\`. + +The `Dockerfile` contains **Instructions**: a series of commands that Docker executes during the creation and execution of a container. + +---- + +### ARG + +The only command that can come before a `FROM` statement is `ARG` + +`ARG` can be used to set arguments for later in the build, e.g., + +``` +ARG VERSION=latest + +FROM ubuntu:$VERSION +``` + +### FROM + +A valid `Dockerfile` must start with a `FROM` statement which initializes a new build stage and sets the **base image** for subsequent layers. + +We’ll start by specifying our base image, using the FROM statement + +``` +FROM ubuntu:latest +``` + +If you are building on an `arm64` or Windows system you can also give the optional `--platform` flag, e.g., + +``` +FROM --platform=linux/amd64 ubuntu:latest +``` + +??? question "When to use a multi-stage build pattern?" + + Docker has the ability to build container images from one image, and run that "builder" image from a second "base" image, in what is called a "builder pattern". + + Build patterns are useful if you're compiling code from (proprietary) source code and only want to feature the binary code as an executed function in the container at run time. + + Build patterns can greatly reduce the size of your container. + + You can use multiple `FROM` commands as build stages. The `AS` statement follows the `image:tag` as a psuedo argument. + + ``` + # build stage + FROM golang:latest AS build-env + WORKDIR /go/src/app + ADD . /go/src/app + RUN go mod init + RUN cd /go/src/app && go build -o hello + + # final stage + FROM alpine:latest + WORKDIR /app + COPY --from=build-env /go/src/app /app/ + ENTRYPOINT ./hello + ``` + +### LABEL + +You can create labels which are then tagged as JSON metadata to the image + +``` +LABEL author="your-name" +LABEL email="your@email-address" +LABEL version="v1.0" +LABEL description="This is your first Dockerfile" +LABEL date_created="2022-05-13" +``` + +You can also add labels to a container when it is run: + +``` +$ docker run --label description="this label came later" ubuntu:latest + +$ docker ps -a + +$ docker inspect ### +``` + +### RUN + +Different than the `docker run` command is the `RUN` build function. `RUN` is used to create new layers atop the "base image" + +Here, we are going to install some games and programs into our base image: + +``` +RUN apt-get update && apt-get install -y fortune cowsay lolcat +``` + +Here we've installed `fortune` `cowsay` and `lolcat` as new programs into our base image. + +!!! Warning "Best practices for building new layers" + + Ever time you use the `RUN` command it is a good idea to use the `apt-get update` or `apt update` command to make sure your layer is up-to-date. This can become a problem though if you have a very large container with a large number of `RUN` layers. + +### ENV + +In our new container, we need to change and update some of the environment flags. We can do this using the `ENV` command + +``` +ENV PATH=/usr/games:${PATH} + +ENV LC_ALL=C +``` + +Here we are adding the `/usr/games` directory to the `PATH` so that when we run the new container it will find our newly installed game commands + +We are also updating the "[locales](https://www.tecmint.com/set-system-locales-in-linux/)" to set the language of the container. + +### COPY + +The `COPY` command will copy files from the directory where `Dockerfile` is kept into the new image. You must specify where to copy the files or directories + +``` +COPY . /app +``` + +??? question "When to use `COPY` vs `ADD`" + + `COPY` is more basic and is good for files + + `ADD` has some extra features like `.tar` extraction and URL support + +### CMD + +The `CMD` command is used to run software in your image. In general use the ["command"] syntax: + +``` +CMD ["executable", "parameter1", "parameter2"] +``` + +### ENTRYPOINT + +ENTRYPOINT works similarly to `CMD` but is designed to allow you to run your container as an executable. + +``` +ENTRYPOINT fortune | cowsay | lolcat +``` + +The default `ENTRYPOINT` of most images is `/bin/sh -c` which executes a `shell` command. + + +`ENTRYPOINT` supports both the `ENTRYPOINT ["command"]` syntax and the `ENTRYPOINT command` syntax + +??? question "What is the difference in the `ENTRYPOINT` and `CMD`" + + The CMD instruction is used to define what is execute when the container is run. + + The ENTRYPOINT instruction cannot be overridden, instead it is appended to when a new command is given to the `docker run container:tag new-cmd` statement + + the executable is defined with ENTRYPOINT, while CMD specifies the default parameter + +### USER + +Most containers are run as `root` meaning that they have super-user privileges within themselves + +Typically, a new user is necessary in a container that is used interactively or may be run on a remote system. + +During the build of the container, you can create a new user with the `adduser` command and set up a `/home/` directory for them. This new user would have something like 1000:1000 `uid:gid` permissions without `sudo` privileges. + +As a last step, the container is run as the new `USER`, e.g., + +``` +ARG VERSION=18.04 + +FROM ubuntu:$VERSION + +RUN useradd ubuntu && \ + chown -R ubuntu:ubuntu /home/ubuntu + +USER ubuntu +``` + +### EXPOSE + +You can open [ports](intro.md#understanding-ports) using the `EXPOSE` command. + +``` +EXPOSE 8888 +``` + +The above command will expose port 8888. + +!!! Note + Running multiple containers using the same port is not trivial and would require the usage of a web server such as [NGINX](https://www.nginx.com/). However, you can have multiple containers interact with each other using [Docker Compose](compose.md). + +--- + +### Pushing to a Registry with :material-docker: docker push + +By default `docker push` will upload your local container image to the [Docker Hub](https://hub.docker.com/){target=_blank}. + +Also, make sure that your container has the appropriate [tag](07_reproducibility_ii.md#tag). + +First, make sure to log into the Docker Hub, this will allow you to download private limages, to upload private/public images: + +``` +docker login +``` + +Alternately, you can [link GitHub / GitLab accounts](https://hub.docker.com/settings/linked-accounts){target=_blank} to the Docker Hub. + +To push the image to the Docker Hub: + +``` +docker push username/imagename:tag +``` + +or, to a private registry, here we push to CyVerse private `harbor.cyverse.org` registry which uses "project" sub folders: + +``` +docker push harbor.cyverse.org/project/imagename:newtag +``` + +## Summary of Instructions + +| Instruction Command | Description | +| --- | --- | +| `ARG` | Sets environmental variables during image building | +| `FROM` | Instructs to use a specific Docker image | +| `LABEL` | Adds metadata to the image | +| `RUN` | Executes a specific command | +| `ENV` | Sets environmental variables | +| `COPY` | Copies a file from a specified location to the image | +| `CMD` | Sets a command to be executed when running a container | +| `ENTRYPOINT` | Configures and run a container as an executable | +| `USER` | Used to set User specific information | +| `EXPOSE` | exposes a specific port | + +--- + +## Managing Data in Docker + +It is possible to store data within the writable layer of a container, but there are some limitations: + +- The data doesn’t persist when that container is no longer running, and it can be difficult to get the data out of the container if another process needs it. +- A container’s writable layer is tightly coupled to the host machine where the container is running. You can’t easily move the data somewhere else. +- Its better to put your data into the container **AFTER** it is built - this keeps the container size smaller and easier to move across networks. + +Docker offers three different ways to mount data into a container from the Docker host: + +- **Volumes** +- **tmpfs mounts** +- **Bind mounts** + +![vol_mount](assets/volume_mount.png/) + +When in doubt, volumes are almost always the right choice. + +### Volumes + +Volumes are often a better choice than persisting data in a container’s writable layer, because using a volume does not increase the size of containers using it, and the volume’s contents exist outside the lifecycle of a given container. While bind mounts (which we will see in the Advanced portion of the Camp) are dependent on the directory structure of the host machine, volumes are completely managed by Docker. Volumes have several advantages over bind mounts: + +- Volumes are easier to back up or migrate than bind mounts. +- You can manage volumes using Docker CLI commands or the Docker API. +- Volumes work on both UNIX and Windows containers. +- Volumes can be more safely shared among multiple containers. +- A new volume’s contents can be pre-populated by a container. + +??? Tip "When Should I Use the Temporary File System mount?" + + If your container generates non-persistent state data, consider using a `tmpfs` mount to avoid storing the data anywhere permanently, and to increase the container’s performance by avoiding writing into the container’s writable layer. The data is written to the host's memory instead of a volume; When the container stops, the `tmpfs` mount is removed, and files written there will not be kept. + +Choose the `-v` flag for mounting volumes + +`-v` or `--volume`: Consists of three fields, separated by colon characters (:). + +The fields must be in the correct order, and the meaning of each field is not immediately obvious. + +- The **first** field is the path on your **local machine** that where the data are. +- The **second** field is the path where the file or directory are **mounted in the container**. +- The third field is optional, and is a comma-separated list of options, such as `ro` (read only). + +``` +-v /home/username/your_data_folder:/container_folder +``` + +``` +$ docker run -v /home/$USER/read_cleanup:/work alpine:latest ls -l /work +``` + +So what if we wanted to work interactively inside the container? + +``` +$ docker run -it -v /home/$USER/read_cleanup:/work alpine:latest sh +``` + +``` +$ ls -l +$ ls -l work +``` + +Once you're in the container, you will see that the `/work` directory is mounted in the working directory. + +Any data that you add to that folder outside the container will appear INSIDE the container. And any work you do inside the container saved in that folder will be saved OUTSIDE the container as well. + +--- \ No newline at end of file diff --git a/08_reproducibility_III/index.html b/08_reproducibility_III/index.html new file mode 100644 index 000000000..3dc73aa32 --- /dev/null +++ b/08_reproducibility_III/index.html @@ -0,0 +1,1804 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 8. Reproducibility III: Build Containers - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Reproducibility III: Building Docker Containers

+
+

Learning Objectives

+

After this lesson, you should be able to:

+
    +
  • Understand the Dockerfile structure and fields
  • +
  • Build, execute and push your own Docker image
  • +
+
+

In Reproducibility II we saw how we can access and execute Docker containers. In this lesson, we continue to explore containerization, covering how we can create our own container and the various commands necessary in order to create them.

+

Images vs Containers

+

As mentioned before, there are differences between what an Image is and what Containers are. Here's a table that addresses some of these differences:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ImageContainer
WhatA snapshot of an application
containing code, libraries, dependencides
and files needed for the application to run
A runtime instance of the Docker image
WhenCreated through a DockerfileRuntime executed after Image is created
WhyReproducibility and consistency!Reproducibility and consistency!
WhereStored in an online registry (e.g., Docker Hub)Executed on your machine
+

Once an Image is created, one can run as many Containers of it as required. Here's a Dockerfile-to-container diagram:

+
graph LR
+    A[Dockerfile] --> |Local build| B{Image}:::colorclass;
+    B --> |storage| C[Online Registry];
+    C ---> |Image pull to local <br> and Execution| D[Container];
+    B ----> |Local Execution| E[Container];
+    B ----> |Local Execution| F[Container];
+    B ----> |Local Execution| G[Container]
+    classDef colorclass fill:#f96
+

Dockerfiles and Instructions

+

docker run starts a container and executes the default "entrypoint", or any other "command" that follows run and any optional flags. These commands are specified within a Dockerfile.

+
+

What is a Dockerfile?

+

A Dockerfile is a text file that contains a list of commands, known as instructions used by Docker to build an image.

+

These commands can include specifying the base image to use, copying files into the image, setting environment variables, running commands, and defining entry points and default commands to run when a container is started from the image. The Dockerfile is processed by the docker build command, which creates a Docker image that can be used to run containers.

+
FROM pdal/pdal:latest                   # Tells the Dockerfile which image to pull from
+                                        # 
+WORKDIR /app                            # Sets the initial working directory
+                                        #
+COPY pdal_copc.sh /app/pdal_copc.sh     # Copies a certain file from your directory to the container
+                                        #
+COPY copc.json /app/copc.json           # Copies a certain file from your directory to the container
+                                        #
+RUN chmod +x pdal_copc.sh               # Runs a specific command (in this case, adds specific permissions)
+                                        #
+ENTRYPOINT ["/app/pdal_copc.sh"]        # Sets the first command activating the container
+
+
+
+What is an entrypoint? +

An entrypoint is the initial command(s) executed upon starting the Docker container. It is listed in the Dockerfile as ENTRYPOINT and can take 2 forms: as commands followed by parameters (ENTRYPOINT command param1 param2) or as an executable (ENTRYPOINT [“executable”, “param1”, “param2”])

+
+

Building Docker Images

+

Now that we are relatively comfortable with running Docker, we can look at some advanced Docker topics, such as:

+
    +
  • Building our own Docker images from the Dockerfile
  • +
  • Modify an existing Dockerfile and create a new image
  • +
  • Push an image to a Registry
  • +
+

Requirements

+

Clone our example repository with pre-written Dockerfiles From your CodeSpace, we are going to copy a second GitHub repository onto our VM. If you are working locally, make sure that you change directories away from any other Git repository that you may have been working in.

+
$ cd /workspaces
+
+$ git clone https://github.com/cyverse-education/intro2docker
+
+$ cd intro2docker/
+
+
+

Writing a Dockerfile

+
+

Important

+

Dockerfile must be capitalized. It does not have a file extension.

+
+

Create a file called Dockerfile, and add content to it as described below, e.g.

+
$ touch Dockerfile
+
+
+

Formatting in the Dockerfile

+

We use a code line escape character \ to allow single line scripts to be written on multiple lines in the Dockerfile.

+

We also use the double characters && which essentially mean “if true, then do this” while executing the code. The && can come at the beginning of a line or the end when used with \.

+
+

The Dockerfile contains Instructions: a series of commands that Docker executes during the creation and execution of a container.

+
+

ARG

+

The only command that can come before a FROM statement is ARG

+

ARG can be used to set arguments for later in the build, e.g.,

+
ARG VERSION=latest
+
+FROM ubuntu:$VERSION
+
+

FROM

+

A valid Dockerfile must start with a FROM statement which initializes a new build stage and sets the base image for subsequent layers.

+

We’ll start by specifying our base image, using the FROM statement

+
FROM ubuntu:latest
+
+

If you are building on an arm64 or Windows system you can also give the optional --platform flag, e.g.,

+
FROM --platform=linux/amd64 ubuntu:latest
+
+
+When to use a multi-stage build pattern? +

Docker has the ability to build container images from one image, and run that "builder" image from a second "base" image, in what is called a "builder pattern".

+

Build patterns are useful if you're compiling code from (proprietary) source code and only want to feature the binary code as an executed function in the container at run time.

+

Build patterns can greatly reduce the size of your container.

+

You can use multiple FROM commands as build stages. The AS statement follows the image:tag as a psuedo argument.

+
# build stage
+FROM golang:latest AS build-env
+WORKDIR /go/src/app
+ADD . /go/src/app
+RUN go mod init
+RUN cd /go/src/app && go build -o hello
+
+# final stage
+FROM alpine:latest
+WORKDIR /app
+COPY --from=build-env /go/src/app /app/
+ENTRYPOINT ./hello
+
+
+

LABEL

+

You can create labels which are then tagged as JSON metadata to the image

+
LABEL author="your-name" 
+LABEL email="your@email-address"
+LABEL version="v1.0"
+LABEL description="This is your first Dockerfile"
+LABEL date_created="2022-05-13"
+
+

You can also add labels to a container when it is run:

+
$ docker run --label description="this label came later" ubuntu:latest
+
+$ docker ps -a
+
+$ docker inspect ###
+
+

RUN

+

Different than the docker run command is the RUN build function. RUN is used to create new layers atop the "base image"

+

Here, we are going to install some games and programs into our base image:

+
RUN apt-get update && apt-get install -y fortune cowsay lolcat
+
+

Here we've installed fortune cowsay and lolcat as new programs into our base image.

+
+

Best practices for building new layers

+

Ever time you use the RUN command it is a good idea to use the apt-get update or apt update command to make sure your layer is up-to-date. This can become a problem though if you have a very large container with a large number of RUN layers.

+
+

ENV

+

In our new container, we need to change and update some of the environment flags. We can do this using the ENV command

+
ENV PATH=/usr/games:${PATH}
+
+ENV LC_ALL=C
+
+

Here we are adding the /usr/games directory to the PATH so that when we run the new container it will find our newly installed game commands

+

We are also updating the "locales" to set the language of the container.

+

COPY

+

The COPY command will copy files from the directory where Dockerfile is kept into the new image. You must specify where to copy the files or directories

+
COPY . /app
+
+
+When to use COPY vs ADD +

COPY is more basic and is good for files

+

ADD has some extra features like .tar extraction and URL support

+
+

CMD

+

The CMD command is used to run software in your image. In general use the ["command"] syntax:

+
CMD ["executable", "parameter1", "parameter2"]
+
+

ENTRYPOINT

+

ENTRYPOINT works similarly to CMD but is designed to allow you to run your container as an executable.

+
ENTRYPOINT fortune | cowsay | lolcat
+
+

The default ENTRYPOINT of most images is /bin/sh -c which executes a shell command.

+

ENTRYPOINT supports both the ENTRYPOINT ["command"] syntax and the ENTRYPOINT command syntax

+
+What is the difference in the ENTRYPOINT and CMD +

The CMD instruction is used to define what is execute when the container is run.

+

The ENTRYPOINT instruction cannot be overridden, instead it is appended to when a new command is given to the docker run container:tag new-cmd statement

+

the executable is defined with ENTRYPOINT, while CMD specifies the default parameter

+
+

USER

+

Most containers are run as root meaning that they have super-user privileges within themselves

+

Typically, a new user is necessary in a container that is used interactively or may be run on a remote system.

+

During the build of the container, you can create a new user with the adduser command and set up a /home/ directory for them. This new user would have something like 1000:1000 uid:gid permissions without sudo privileges.

+

As a last step, the container is run as the new USER, e.g.,

+
ARG VERSION=18.04
+
+FROM ubuntu:$VERSION
+
+RUN useradd ubuntu && \
+    chown -R ubuntu:ubuntu /home/ubuntu
+
+USER ubuntu
+
+

EXPOSE

+

You can open ports using the EXPOSE command.

+
EXPOSE 8888
+
+

The above command will expose port 8888.

+
+

Note

+

Running multiple containers using the same port is not trivial and would require the usage of a web server such as NGINX. However, you can have multiple containers interact with each other using Docker Compose.

+
+
+

Pushing to a Registry with docker push

+

By default docker push will upload your local container image to the Docker Hub.

+

Also, make sure that your container has the appropriate tag.

+

First, make sure to log into the Docker Hub, this will allow you to download private limages, to upload private/public images:

+
docker login
+
+

Alternately, you can link GitHub / GitLab accounts to the Docker Hub.

+

To push the image to the Docker Hub:

+
docker push username/imagename:tag 
+
+

or, to a private registry, here we push to CyVerse private harbor.cyverse.org registry which uses "project" sub folders:

+
docker push harbor.cyverse.org/project/imagename:newtag 
+
+

Summary of Instructions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Instruction CommandDescription
ARGSets environmental variables during image building
FROMInstructs to use a specific Docker image
LABELAdds metadata to the image
RUNExecutes a specific command
ENVSets environmental variables
COPYCopies a file from a specified location to the image
CMDSets a command to be executed when running a container
ENTRYPOINTConfigures and run a container as an executable
USERUsed to set User specific information
EXPOSEexposes a specific port
+
+

Managing Data in Docker

+

It is possible to store data within the writable layer of a container, but there are some limitations:

+
    +
  • The data doesn’t persist when that container is no longer running, and it can be difficult to get the data out of the container if another process needs it.
  • +
  • A container’s writable layer is tightly coupled to the host machine where the container is running. You can’t easily move the data somewhere else.
  • +
  • Its better to put your data into the container AFTER it is built - this keeps the container size smaller and easier to move across networks.
  • +
+

Docker offers three different ways to mount data into a container from the Docker host:

+
    +
  • Volumes
  • +
  • tmpfs mounts
  • +
  • Bind mounts
  • +
+

vol_mount

+

When in doubt, volumes are almost always the right choice.

+

Volumes

+

Volumes are often a better choice than persisting data in a container’s writable layer, because using a volume does not increase the size of containers using it, and the volume’s contents exist outside the lifecycle of a given container. While bind mounts (which we will see in the Advanced portion of the Camp) are dependent on the directory structure of the host machine, volumes are completely managed by Docker. Volumes have several advantages over bind mounts:

+
    +
  • Volumes are easier to back up or migrate than bind mounts.
  • +
  • You can manage volumes using Docker CLI commands or the Docker API.
  • +
  • Volumes work on both UNIX and Windows containers.
  • +
  • Volumes can be more safely shared among multiple containers.
  • +
  • A new volume’s contents can be pre-populated by a container.
  • +
+
+When Should I Use the Temporary File System mount? +

If your container generates non-persistent state data, consider using a tmpfs mount to avoid storing the data anywhere permanently, and to increase the container’s performance by avoiding writing into the container’s writable layer. The data is written to the host's memory instead of a volume; When the container stops, the tmpfs mount is removed, and files written there will not be kept.

+
+

Choose the -v flag for mounting volumes

+

-v or --volume: Consists of three fields, separated by colon characters (:).

+

The fields must be in the correct order, and the meaning of each field is not immediately obvious.

+
    +
  • The first field is the path on your local machine that where the data are.
  • +
  • The second field is the path where the file or directory are mounted in the container.
  • +
  • The third field is optional, and is a comma-separated list of options, such as ro (read only).
  • +
+
-v /home/username/your_data_folder:/container_folder
+
+
$ docker run -v /home/$USER/read_cleanup:/work alpine:latest ls -l /work
+
+

So what if we wanted to work interactively inside the container?

+
$ docker run -it -v /home/$USER/read_cleanup:/work alpine:latest sh
+
+
$ ls -l 
+$ ls -l work
+
+

Once you're in the container, you will see that the /work directory is mounted in the working directory.

+

Any data that you add to that folder outside the container will appear INSIDE the container. And any work you do inside the container saved in that folder will be saved OUTSIDE the container as well.

+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/09_remote_computing_cyverse/09_remote_computing_cyverse.md b/09_remote_computing_cyverse/09_remote_computing_cyverse.md new file mode 100644 index 000000000..563c33188 --- /dev/null +++ b/09_remote_computing_cyverse/09_remote_computing_cyverse.md @@ -0,0 +1,16 @@ +# Remote Computing with Cyverse + + + +
+ ![open science](../assets/cyverse_float.gif){ width="500" } +
+
+ + + +### Get off your own machine + +More and more work is being done somewhere other than a personal computer. This could be an HPC cluster at a university or a cloud computing provider. "Cloud" just means somebody else is handling the computers, and you get to use them when you need to, typically for a price. Some 'cloud' options include: Binder, Colab, and Cyverse VICE and Github Codespace. + +The take home message on Cloud is that it is a great way to make your work more reproducible, as you can share a link to your work, and anyone can run it without having to install anything. \ No newline at end of file diff --git a/09_remote_computing_cyverse/index.html b/09_remote_computing_cyverse/index.html new file mode 100644 index 000000000..93ce248e3 --- /dev/null +++ b/09_remote_computing_cyverse/index.html @@ -0,0 +1,1115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 9. Remote Computing: Cyverse - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+ + + + + + + +

Remote Computing with Cyverse

+
+

open science +

+
+

Get off your own machine

+

More and more work is being done somewhere other than a personal computer. This could be an HPC cluster at a university or a cloud computing provider. "Cloud" just means somebody else is handling the computers, and you get to use them when you need to, typically for a price. Some 'cloud' options include: Binder, Colab, and Cyverse VICE and Github Codespace.

+

The take home message on Cloud is that it is a great way to make your work more reproducible, as you can share a link to your work, and anyone can run it without having to install anything.

+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/10_reproducibility_IV/10_reproducibility_IV.md b/10_reproducibility_IV/10_reproducibility_IV.md new file mode 100644 index 000000000..df2801e7e --- /dev/null +++ b/10_reproducibility_IV/10_reproducibility_IV.md @@ -0,0 +1,612 @@ +# Remote Computing with High Performance Computer (HPC) + +
+ ![sin-2-app](assets/logos/sin-app-slurm.png) +
Singularity > Apptainer and working with the [SLURM workload manager](https://slurm.schedmd.com/documentation.html) (HPC scheduler).
+
+ +!!! Success "Expected Outcomes" + - Being able to log onto the HPC and start an interactive node + - Know basic HPC related commands + - Execute containerized code through Singularity/Apptainer + - Being able to search and load specific software + - Execute NextFlow scripts on the HPC + +Through FOSS and FOSS+ we have learned about container technology and how it can affect reproducibility by wrapping all the necessary components that allow a specific software to be executed. + +Similar to Docker, Singularity/Apptainer is a powerful tool that enables researchers to package entire environments, including software dependencies and libraries, into a single executable file. + +**Unlike other containerization platforms, Singularity/Apptainer is designed with [HPC](https://en.wikipedia.org/wiki/High-performance_computing) in mind, allowing seamless integration with cluster computing environments.** + +The biggest difference between Docker and Singularity/Apptainer, is that with the latter *you do **not** require sudo priviledges*. Exciting! + +In this workshop, we are going to learn how we can use Singularity/Apptainer on the UA HPC, covering orientation of the HPC, and executing Singularity/Apptainer control commands. + +--- + +## A 10,000ft View of the HPC + +[Next week](10_hpc.md) (Apr 04th) [Chris Reidy](https://datascience.arizona.edu/person/chris-reidy) from [UITS](https://it.arizona.edu/) is going to talk in more details regarding the hardware specifications of the UA HPC systems. + +Here, we are going to concentrate on *HOW* to operate the HPC system as a general user. + +!!! warning "Who is this lesson for?" + This workshop is primarely aimed to UA students, grad students, faculty and staff as being part of the University of Arizona grants you access to the UA HPC. + + If you are not part of UA... *you're still very welcome to take part of this lesson!* You may not be able to execute the commands but you can still walk out of this with a good understanding of your institution's own HPC and Singularity/Apptainer. Everyone's welcome! + +### Logging onto the HPC + +If you have a UA account, to connect to the HPC you need to use `ssh` ([Secure Shell](https://en.wikipedia.org/wiki/Secure_Shell)). Open a terminal, and type: + +``` +ssh @hpc.arizona.edu +``` + +Type your UA password and if successful you'll be greeted with a two-factor login. Select which choice, and complete the authentification. Once you are past the authentification steps, you will enter the [Bastion server](https://en.wikipedia.org/wiki/Bastion_host). This step has 2 purposes: + +1. Protect from attacks. +2. Select what HPC system you want to use. + +!!! warning "Note: the Bastion server is NOT YET the HPC! Here you cannot submit jobs or run analyes. Type `shell` in order to select what system you want to use." + +The whole process (from logging to selecting the system) looks like the following: + +``` +ssh cosi@hpc.arizona.edu +(cosi@hpc.arizona.edu) Password: +(cosi@hpc.arizona.edu) Duo two-factor login for cosi + +Enter a passcode or select one of the following options: + + 1. Duo Push to XXX-XXX-8418 + 2. SMS passcodes to XXX-XXX-8418 + +Passcode or option (1-2): 1 +Success. Logging you in... +Last login: Tue Mar 26 14:52:39 2024 from dhcp-10-132-212-1.uawifi.arizona.edu +This is a bastion host used to access the rest of the RT/HPC environment. + +Type "shell" to access the job submission hosts for all environments +----------------------------------------- + +[cosi@gatekeeper ~]$ shell +Last login: Wed Mar 20 10:30:25 2024 from gatekeeper.hpc.arizona.edu +*** +The default cluster for job submission is Puma +*** +Shortcut commands change the target cluster +----------------------------------------- +Puma: +$ puma +(puma) $ +Ocelote: +$ ocelote +(ocelote) $ +ElGato: +$ elgato +(elgato) $ +----------------------------------------- + +[cosi@wentletrap ~]$ ocelote +(ocelote) [cosi@wentletrap ~]$ +``` + +At this point you are in the Login Node, where you can submit jobs or ask for an interactive node. + +
+ ![nodes](https://uarizona.atlassian.net/wiki/download/thumbnails/75989999/HPCDiagram_FileTransfers.png?version=1&modificationDate=1696282205000&cacheVersion=1&api=v2&effects=drop-shadow&width=1124&height=686) +
A representation of what the HPC structure and its various nodes. [Source](https://uarizona.atlassian.net/wiki/spaces/UAHPC/pages/75989999/HPC+Quick+Start).
+
+ + +### Choosing a System + +In the example above, we chose the Ocelote system. Notice how there are 2 other choices: Puma and El Gato. Without going in too much detail, here are some of the statistics regarding the 3 HPC systems at the UA HPC. + +|System|Year of Aquisition|Processors|RAM|GPU| +|-|-|-|-|-| +|Puma|2020|2x AMD Zen2 48 CPU (94 cores total)|512GB|6x Nvidia V100S| +|Ocelote|2016|2x Xeon E5-2695v3 14-core (28 cores total)|192GB|46x Nvidia P100| +|El Gato|2013|2x Xeon E5-2650v2 8-core (16 core total)|64GB|removed as obsolete| + +Find the full systems specs at the [official UA HPC documentatio resources page](https://uarizona.atlassian.net/wiki/spaces/UAHPC/pages/75990208/Compute+Resources). + +El Gato is the oldest system, and potentially not useful for heavy research. Puma is the newest and most requested, whislt Ocelote is the "middle child": not as popular but still able to pack a punch. + +Depending on what your work is, your best bet would be Puma for heavy computation (if you are ok with waiting long queues); However, if your jobs aren't as taxing, then Ocelote could easily be a safe choice. + +For this workshop, we are going to be using Ocelote. + +### Checking Available Resources + +#### Allocations + +Once past the Bastion server and logged into the Ocelote login node, you are able to submit jobs or request an interactive node. Before you do so, it is wise to check your available resources. These resources are the ones made available to you by your PI or working group. + +In order for you to check your resources, type `va`. + +``` +(ocelote) [cosi@wentletrap ~]$ va +Windfall: Unlimited + +PI: parent_1743 Total time: 100000:00:00 + Total used*: 1:00:00 + Total encumbered: 0:00:00 + Total remaining: 99999:00:00 + Group: datalab Time used: 1:00:00 Time encumbered: 0:00:00 + + +*Usage includes all subgroups, some of which may not be displayed here +``` + +`va` allows you to view all of the resources available from all the groups you are part of. + +#### Storage + +There are a number of ways one can approach storage on the HPC: + +- Your own folder (in `/home/`): 50GB limit +- Your group (in `/groups/`): 500GB limit +- Your PI research (in `/xdisk/`): 20TB + +Four the purpose of this workshop, we can access the datalab group storage in `/groups/cosi` (we will try and rename it in the future). + +#### Queues and Submissions + +One can check queue times and sumbissions by executing the SLURM command `squeue`. This will display the job id, submission type (standard, windfall, high priority), name of submission, user, status (queued, running), time elapsed, number of used nodes, and nodelist. + +``` +(ocelote) [cosi@wentletrap ~]$ squeue + JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) +2875825_[432-1000] standard qchem snanayak PD 0:00 1 (AssocGrpCPUMinutesLimit) + 2890485 standard R92L_Met krishnag PD 0:00 1 (Dependency) + 2890484 standard R92L_Met krishnag PD 0:00 1 (Dependency) + 2890483 standard R92L_Met krishnag PD 0:00 1 (Dependency) + 2881573 standard R92W_Met krishnag PD 0:00 1 (Dependency) + 2881572 standard R92W_Met krishnag PD 0:00 1 (Dependency) + 2802511 standard eigen krishnag PD 0:00 1 (Dependency) + 2949224 standard brutefor theronir PD 0:00 1 (None) + 2898419 standard start_so mmartino PD 0:00 1 (Dependency) + 2898418 standard make_sor mmartino PD 0:00 1 (Dependency) + 2898416 standard start_so mmartino PD 0:00 1 (Dependency) + 2898415 standard make_sor mmartino PD 0:00 1 (Dependency) + 2898410 standard start_so mmartino PD 0:00 1 (Dependency) + 2898409 standard make_sor mmartino PD 0:00 1 (Dependency) + . . . . . . . . + . . . . . . . . + . . . . . . . . + 2884142 windfall li_d_t_6 bubin R 2-06:58:50 1 i5n14 + 2884107 windfall li_d_t_4 bubin R 2-07:00:26 1 i6n8 + 2884098 windfall li_d_t_7 bubin R 2-07:00:50 1 i6n5 + 2880486 windfall be_10b teodar R 4-22:35:44 1 i16n5 + 2880487 windfall be_9b teodar R 4-22:35:44 1 i16n6 + 2880488 windfall be_7b teodar R 4-22:35:44 1 i16n12 + 2880489 windfall be_2b teodar R 4-22:35:44 1 i16n16 +``` + +Likely, this will output 100s of lines, therefore if you want to check on your own job, you could use the CLI and `grep` to select the running submission (e.g., `squeue | grep ` or `squeue --user $NETID`). + +--- + +## HPC, SLURM and Jobs Submissions + +
+ SLURM + Slurm futurama +
+
+ SLURM not Slurm. +
+ +All of the UA HPC systems run on a workload manager and job scheduler named [SLURM](https://slurm.schedmd.com/documentation.html) (Simple Linux Utility for Resource Management). It's designed to manage and schedule computing resources such as CPUs, GPUs, memory, and storage across a cluster of interconnected nodes. + +You can learn more on SLURM and HPC system commands [here](https://uarizona.atlassian.net/wiki/spaces/UAHPC/pages/75989875/Running+Jobs+with+Slurm). + +### Job Submissions +There are 2 ways one can submit jobs onto the HPC system. The first is to run a **batch job**, which is the more popular submission type, whilst the other is by requesting an **interactive node**. + +#### Batch jobs + +As we are not going to be using batch submissions, we are not going to be going into too much detail. However, here is what you need to know. For more details on running batch jobs, visit the [official documentation page on batch jobs](https://uarizona.atlassian.net/wiki/spaces/UAHPC/pages/75989875/Running+Jobs+with+Slurm). + +!!! Note "Writing a Batch Script" + + Batch scripts require a number of job **directives**. These are similar to the Dockerfile instructions, but instead of telling Docker how to build the image, these instead tell the SLURM system what to do with the job. The essential directives are the following: + + |Directive|Purpose| + |-|-| + | `#SBATCH --account=group_name` | Specify the account where hours are charged. | + | `#SBATCH --partition=partition_name` | Set the job partition. This determines your job's priority and the hours charged. | + | `#SBATCH --time=DD-HH:MM:SS` | Set the job's runtime limit in days, hours, minutes, and seconds. A single job cannot exceed 10 days or 240 hours. | + | `#SBATCH --nodes=N` | Allocate N nodes to your job. | + | `#SBATCH --cpus-per-task=M`
and
`#SBATCH --ntasks=N` | ntasks specifies the number of tasks (or processes) the job will run. By default, you will be allocated one CPU/task. This can be increased by including the additional directive --cpus-per-task. | + | `#SBATCH --mem=Ngb` | Select N gb of memory per node. If "gb" is not included, this value defaults to MB. | + + After setting your directives, you can instruct the HPC to do what you require similar to a bash script. + + Here's an example of a batch job: + + ``` + #!/bin/bash + #SBATCH --job-name=blast_job # Job name + #SBATCH --partition=standard # Sets the job priority to standard + #SBATCH --nodes=1 # Number of nodes + #SBATCH --ntasks=1 # Number of tasks (processes) per node + #SBATCH --cpus-per-task=4 # Number of CPU cores per task + #SBATCH --mem=8G # Memory per node (in this case, 8GB) + #SBATCH --time=02:00:00 # Time limit (HH:MM:SS) + + # Load necessary modules + module load blast/2.12.0 # Load BLAST module (adjust version as needed) + + # Change to the directory where the job will run + cd $SLURM_SUBMIT_DIR + + # Define input and output files + query_file="query.fasta" # Input query file (FASTA format) + database_file="database.fasta" # BLAST database file (FASTA format) + output_file="blast_results.out" # Output file for BLAST results + + # Run BLAST command + blastp -query $query_file -db $database_file -out $output_file -evalue 0.001 -num_threads $SLURM_CPUS_PER_TASK + ``` + + !!! Tip "Submitting a Batch Script" + + - To submit jobs you need to use `sbatch`, such as `sbatch script.slurm` + - To cancel your job you do `scancel`, such as `scancel $JOBID` or `scancel -u $NETID` + + This will submit your job to the queue. Execution will depend on your submission type (partition). + +#### Launching an Interactive Node + +An **interactive node**, unlike batch jobs which are run asynchronously, allows immediate access to compute. Similar to batch jobs, interactive nodes are submitted to the queue, but once available, you will receive a prompt for a node with the selected resources. Read more on how to launch interactive jobs in [the official documentation](https://uarizona.atlassian.net/wiki/spaces/UAHPC/pages/75989825/Interactive+Jobs). + +!!! tip "The Quick and Dirty" + + Don't need a lot of resources and just want access to the compute? + + Just type `interactive`. + + Disclaimer: you may require to wait longer as your job is going to fall in the `windfall` queue. + +Following are a list of useful flags (options) for setting up the interactive node. + +|Flag|Default value| Description| Example| +|-|-|-|-| +| `-n` | 1 | Number of CPUs requested per node | interactive -n 8 | +| `-m` | 4GB | Memory per CPU | interactive -m 5GB | +| `-a` | none | Account (group) to charge | interactive -a datalab | +| `--partition=` | windfall | Partition to determine CPU time charges and is set to windfall when no account is specified, and is **set to standard when an account is provided.** | interactive --partition=windfall | +| `-t` | 01:00:00 | Time allocated to session. | interactive -t 08:00:00 | +| `-N` | 1 | Number of nodes. | There is no reason to exceed 1 node unless the number of CPUs requested is greater than the number of CPUs per node on a given cluster. | (in el gato, where number of CPU per node is 16 max ) interactive -n 32 -N 2 | + +An example for an interactive node is: + +``` +interactive -n 8 -m 16GB -a datalab -t 02:00:00 +``` + +The above example will request an interactive node with 8 cores, 16GB RAM, "charging" the datalab, running for 2 hours. Try it! + +!!! Note "Modules" + There are 100s of tools installed on the HPC, few of which are available on the login screen. These tools are available only during a batch job submission or within interactive jobs. + + To see what tools are already running, or which are available, you will need to use the `module` command. + + !!! tip "Helpful `module` commands" + |Command|Purpose| + |-|-| + |`module list`| Lists loaded modules| + |`module avail`| Lists available modules| + |`module spider`| Lists ALL modules| + |`module load`| Loads a module| + |`module help`| Help command!| + +--- + +## Singularity/Apptainer + +In 2021, the Sylabs, the developers behind the original Singularity, made a fork of the original project and renamed it [SingularityCE (Community Edition)](https://github.com/apptainer/singularity). This would allow for the SingularityCE to be compliat with FOSS and allowing for the community to contribute to the builds. The Singularity team then joined the [Linux Foundation](https://en.wikipedia.org/wiki/Linux_Foundation) and decided to rename their effor to [**Apptainer**](https://github.com/apptainer/apptainer). + +The technology behind Singularity/Apptainer is similar to the one of Docker, but, as mentioned before, it was created with the HPC in mind, and therefore bypasses the requirement of sudo. + +!!! Note + Until now we have used Singularity/Apptainer to refer to the same software. Onwards, you can decide whether to use Singularity *OR* Apptainer; in order to keep up with the latest release, we are going to be executing apptainer commands. + +??? info "Docker vs SingularityCE & Apptainer in the blink of an eye" + + **:material-open-source-initiative: Apptainer and SingularityCE are 100% compatible with Docker but they do have some distinct differences** + + + **:material-docker: Docker** + + :octicons-container-24: Docker containers run as `root` + + - This privilege is almost never supported by administrators of High Performance Computing (HPC) centers. Meaning Docker is not, and will likely never be, installed natively on your HPC cluster. + + :octicons-container-24: uses compressed layers to create one image + + **:material-open-source-initiative: SingularityCE & Apptainer**: + + :octicons-container-24: Same user and group identity inside as outside the container + + :octicons-container-24: User only has `root` privileges if elevated with `sudo` when the container is run + + :octicons-container-24: Can run and modify any existing Docker image + + - These key differences allow Singularity to be installed on most HPC centers. Because you can run virtually all Docker containers in Singularity, you can effectively run Docker on an HPC. + +### General Executable Commands + +Resources: + + - https://cc.cyverse.org/singularity/intro/ + - https://cc.cyverse.org/singularity/hpc/ + - https://cc.cyverse.org/singularity/advanced/ + + +Apptainer’s [command line interface](https://apptainer.org/docs/user/main/cli.html){target=_blank} allows you to build and interact with containers transparently. You can run programs inside a container as if they were running on your host system. You can easily redirect IO, use pipes, pass arguments, and access files, sockets, and ports on the host system from within a container. + +#### :octicons-container-24: help + +The `help` command gives an overview of Apptainer options and subcommands as follows: + +``` + $ apptainer help pull +Pull an image from a URI + +Usage: + apptainer pull [pull options...] [output file] + +Description: + The 'pull' command allows you to download or build a container from a given + URI. Supported URIs include: + + library: Pull an image from the currently configured library + library://user/collection/container[:tag] + + docker: Pull a Docker/OCI image from Docker Hub, or another OCI registry. + docker://user/image:tag + + shub: Pull an image from Singularity Hub + shub://user/image:tag + + oras: Pull a SIF image from an OCI registry that supports ORAS. + oras://registry/namespace/image:tag + + http, https: Pull an image using the http(s?) protocol + https://example.com/alpine.sif + +Options: + --arch string architecture to pull from library (default + "amd64") + --arch-variant string architecture variant to pull from library + --dir string download images to the specific directory + --disable-cache do not use or create cached images/blobs + --docker-host string specify a custom Docker daemon host + --docker-login login to a Docker Repository interactively + -F, --force overwrite an image file if it exists + -h, --help help for pull + --library string download images from the provided library + --no-cleanup do NOT clean up bundle after failed build, + can be helpful for debugging + --no-https use http instead of https for docker:// + oras:// and library:///... URIs + + +Examples: + From a library + $ apptainer pull alpine.sif library://alpine:latest + + From Docker + $ apptainer pull tensorflow.sif docker://tensorflow/tensorflow:latest + $ apptainer pull --arch arm --arch-variant 6 alpine.sif docker://alpine:latest + + From Shub + $ apptainer pull apptainer-images.sif shub://vsoch/apptainer-images + + From supporting OCI registry (e.g. Azure Container Registry) + $ apptainer pull image.sif oras://.azurecr.io/namespace/image:tag + + +For additional help or support, please visit https://apptainer.org/help/ +``` + + +#### :octicons-container-24: search + +Just like with Docker, you can `search` the Apptainer container [registries](https://apptainer.org/docs/user/1.0/library_api.html) for images. + +``` +$ apptainer search tensorflow +``` + +#### :octicons-container-24: pull + +The easiest way to use a Apptainer is to `pull` an existing container from one of the Registries. + +``` +$ apptainer pull library://lolcow +``` + +Not only you can pull fromt the Apptainer registries/libraries, but you can pull from Docker. + +``` +$ apptainer pull docker://alpine +``` + +!!! Note "In my humble opinion..." + + This is whre Apptainer shines: you can pull from Docker and run Docker built images on the HPC! These are automatically converted to Apptainer images (`.sif`) and executable on the HPC! + +!!! tip "... so where are the Apptainer `.sif` images stored?" + + Right where in the directory you are pulling them to. Check with `cd`! + +### Obtaining Images + +As metioned earlier, you can use the `pull` command to download pre-built images from a number of Container Registries, here we'll be focusing on the [DockerHub](https://hub.docker.com/). + +Container Registries: + +- `library://` - images hosted on Sylabs Cloud +- `docker://` - images hosted on Docker Hub +- `localimage://` - images saved on your machine +- `yum://` - yum based systems such as CentOS and Scientific Linux +- `debootstrap://` - apt based systems such as Debian and Ubuntu +- `arch://` - Arch Linux +- `busybox://` - BusyBox +- `zypper://` - zypper based systems such as Suse and OpenSuse +- `shub://` - (archived) images hosted on Singularity Hub, no longer maintained + +#### Pulling an image from Singularity Hub + +Similar to previous example, in this example I am pulling a base Ubuntu +container from Singularity-Hub: + +``` +$ apptainer pull shub://singularityhub/ubuntu +INFO: Downloading shub image +88.6MiB / 88.6MiB [=============================================================================] 100 % 39.1 MiB/s 0s +``` + +!!! tip "Re/naming" + You can give the the container using the `--name` flag: such as `apptainer pull --name my-own-ubuntu-pulled-image.sif shub://singularityhub/ubuntu` + + +#### Pulling an image from Docker Hub + +This example pulls an `ubuntu:22.04` image from DockerHub and saves it to the working directory. + +``` +$ apptainer pull docker://ubuntu:22.04 +INFO: Converting OCI blobs to SIF format +INFO: Starting build... +Getting image source signatures +Copying blob bccd10f490ab done +Copying config ca2b0f2696 done +Writing manifest to image destination +Storing signatures +2024/03/27 20:14:50 info unpack layer: sha256:bccd10f490ab0f3fba61b193d1b80af91b17ca9bdca9768a16ed05ce16552fcb +INFO: Creating SIF file... +``` + +### Interacting with Images + +You can interact with images in several ways such as `run`, `shell` and `exec`. + +For these examples we will use a `cowsay_latest.sif` image that can be pulled from the Docker Hub. + +``` +$ apptainer pull docker://tswetnam/cowsay +INFO: Converting OCI blobs to SIF format +INFO: Starting build... +Getting image source signatures +Copying blob 05e030abce7b done +Copying blob b4624b3efe06 done +Copying blob 6cf436f81810 done +Copying blob 987088a85b96 done +Copying blob d42beb8ded59 done +Copying config ee9e20351a done +Writing manifest to image destination +Storing signatures +2024/03/27 20:16:29 info unpack layer: sha256:6cf436f81810f067c6d4ffca6793eae7cb6d38456715b0707d8a5a2d1acccf12 +2024/03/27 20:16:29 warn rootless{dev/full} creating empty file in place of device 1:7 +2024/03/27 20:16:29 warn rootless{dev/null} creating empty file in place of device 1:3 +2024/03/27 20:16:29 warn rootless{dev/ptmx} creating empty file in place of device 5:2 +2024/03/27 20:16:29 warn rootless{dev/random} creating empty file in place of device 1:8 +2024/03/27 20:16:29 warn rootless{dev/tty} creating empty file in place of device 5:0 +2024/03/27 20:16:29 warn rootless{dev/urandom} creating empty file in place of device 1:9 +2024/03/27 20:16:29 warn rootless{dev/zero} creating empty file in place of device 1:5 +2024/03/27 20:16:30 info unpack layer: sha256:987088a85b9606eb474a365eb210db765ff0d011ee099a6e3de5087435c6f966 +2024/03/27 20:16:30 info unpack layer: sha256:b4624b3efe0617e59ed3998407eafdbe1cb6451346a6cabd066b6e253f50efb1 +2024/03/27 20:16:30 info unpack layer: sha256:d42beb8ded595df5627ad4ef31bf528a6fdbfbd11d82f9023152738d6b05a7fa +2024/03/27 20:16:30 info unpack layer: sha256:05e030abce7b562606031bcc54646a868984685f4c89c7c354f34f1f6e502917 +INFO: Creating SIF file.. + +$ ls +alpine_latest.sif lolcow_latest.sif ubuntu_22.04.sif cowsay_latest.sif +``` + +#### :octicons-container-24: run + +Apptainer containers contain [runscripts](https://www.sylabs.io/guides/3.0/user-guide/definition_files.html#runscript). These are user defined scripts that define the actions a container should perform when someone runs it. The runscript can be triggered with the `run` command, or simply by calling the container as though it were an executable. + +``` +$ apptainer run cowsay_latest.sif +INFO: underlay of /etc/localtime required more than 50 (76) bind mounts + ____________________________________ +/ Q: Do you know what the death rate \ +\ around here is? A: One per person. / + ------------------------------------ + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +``` + +#### :octicons-container-24: shell + +The `shell` command allows you to spawn a new shell within your container and interact with it as though it were a small virtual machine. + +``` +$ apptainer shell cowsay_latest.sif +INFO: underlay of /etc/localtime required more than 50 (76) bind mounts +(ocelote) Apptainer> +``` + +The change in prompt indicates that you have entered the container (though you should not rely on that to determine whether you are in container or not). + +Once inside of a Apptainer container, you are the same user as you are on the host system. + +``` +(ocelote) Apptainer> whoami +cosi +``` + +!!! tip "Type `exit` to exit the container." + +!!! Warning "The more you know :material-star-shooting:" + `shell` also works with the `library://`, `docker://`, and `shub://` URIs. + This creates an **ephemeral container**\* that disappears when the shell is + exited. + + **Ephemeral container**\*: a short-lived container instance that is created dynamically to perform a specific task or process and then terminated once the task is complete. These containers are typically used for one-off jobs, temporary operations, or short-duration tasks within a larger computing environment. + + +#### :octicons-container-24: exec + +The exec command allows you to execute a custom command within a container by specifying the image file. For instance, to execute the `cowsay` program within the `cowsay_latest.sif` container: + +``` +$ apptainer exec cowsay_latest.sif cowsay whoaaaa the grass is soooo green inside the HPC! +INFO: underlay of /etc/localtime required more than 50 (76) bind mounts + _________________________________________ +/ whoaaaa the grass is soooo green inside \ +\ the HPC! / + ----------------------------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +``` + +This also creates an ephemeral container that executes a command and disappears. + +#### :octicons-container-24: inspect + +The `inspect` command will provide information about labels, metadata, and environmental variables. + +``` +$ apptainer inspect cowsay_latest.sif +org.label-schema.build-arch: amd64 +org.label-schema.build-date: Wednesday_27_March_2024_20:16:32_MST +org.label-schema.schema-version: 1.0 +org.label-schema.usage.apptainer.version: 1.2.5-1.el7 +org.label-schema.usage.singularity.deffile.bootstrap: docker +org.label-schema.usage.singularity.deffile.from: tswetnam/cowsay +``` \ No newline at end of file diff --git a/10_reproducibility_IV/index.html b/10_reproducibility_IV/index.html new file mode 100644 index 000000000..a67f14fa7 --- /dev/null +++ b/10_reproducibility_IV/index.html @@ -0,0 +1,2226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10. Remote Computing: HPC - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Remote Computing with High Performance Computer (HPC)

+
+

sin-2-app +

Singularity > Apptainer and working with the SLURM workload manager (HPC scheduler).

+
+
+

Expected Outcomes

+
    +
  • Being able to log onto the HPC and start an interactive node
  • +
  • Know basic HPC related commands
  • +
  • Execute containerized code through Singularity/Apptainer
  • +
  • Being able to search and load specific software
  • +
  • Execute NextFlow scripts on the HPC
  • +
+
+

Through FOSS and FOSS+ we have learned about container technology and how it can affect reproducibility by wrapping all the necessary components that allow a specific software to be executed.

+

Similar to Docker, Singularity/Apptainer is a powerful tool that enables researchers to package entire environments, including software dependencies and libraries, into a single executable file.

+

Unlike other containerization platforms, Singularity/Apptainer is designed with HPC in mind, allowing seamless integration with cluster computing environments.

+

The biggest difference between Docker and Singularity/Apptainer, is that with the latter you do not require sudo priviledges. Exciting!

+

In this workshop, we are going to learn how we can use Singularity/Apptainer on the UA HPC, covering orientation of the HPC, and executing Singularity/Apptainer control commands.

+
+

A 10,000ft View of the HPC

+

Next week (Apr 04th) Chris Reidy from UITS is going to talk in more details regarding the hardware specifications of the UA HPC systems.

+

Here, we are going to concentrate on HOW to operate the HPC system as a general user.

+
+

Who is this lesson for?

+

This workshop is primarely aimed to UA students, grad students, faculty and staff as being part of the University of Arizona grants you access to the UA HPC.

+

If you are not part of UA... you're still very welcome to take part of this lesson! You may not be able to execute the commands but you can still walk out of this with a good understanding of your institution's own HPC and Singularity/Apptainer. Everyone's welcome!

+
+

Logging onto the HPC

+

If you have a UA account, to connect to the HPC you need to use ssh (Secure Shell). Open a terminal, and type:

+
ssh <UA username>@hpc.arizona.edu
+
+

Type your UA password and if successful you'll be greeted with a two-factor login. Select which choice, and complete the authentification. Once you are past the authentification steps, you will enter the Bastion server. This step has 2 purposes:

+
    +
  1. Protect from attacks.
  2. +
  3. Select what HPC system you want to use.
  4. +
+
+

Note: the Bastion server is NOT YET the HPC! Here you cannot submit jobs or run analyes. Type shell in order to select what system you want to use.

+
+

The whole process (from logging to selecting the system) looks like the following:

+
ssh cosi@hpc.arizona.edu
+(cosi@hpc.arizona.edu) Password: 
+(cosi@hpc.arizona.edu) Duo two-factor login for cosi
+
+Enter a passcode or select one of the following options:
+
+ 1. Duo Push to XXX-XXX-8418
+ 2. SMS passcodes to XXX-XXX-8418
+
+Passcode or option (1-2): 1
+Success. Logging you in...
+Last login: Tue Mar 26 14:52:39 2024 from dhcp-10-132-212-1.uawifi.arizona.edu
+This is a bastion host used to access the rest of the RT/HPC environment.
+
+Type "shell" to access the job submission hosts for all environments
+-----------------------------------------
+
+[cosi@gatekeeper ~]$ shell
+Last login: Wed Mar 20 10:30:25 2024 from gatekeeper.hpc.arizona.edu
+***
+The default cluster for job submission is Puma
+***
+Shortcut commands change the target cluster
+-----------------------------------------
+Puma:
+$ puma
+(puma) $
+Ocelote:
+$ ocelote
+(ocelote) $
+ElGato:
+$ elgato
+(elgato) $
+-----------------------------------------
+
+[cosi@wentletrap ~]$ ocelote
+(ocelote) [cosi@wentletrap ~]$
+
+

At this point you are in the Login Node, where you can submit jobs or ask for an interactive node.

+
+

nodes +

A representation of what the HPC structure and its various nodes. Source.

+
+

Choosing a System

+

In the example above, we chose the Ocelote system. Notice how there are 2 other choices: Puma and El Gato. Without going in too much detail, here are some of the statistics regarding the 3 HPC systems at the UA HPC.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SystemYear of AquisitionProcessorsRAMGPU
Puma20202x AMD Zen2 48 CPU (94 cores total)512GB6x Nvidia V100S
Ocelote20162x Xeon E5-2695v3 14-core (28 cores total)192GB46x Nvidia P100
El Gato20132x Xeon E5-2650v2 8-core (16 core total)64GBremoved as obsolete
+

Find the full systems specs at the official UA HPC documentatio resources page.

+

El Gato is the oldest system, and potentially not useful for heavy research. Puma is the newest and most requested, whislt Ocelote is the "middle child": not as popular but still able to pack a punch.

+

Depending on what your work is, your best bet would be Puma for heavy computation (if you are ok with waiting long queues); However, if your jobs aren't as taxing, then Ocelote could easily be a safe choice.

+

For this workshop, we are going to be using Ocelote.

+

Checking Available Resources

+

Allocations

+

Once past the Bastion server and logged into the Ocelote login node, you are able to submit jobs or request an interactive node. Before you do so, it is wise to check your available resources. These resources are the ones made available to you by your PI or working group.

+

In order for you to check your resources, type va.

+
(ocelote) [cosi@wentletrap ~]$ va
+Windfall: Unlimited
+
+PI: parent_1743 Total time: 100000:00:00
+    Total used*: 1:00:00
+    Total encumbered: 0:00:00
+    Total remaining: 99999:00:00
+    Group: datalab Time used: 1:00:00 Time encumbered: 0:00:00
+
+
+*Usage includes all subgroups, some of which may not be displayed here
+
+

va allows you to view all of the resources available from all the groups you are part of.

+

Storage

+

There are a number of ways one can approach storage on the HPC:

+
    +
  • Your own folder (in /home/): 50GB limit
  • +
  • Your group (in /groups/): 500GB limit
  • +
  • Your PI research (in /xdisk/): 20TB
  • +
+

Four the purpose of this workshop, we can access the datalab group storage in /groups/cosi (we will try and rename it in the future).

+

Queues and Submissions

+

One can check queue times and sumbissions by executing the SLURM command squeue. This will display the job id, submission type (standard, windfall, high priority), name of submission, user, status (queued, running), time elapsed, number of used nodes, and nodelist.

+
(ocelote) [cosi@wentletrap ~]$ squeue
+             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
+2875825_[432-1000]  standard    qchem snanayak PD       0:00      1 (AssocGrpCPUMinutesLimit)
+           2890485  standard R92L_Met krishnag PD       0:00      1 (Dependency)
+           2890484  standard R92L_Met krishnag PD       0:00      1 (Dependency)
+           2890483  standard R92L_Met krishnag PD       0:00      1 (Dependency)
+           2881573  standard R92W_Met krishnag PD       0:00      1 (Dependency)
+           2881572  standard R92W_Met krishnag PD       0:00      1 (Dependency)
+           2802511  standard    eigen krishnag PD       0:00      1 (Dependency)
+           2949224  standard brutefor theronir PD       0:00      1 (None)
+           2898419  standard start_so mmartino PD       0:00      1 (Dependency)
+           2898418  standard make_sor mmartino PD       0:00      1 (Dependency)
+           2898416  standard start_so mmartino PD       0:00      1 (Dependency)
+           2898415  standard make_sor mmartino PD       0:00      1 (Dependency)
+           2898410  standard start_so mmartino PD       0:00      1 (Dependency)
+           2898409  standard make_sor mmartino PD       0:00      1 (Dependency)
+              .         .        .        .     .       .         .   .
+              .         .        .        .     .       .         .   .
+              .         .        .        .     .       .         .   .
+           2884142  windfall li_d_t_6    bubin  R 2-06:58:50      1 i5n14
+           2884107  windfall li_d_t_4    bubin  R 2-07:00:26      1 i6n8
+           2884098  windfall li_d_t_7    bubin  R 2-07:00:50      1 i6n5
+           2880486  windfall   be_10b   teodar  R 4-22:35:44      1 i16n5
+           2880487  windfall    be_9b   teodar  R 4-22:35:44      1 i16n6
+           2880488  windfall    be_7b   teodar  R 4-22:35:44      1 i16n12
+           2880489  windfall    be_2b   teodar  R 4-22:35:44      1 i16n16
+
+

Likely, this will output 100s of lines, therefore if you want to check on your own job, you could use the CLI and grep to select the running submission (e.g., squeue | grep <username> or squeue --user $NETID).

+
+

HPC, SLURM and Jobs Submissions

+
+ SLURM + Slurm futurama +
+
+ SLURM not Slurm. +
+ +

All of the UA HPC systems run on a workload manager and job scheduler named SLURM (Simple Linux Utility for Resource Management). It's designed to manage and schedule computing resources such as CPUs, GPUs, memory, and storage across a cluster of interconnected nodes.

+

You can learn more on SLURM and HPC system commands here.

+

Job Submissions

+

There are 2 ways one can submit jobs onto the HPC system. The first is to run a batch job, which is the more popular submission type, whilst the other is by requesting an interactive node.

+

Batch jobs

+

As we are not going to be using batch submissions, we are not going to be going into too much detail. However, here is what you need to know. For more details on running batch jobs, visit the official documentation page on batch jobs.

+
+

Writing a Batch Script

+

Batch scripts require a number of job directives. These are similar to the Dockerfile instructions, but instead of telling Docker how to build the image, these instead tell the SLURM system what to do with the job. The essential directives are the following:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DirectivePurpose
#SBATCH --account=group_nameSpecify the account where hours are charged.
#SBATCH --partition=partition_nameSet the job partition. This determines your job's priority and the hours charged.
#SBATCH --time=DD-HH:MM:SSSet the job's runtime limit in days, hours, minutes, and seconds. A single job cannot exceed 10 days or 240 hours.
#SBATCH --nodes=NAllocate N nodes to your job.
#SBATCH --cpus-per-task=M
and
#SBATCH --ntasks=N
ntasks specifies the number of tasks (or processes) the job will run. By default, you will be allocated one CPU/task. This can be increased by including the additional directive --cpus-per-task.
#SBATCH --mem=NgbSelect N gb of memory per node. If "gb" is not included, this value defaults to MB.
+

After setting your directives, you can instruct the HPC to do what you require similar to a bash script.

+

Here's an example of a batch job:

+
#!/bin/bash
+#SBATCH --job-name=blast_job          # Job name
+#SBATCH --partition=standard          # Sets the job priority to standard
+#SBATCH --nodes=1                     # Number of nodes
+#SBATCH --ntasks=1                    # Number of tasks (processes) per node
+#SBATCH --cpus-per-task=4             # Number of CPU cores per task
+#SBATCH --mem=8G                      # Memory per node (in this case, 8GB)
+#SBATCH --time=02:00:00               # Time limit (HH:MM:SS)
+
+# Load necessary modules
+module load blast/2.12.0              # Load BLAST module (adjust version as needed)
+
+# Change to the directory where the job will run
+cd $SLURM_SUBMIT_DIR
+
+# Define input and output files
+query_file="query.fasta"              # Input query file (FASTA format)
+database_file="database.fasta"        # BLAST database file (FASTA format)
+output_file="blast_results.out"       # Output file for BLAST results
+
+# Run BLAST command
+blastp -query $query_file -db $database_file -out $output_file -evalue 0.001 -num_threads $SLURM_CPUS_PER_TASK
+
+
+

Submitting a Batch Script

+
    +
  • To submit jobs you need to use sbatch, such as sbatch script.slurm
  • +
  • To cancel your job you do scancel, such as scancel $JOBID or scancel -u $NETID
  • +
+

This will submit your job to the queue. Execution will depend on your submission type (partition).

+
+
+

Launching an Interactive Node

+

An interactive node, unlike batch jobs which are run asynchronously, allows immediate access to compute. Similar to batch jobs, interactive nodes are submitted to the queue, but once available, you will receive a prompt for a node with the selected resources. Read more on how to launch interactive jobs in the official documentation.

+
+

The Quick and Dirty

+

Don't need a lot of resources and just want access to the compute?

+

Just type interactive.

+

Disclaimer: you may require to wait longer as your job is going to fall in the windfall queue.

+
+

Following are a list of useful flags (options) for setting up the interactive node.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FlagDefault valueDescriptionExample
-n1Number of CPUs requested per nodeinteractive -n 8
-m4GBMemory per CPUinteractive -m 5GB
-anoneAccount (group) to chargeinteractive -a datalab
--partition=windfallPartition to determine CPU time charges and is set to windfall when no account is specified, and is set to standard when an account is provided.interactive --partition=windfall
-t01:00:00Time allocated to session.interactive -t 08:00:00
-N1Number of nodes.There is no reason to exceed 1 node unless the number of CPUs requested is greater than the number of CPUs per node on a given cluster.
+

An example for an interactive node is:

+
interactive -n 8 -m 16GB -a datalab -t 02:00:00 
+
+

The above example will request an interactive node with 8 cores, 16GB RAM, "charging" the datalab, running for 2 hours. Try it!

+
+

Modules

+

There are 100s of tools installed on the HPC, few of which are available on the login screen. These tools are available only during a batch job submission or within interactive jobs.

+

To see what tools are already running, or which are available, you will need to use the module command.

+
+

Helpful module commands

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandPurpose
module listLists loaded modules
module availLists available modules
module spiderLists ALL modules
module loadLoads a module
module helpHelp command!
+
+
+
+

Singularity/Apptainer

+

In 2021, the Sylabs, the developers behind the original Singularity, made a fork of the original project and renamed it SingularityCE (Community Edition). This would allow for the SingularityCE to be compliat with FOSS and allowing for the community to contribute to the builds. The Singularity team then joined the Linux Foundation and decided to rename their effor to Apptainer.

+

The technology behind Singularity/Apptainer is similar to the one of Docker, but, as mentioned before, it was created with the HPC in mind, and therefore bypasses the requirement of sudo.

+
+

Note

+

Until now we have used Singularity/Apptainer to refer to the same software. Onwards, you can decide whether to use Singularity OR Apptainer; in order to keep up with the latest release, we are going to be executing apptainer commands.

+
+
+Docker vs SingularityCE & Apptainer in the blink of an eye +

Apptainer and SingularityCE are 100% compatible with Docker but they do have some distinct differences

+

Docker

+

Docker containers run as root

+
    +
  • This privilege is almost never supported by administrators of High Performance Computing (HPC) centers. Meaning Docker is not, and will likely never be, installed natively on your HPC cluster.
  • +
+

uses compressed layers to create one image

+

SingularityCE & Apptainer:

+

Same user and group identity inside as outside the container

+

User only has root privileges if elevated with sudo when the container is run

+

Can run and modify any existing Docker image

+
    +
  • These key differences allow Singularity to be installed on most HPC centers. Because you can run virtually all Docker containers in Singularity, you can effectively run Docker on an HPC.
  • +
+
+

General Executable Commands

+

Resources:

+ +

Apptainer’s command line interface allows you to build and interact with containers transparently. You can run programs inside a container as if they were running on your host system. You can easily redirect IO, use pipes, pass arguments, and access files, sockets, and ports on the host system from within a container.

+

help

+

The help command gives an overview of Apptainer options and subcommands as follows:

+
 $ apptainer help pull
+Pull an image from a URI
+
+Usage:
+  apptainer pull [pull options...] [output file] <URI>
+
+Description:
+  The 'pull' command allows you to download or build a container from a given
+  URI. Supported URIs include:
+
+  library: Pull an image from the currently configured library
+      library://user/collection/container[:tag]
+
+  docker: Pull a Docker/OCI image from Docker Hub, or another OCI registry.
+      docker://user/image:tag
+
+  shub: Pull an image from Singularity Hub
+      shub://user/image:tag
+
+  oras: Pull a SIF image from an OCI registry that supports ORAS.
+      oras://registry/namespace/image:tag
+
+  http, https: Pull an image using the http(s?) protocol
+      https://example.com/alpine.sif
+
+Options:
+      --arch string           architecture to pull from library (default
+                              "amd64")
+      --arch-variant string   architecture variant to pull from library
+      --dir string            download images to the specific directory
+      --disable-cache         do not use or create cached images/blobs
+      --docker-host string    specify a custom Docker daemon host
+      --docker-login          login to a Docker Repository interactively
+  -F, --force                 overwrite an image file if it exists
+  -h, --help                  help for pull
+      --library string        download images from the provided library
+      --no-cleanup            do NOT clean up bundle after failed build,
+                              can be helpful for debugging
+      --no-https              use http instead of https for docker://
+                              oras:// and library://<hostname>/... URIs
+
+
+Examples:
+  From a library
+  $ apptainer pull alpine.sif library://alpine:latest
+
+  From Docker
+  $ apptainer pull tensorflow.sif docker://tensorflow/tensorflow:latest
+  $ apptainer pull --arch arm --arch-variant 6 alpine.sif docker://alpine:latest
+
+  From Shub
+  $ apptainer pull apptainer-images.sif shub://vsoch/apptainer-images
+
+  From supporting OCI registry (e.g. Azure Container Registry)
+  $ apptainer pull image.sif oras://<username>.azurecr.io/namespace/image:tag
+
+
+For additional help or support, please visit https://apptainer.org/help/
+
+ +

Just like with Docker, you can search the Apptainer container registries for images.

+
$ apptainer search tensorflow
+
+

pull

+

The easiest way to use a Apptainer is to pull an existing container from one of the Registries.

+
$ apptainer pull library://lolcow
+
+

Not only you can pull fromt the Apptainer registries/libraries, but you can pull from Docker.

+
$ apptainer pull docker://alpine
+
+
+

In my humble opinion...

+

This is whre Apptainer shines: you can pull from Docker and run Docker built images on the HPC! These are automatically converted to Apptainer images (.sif) and executable on the HPC!

+
+
+

... so where are the Apptainer .sif images stored?

+

Right where in the directory you are pulling them to. Check with cd!

+
+

Obtaining Images

+

As metioned earlier, you can use the pull command to download pre-built images from a number of Container Registries, here we'll be focusing on the DockerHub.

+

Container Registries:

+
    +
  • library:// - images hosted on Sylabs Cloud
  • +
  • docker:// - images hosted on Docker Hub
  • +
  • localimage:// - images saved on your machine
  • +
  • yum:// - yum based systems such as CentOS and Scientific Linux
  • +
  • debootstrap:// - apt based systems such as Debian and Ubuntu
  • +
  • arch:// - Arch Linux
  • +
  • busybox:// - BusyBox
  • +
  • zypper:// - zypper based systems such as Suse and OpenSuse
  • +
  • shub:// - (archived) images hosted on Singularity Hub, no longer maintained
  • +
+

Pulling an image from Singularity Hub

+

Similar to previous example, in this example I am pulling a base Ubuntu +container from Singularity-Hub:

+
$ apptainer pull shub://singularityhub/ubuntu
+INFO:    Downloading shub image
+88.6MiB / 88.6MiB [=============================================================================] 100 % 39.1 MiB/s 0s
+
+
+

Re/naming

+

You can give the the container using the --name flag: such as apptainer pull --name my-own-ubuntu-pulled-image.sif shub://singularityhub/ubuntu

+
+

Pulling an image from Docker Hub

+

This example pulls an ubuntu:22.04 image from DockerHub and saves it to the working directory.

+
$ apptainer pull docker://ubuntu:22.04
+INFO:    Converting OCI blobs to SIF format
+INFO:    Starting build...
+Getting image source signatures
+Copying blob bccd10f490ab done
+Copying config ca2b0f2696 done
+Writing manifest to image destination
+Storing signatures
+2024/03/27 20:14:50  info unpack layer: sha256:bccd10f490ab0f3fba61b193d1b80af91b17ca9bdca9768a16ed05ce16552fcb
+INFO:    Creating SIF file...
+
+

Interacting with Images

+

You can interact with images in several ways such as run, shell and exec.

+

For these examples we will use a cowsay_latest.sif image that can be pulled from the Docker Hub.

+
$ apptainer pull docker://tswetnam/cowsay
+INFO:    Converting OCI blobs to SIF format
+INFO:    Starting build...
+Getting image source signatures
+Copying blob 05e030abce7b done
+Copying blob b4624b3efe06 done
+Copying blob 6cf436f81810 done
+Copying blob 987088a85b96 done
+Copying blob d42beb8ded59 done
+Copying config ee9e20351a done
+Writing manifest to image destination
+Storing signatures
+2024/03/27 20:16:29  info unpack layer: sha256:6cf436f81810f067c6d4ffca6793eae7cb6d38456715b0707d8a5a2d1acccf12
+2024/03/27 20:16:29  warn rootless{dev/full} creating empty file in place of device 1:7
+2024/03/27 20:16:29  warn rootless{dev/null} creating empty file in place of device 1:3
+2024/03/27 20:16:29  warn rootless{dev/ptmx} creating empty file in place of device 5:2
+2024/03/27 20:16:29  warn rootless{dev/random} creating empty file in place of device 1:8
+2024/03/27 20:16:29  warn rootless{dev/tty} creating empty file in place of device 5:0
+2024/03/27 20:16:29  warn rootless{dev/urandom} creating empty file in place of device 1:9
+2024/03/27 20:16:29  warn rootless{dev/zero} creating empty file in place of device 1:5
+2024/03/27 20:16:30  info unpack layer: sha256:987088a85b9606eb474a365eb210db765ff0d011ee099a6e3de5087435c6f966
+2024/03/27 20:16:30  info unpack layer: sha256:b4624b3efe0617e59ed3998407eafdbe1cb6451346a6cabd066b6e253f50efb1
+2024/03/27 20:16:30  info unpack layer: sha256:d42beb8ded595df5627ad4ef31bf528a6fdbfbd11d82f9023152738d6b05a7fa
+2024/03/27 20:16:30  info unpack layer: sha256:05e030abce7b562606031bcc54646a868984685f4c89c7c354f34f1f6e502917
+INFO:    Creating SIF file..
+
+$ ls
+alpine_latest.sif  lolcow_latest.sif  ubuntu_22.04.sif  cowsay_latest.sif
+
+

run

+

Apptainer containers contain runscripts. These are user defined scripts that define the actions a container should perform when someone runs it. The runscript can be triggered with the run command, or simply by calling the container as though it were an executable.

+
$ apptainer run cowsay_latest.sif
+INFO:    underlay of /etc/localtime required more than 50 (76) bind mounts
+ ____________________________________
+/ Q: Do you know what the death rate \
+\ around here is? A: One per person. /
+ ------------------------------------
+        \   ^__^
+         \  (oo)\_______
+            (__)\       )\/\
+                ||----w |
+                ||     ||
+
+

shell

+

The shell command allows you to spawn a new shell within your container and interact with it as though it were a small virtual machine.

+
$ apptainer shell cowsay_latest.sif
+INFO:    underlay of /etc/localtime required more than 50 (76) bind mounts
+(ocelote) Apptainer>
+
+

The change in prompt indicates that you have entered the container (though you should not rely on that to determine whether you are in container or not).

+

Once inside of a Apptainer container, you are the same user as you are on the host system.

+
(ocelote) Apptainer> whoami
+cosi
+
+
+

Type exit to exit the container.

+
+
+

The more you know

+

shell also works with the library://, docker://, and shub:// URIs. +This creates an ephemeral container* that disappears when the shell is +exited.

+

Ephemeral container*: a short-lived container instance that is created dynamically to perform a specific task or process and then terminated once the task is complete. These containers are typically used for one-off jobs, temporary operations, or short-duration tasks within a larger computing environment.

+
+

exec

+

The exec command allows you to execute a custom command within a container by specifying the image file. For instance, to execute the cowsay program within the cowsay_latest.sif container:

+
$ apptainer exec cowsay_latest.sif cowsay whoaaaa the grass is soooo green inside the HPC!
+INFO:    underlay of /etc/localtime required more than 50 (76) bind mounts
+ _________________________________________
+/ whoaaaa the grass is soooo green inside \
+\ the HPC!                                /
+ -----------------------------------------
+        \   ^__^
+         \  (oo)\_______
+            (__)\       )\/\
+                ||----w |
+                ||     ||
+
+

This also creates an ephemeral container that executes a command and disappears.

+

inspect

+

The inspect command will provide information about labels, metadata, and environmental variables.

+
$ apptainer inspect cowsay_latest.sif
+org.label-schema.build-arch: amd64
+org.label-schema.build-date: Wednesday_27_March_2024_20:16:32_MST
+org.label-schema.schema-version: 1.0
+org.label-schema.usage.apptainer.version: 1.2.5-1.el7
+org.label-schema.usage.singularity.deffile.bootstrap: docker
+org.label-schema.usage.singularity.deffile.from: tswetnam/cowsay
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/11_sql_duckdb/11_sql_duckdb.md b/11_sql_duckdb/11_sql_duckdb.md new file mode 100644 index 000000000..6e74f7968 --- /dev/null +++ b/11_sql_duckdb/11_sql_duckdb.md @@ -0,0 +1,1949 @@ +# Exploring SQL Databases with DuckDB + +___ + +## Database + +* A database is a structured collection of data. +* It allows for the storage, retrieval, modification, and deletion of data in an organized manner. +* Databases come in various types, including relational and NoSQL. Relational databases organize data into tables, which are interconnected. +* Each table represents a related data collection and is organized into rows and columns. + +![Relational DB Image](https://raw.githubusercontent.com/ua-datalab/DataEngineering/main/images/unnamed.png) + +___ + +## SQL stands for Structured Query Language. + +It's a programming language that stores and processes information in relational databases. + +### Types of SQL Statements + +* DDL (Data Definition Language): DDL statements are used to define, modify, and remove database structures, but not the data within them. Common DDL statements include CREATE, ALTER, and DROP. +* DML (Data Manipulation Language): DML statements manage data within schema objects. They include SELECT, INSERT, UPDATE, and DELETE. + +___ + +## DuckDB +Duck DB Logo + +* An in-memory, columnar database management system optimized for analytical queries." +* Open-source and designed for simplicity, speed, and efficiency in processing analytical workloads." +* Supports SQL standards for easy integration with existing tools and workflows. +___ +## Install DuckDB +### With Python set up, you can now install DuckDB using pip. In your terminal or command line, execute: +```console +pip install duckdb +``` +___ + + + +___ + +### To ensure DuckDB is installed correctly, launch your jupyter Notebook and try importing the DuckDB module: + +```python +pip install duckdb +``` +### If no errors occur, DuckDB is successfully installed and ready to use. +___ +### In your Jupyter Notebook, import duckdb by running +```python +import duckdb +``` +### For this tutorial, we will be using a pre-configured Duckdb Database. +### Let's download the database file: +```python +!wget --content-disposition https://arizona.box.com/shared/static/uozg0z86rtdjupwpc7i971xwzuzhp42o.duckdb +``` + +### Connect to the database using: +```python +conn = duckdb.connect(database='/content/my_database.duckdb', read_only=True) +``` +### Viewing which tables are available inside the database + +```python +conn.sql("SHOW TABLES;") +``` + +```python +┌───────────────────┐ +│ name │ +│ varchar │ +├───────────────────┤ +│ allergies │ +│ careplans │ +│ conditions │ +│ devices │ +│ encounters │ +│ imaging_studies │ +│ immunizations │ +│ medications │ +│ observations │ +│ organizations │ +│ patients │ +│ payer_transitions │ +│ payers │ +│ procedures │ +│ providers │ +│ supplies │ +├───────────────────┤ +│ 16 rows │ +└───────────────────┘ +``` +### Before running any query, we need to know the columns inside particular tables + +```python +conn.sql("DESCRIBE patients;") +``` +```python +┌─────────────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐ +│ column_name │ column_type │ null │ key │ default │ extra │ +│ varchar │ varchar │ varchar │ varchar │ varchar │ int32 │ +├─────────────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤ +│ Id │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ BIRTHDATE │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ DEATHDATE │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ SSN │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ DRIVERS │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ PASSPORT │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ PREFIX │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ FIRST │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ LAST │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ SUFFIX │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ · │ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ · │ +│ BIRTHPLACE │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ ADDRESS │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ CITY │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ STATE │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ COUNTY │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ ZIP │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +│ LAT │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +│ LON │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +│ HEALTHCARE_EXPENSES │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +│ HEALTHCARE_COVERAGE │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +├─────────────────────┴─────────────┴─────────┴─────────┴─────────┴───────┤ +│ 25 rows (20 shown) 6 columns │ +└─────────────────────────────────────────────────────────────────────────┘ +``` +```python +conn.sql("DESCRIBE medications;") +``` +```python + +conn.sql("DESCRIBE medications;") +┌───────────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐ +│ column_name │ column_type │ null │ key │ default │ extra │ +│ varchar │ varchar │ varchar │ varchar │ varchar │ int32 │ +├───────────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤ +│ START │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ STOP │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ PATIENT │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ PAYER │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ ENCOUNTER │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ CODE │ BIGINT │ YES │ NULL │ NULL │ NULL │ +│ DESCRIPTION │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ BASE_COST │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +│ PAYER_COVERAGE │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +│ DISPENSES │ BIGINT │ YES │ NULL │ NULL │ NULL │ +│ TOTALCOST │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +│ REASONCODE │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +│ REASONDESCRIPTION │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +├───────────────────┴─────────────┴─────────┴─────────┴─────────┴───────┤ +│ 13 rows 6 columns │ +└───────────────────────────────────────────────────────────────────────┘ +``` +```python +conn.sql("DESCRIBE immunizations;") +``` +```python +┌─────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐ +│ column_name │ column_type │ null │ key │ default │ extra │ +│ varchar │ varchar │ varchar │ varchar │ varchar │ int32 │ +├─────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤ +│ DATE │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ PATIENT │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ ENCOUNTER │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ CODE │ BIGINT │ YES │ NULL │ NULL │ NULL │ +│ DESCRIPTION │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ BASE_COST │ DOUBLE │ YES │ NULL │ NULL │ NULL │ +└─────────────┴─────────────┴─────────┴─────────┴─────────┴───────┘ +``` +___ +___ +### Querying Data +#### **SELECT** - The SELECT statement selects data from a database. + +E.g.: Query the patients table and display patient first name, last name, gender, and the city they live in. + +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients;") +``` +```python +┌─────────────────┬────────────────┬─────────┬─────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ +│ varchar │ varchar │ varchar │ varchar │ +├─────────────────┼────────────────┼─────────┼─────────────┤ +│ Jacinto644 │ Kris249 │ M │ Springfield │ +│ Alva958 │ Krajcik437 │ F │ Walpole │ +│ Jimmie93 │ Harris789 │ F │ Pembroke │ +│ Gregorio366 │ Auer97 │ M │ Boston │ +│ Karyn217 │ Mueller846 │ F │ Colrain │ +│ Jayson808 │ Fadel536 │ M │ Chicopee │ +│ José Eduardo181 │ Gómez206 │ M │ Chicopee │ +│ Milo271 │ Feil794 │ M │ Somerville │ +│ Karyn217 │ Metz686 │ F │ Medfield │ +│ Jeffrey461 │ Greenfelder433 │ M │ Springfield │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Raymond398 │ Kuvalis369 │ M │ Hingham │ +│ Gearldine455 │ Boyer713 │ F │ Boston │ +│ Nichol11 │ Gleichner915 │ F │ Lynn │ +│ Louvenia131 │ Marks830 │ F │ Springfield │ +│ Raymon366 │ Beer512 │ M │ Springfield │ +│ Camelia346 │ Stamm704 │ F │ Dedham │ +│ William805 │ Pacocha935 │ M │ Brockton │ +│ Guillermo498 │ Téllez750 │ M │ Somerville │ +│ Milton509 │ Bailey598 │ M │ Hull │ +│ Cecilia788 │ Wisozk929 │ F │ Worcester │ +├─────────────────┴────────────────┴─────────┴─────────────┤ +│ ? rows (>9999 rows, 20 shown) 4 columns │ +└──────────────────────────────────────────────────────────┘ +``` +### Count the number of rows in patients table +### This query will count the total number of rows in the patients table, effectively giving you the total number of patients. + +```python +conn.sql("SELECT COUNT(*) FROM patients;") +``` +```python +┌──────────────┐ +│ count_star() │ +│ int64 │ +├──────────────┤ +│ 124150 │ +└──────────────┘ +``` +### **COUNT** and **DISTINCT** +If you want to count distinct values of a specific column, for example, distinct cities, you can modify the query as follows: +```python +conn.sql("SELECT COUNT(DISTINCT CITY) FROM patients;") +``` +```python +┌──────────────────────┐ +│ count(DISTINCT CITY) │ +│ int64 │ +├──────────────────────┤ +│ 351 │ +└──────────────────────┘ +``` +### Filtering data based on a condition +The WHERE clause filters records that fulfill a specified condition. +e.g: Patients in City = 'Springfield'; + +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY, PASSPORT FROM patients WHERE city = 'Springfield';") +``` +```python +┌──────────────┬────────────────┬─────────┬─────────────┬────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ PASSPORT │ +│ varchar │ varchar │ varchar │ varchar │ varchar │ +├──────────────┼────────────────┼─────────┼─────────────┼────────────┤ +│ Jacinto644 │ Kris249 │ M │ Springfield │ NULL │ +│ Jeffrey461 │ Greenfelder433 │ M │ Springfield │ NULL │ +│ Sabina296 │ Flatley871 │ F │ Springfield │ X85058581X │ +│ Theodora872 │ Johnson679 │ F │ Springfield │ X21164602X │ +│ Lavera253 │ Anderson154 │ F │ Springfield │ X83686992X │ +│ Golden321 │ Pollich983 │ F │ Springfield │ NULL │ +│ Georgiann138 │ Greenfelder433 │ F │ Springfield │ X58134116X │ +│ Fausto876 │ Bechtelar572 │ M │ Springfield │ NULL │ +│ Talisha682 │ Brakus656 │ F │ Springfield │ X53645004X │ +│ Golden321 │ Durgan499 │ F │ Springfield │ X49016634X │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ Carla633 │ Terán294 │ F │ Springfield │ X50212869X │ +│ Ángela136 │ Muñiz642 │ F │ Springfield │ NULL │ +│ Marceline716 │ Kuhlman484 │ F │ Springfield │ X70432308X │ +│ Ana María762 │ Cotto891 │ F │ Springfield │ X4604857X │ +│ Josefina523 │ Vanegas191 │ F │ Springfield │ X85396222X │ +│ Ester635 │ Sevilla788 │ F │ Springfield │ X50601786X │ +│ Tomás404 │ Galarza986 │ M │ Springfield │ X76790663X │ +│ Arturo47 │ Delafuente833 │ M │ Springfield │ X45087804X │ +│ Gilberto712 │ Martínez540 │ M │ Springfield │ X89729075X │ +│ Juanita470 │ Connelly992 │ F │ Springfield │ X71156217X │ +├──────────────┴────────────────┴─────────┴─────────────┴────────────┤ +│ 2814 rows (20 shown) 5 columns │ +└────────────────────────────────────────────────────────────────────┘ +``` +### Multiple conditions in Selection +e.g 1: Female Patients in City = 'Springfield'; +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE city = 'Springfield' AND gender = 'F';") +``` +```python + +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE city = 'Springfield' AND gender = 'F';") +┌──────────────┬────────────────┬─────────┬─────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ +│ varchar │ varchar │ varchar │ varchar │ +├──────────────┼────────────────┼─────────┼─────────────┤ +│ Sabina296 │ Flatley871 │ F │ Springfield │ +│ Theodora872 │ Johnson679 │ F │ Springfield │ +│ Lavera253 │ Anderson154 │ F │ Springfield │ +│ Golden321 │ Pollich983 │ F │ Springfield │ +│ Georgiann138 │ Greenfelder433 │ F │ Springfield │ +│ Talisha682 │ Brakus656 │ F │ Springfield │ +│ Golden321 │ Durgan499 │ F │ Springfield │ +│ Jerrie417 │ Gislason620 │ F │ Springfield │ +│ Venus149 │ Hodkiewicz467 │ F │ Springfield │ +│ Refugia211 │ Wintheiser220 │ F │ Springfield │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Tisha655 │ Renner328 │ F │ Springfield │ +│ Mona85 │ Senger904 │ F │ Springfield │ +│ Isabel214 │ Camacho176 │ F │ Springfield │ +│ Carla633 │ Terán294 │ F │ Springfield │ +│ Ángela136 │ Muñiz642 │ F │ Springfield │ +│ Marceline716 │ Kuhlman484 │ F │ Springfield │ +│ Ana María762 │ Cotto891 │ F │ Springfield │ +│ Josefina523 │ Vanegas191 │ F │ Springfield │ +│ Ester635 │ Sevilla788 │ F │ Springfield │ +│ Juanita470 │ Connelly992 │ F │ Springfield │ +├──────────────┴────────────────┴─────────┴─────────────┤ +│ 1351 rows (20 shown) 4 columns │ +└───────────────────────────────────────────────────────┘ +``` +### Nested selection +e.g. Female patients from City Springfield or Boston +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE gender = 'F' AND (city = 'Springfield' OR city = 'Boston');") +``` +```python +┌──────────────┬────────────────┬─────────┬─────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ +│ varchar │ varchar │ varchar │ varchar │ +├──────────────┼────────────────┼─────────┼─────────────┤ +│ Sabina296 │ Flatley871 │ F │ Springfield │ +│ Theodora872 │ Johnson679 │ F │ Springfield │ +│ Lavera253 │ Anderson154 │ F │ Springfield │ +│ Golden321 │ Pollich983 │ F │ Springfield │ +│ Georgiann138 │ Greenfelder433 │ F │ Springfield │ +│ Talisha682 │ Brakus656 │ F │ Springfield │ +│ Golden321 │ Durgan499 │ F │ Springfield │ +│ Jerrie417 │ Gislason620 │ F │ Springfield │ +│ Venus149 │ Hodkiewicz467 │ F │ Springfield │ +│ Refugia211 │ Wintheiser220 │ F │ Springfield │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Freida957 │ Hand679 │ F │ Boston │ +│ Clora637 │ Rempel203 │ F │ Boston │ +│ Bonny428 │ Turner526 │ F │ Boston │ +│ Tien590 │ Gaylord332 │ F │ Boston │ +│ Arlyne429 │ Deckow585 │ F │ Boston │ +│ Doreen575 │ Johnson679 │ F │ Boston │ +│ Kym935 │ Hayes766 │ F │ Boston │ +│ Julia241 │ Nevárez403 │ F │ Boston │ +│ Ja391 │ Murray856 │ F │ Boston │ +│ Creola518 │ Spinka232 │ F │ Boston │ +├──────────────┴────────────────┴─────────┴─────────────┤ +│ 7191 rows (20 shown) 4 columns │ +└───────────────────────────────────────────────────────┘ +``` +#### Alternative +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE gender = 'F' AND (city IN ('Springfield','Boston'));") +``` +```python +┌──────────────┬────────────────┬─────────┬─────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ +│ varchar │ varchar │ varchar │ varchar │ +├──────────────┼────────────────┼─────────┼─────────────┤ +│ Sabina296 │ Flatley871 │ F │ Springfield │ +│ Theodora872 │ Johnson679 │ F │ Springfield │ +│ Lavera253 │ Anderson154 │ F │ Springfield │ +│ Golden321 │ Pollich983 │ F │ Springfield │ +│ Georgiann138 │ Greenfelder433 │ F │ Springfield │ +│ Talisha682 │ Brakus656 │ F │ Springfield │ +│ Golden321 │ Durgan499 │ F │ Springfield │ +│ Jerrie417 │ Gislason620 │ F │ Springfield │ +│ Venus149 │ Hodkiewicz467 │ F │ Springfield │ +│ Refugia211 │ Wintheiser220 │ F │ Springfield │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Freida957 │ Hand679 │ F │ Boston │ +│ Clora637 │ Rempel203 │ F │ Boston │ +│ Bonny428 │ Turner526 │ F │ Boston │ +│ Tien590 │ Gaylord332 │ F │ Boston │ +│ Arlyne429 │ Deckow585 │ F │ Boston │ +│ Doreen575 │ Johnson679 │ F │ Boston │ +│ Kym935 │ Hayes766 │ F │ Boston │ +│ Julia241 │ Nevárez403 │ F │ Boston │ +│ Ja391 │ Murray856 │ F │ Boston │ +│ Creola518 │ Spinka232 │ F │ Boston │ +├──────────────┴────────────────┴─────────┴─────────────┤ +│ 7191 rows (20 shown) 4 columns │ +└───────────────────────────────────────────────────────┘ +``` +### Filter on missing data +* Filtering on missing data is crucial for maintaining data integrity and ensuring accurate analysis by identifying and handling incomplete records effectively. +* Retrieve the first name, last name, gender, and city for all patients who are from Springfield and have a passport number recorded (i.e., the passport field is not empty). + +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY, PASSPORT FROM patients WHERE city = 'Springfield' AND PASSPORT IS NOT NULL;") +``` +```python +┌──────────────┬────────────────┬─────────┬─────────────┬────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ PASSPORT │ +│ varchar │ varchar │ varchar │ varchar │ varchar │ +├──────────────┼────────────────┼─────────┼─────────────┼────────────┤ +│ Sabina296 │ Flatley871 │ F │ Springfield │ X85058581X │ +│ Theodora872 │ Johnson679 │ F │ Springfield │ X21164602X │ +│ Lavera253 │ Anderson154 │ F │ Springfield │ X83686992X │ +│ Georgiann138 │ Greenfelder433 │ F │ Springfield │ X58134116X │ +│ Talisha682 │ Brakus656 │ F │ Springfield │ X53645004X │ +│ Golden321 │ Durgan499 │ F │ Springfield │ X49016634X │ +│ Ty725 │ Schmeler639 │ M │ Springfield │ X22960735X │ +│ Pilar644 │ Pouros728 │ F │ Springfield │ X21434326X │ +│ Georgette866 │ Stark857 │ F │ Springfield │ X84034866X │ +│ Annika454 │ Gutmann970 │ F │ Springfield │ X3275916X │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ Isabel214 │ Camacho176 │ F │ Springfield │ X52173808X │ +│ Carla633 │ Terán294 │ F │ Springfield │ X50212869X │ +│ Marceline716 │ Kuhlman484 │ F │ Springfield │ X70432308X │ +│ Ana María762 │ Cotto891 │ F │ Springfield │ X4604857X │ +│ Josefina523 │ Vanegas191 │ F │ Springfield │ X85396222X │ +│ Ester635 │ Sevilla788 │ F │ Springfield │ X50601786X │ +│ Tomás404 │ Galarza986 │ M │ Springfield │ X76790663X │ +│ Arturo47 │ Delafuente833 │ M │ Springfield │ X45087804X │ +│ Gilberto712 │ Martínez540 │ M │ Springfield │ X89729075X │ +│ Juanita470 │ Connelly992 │ F │ Springfield │ X71156217X │ +├──────────────┴────────────────┴─────────┴─────────────┴────────────┤ +│ 2221 rows (20 shown) 5 columns │ +└────────────────────────────────────────────────────────────────────┘ +``` +### Filter and select in numeric range +Patients in City = 'Springfield' where the HEALTHCARE_EXPENSES between 1.5M and 2M + +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE city = 'Springfield' AND HEALTHCARE_EXPENSES BETWEEN 1500000 AND 2000000;") +``` +```python +┌──────────────┬────────────────┬─────────┬─────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ +│ varchar │ varchar │ varchar │ varchar │ +├──────────────┼────────────────┼─────────┼─────────────┤ +│ Talisha682 │ Brakus656 │ F │ Springfield │ +│ Jerold208 │ Harber290 │ M │ Springfield │ +│ Dean966 │ Tillman293 │ M │ Springfield │ +│ Orval846 │ Cartwright189 │ M │ Springfield │ +│ Jacinto644 │ Abernathy524 │ M │ Springfield │ +│ Bethel526 │ Satterfield305 │ F │ Springfield │ +│ Dorene845 │ Botsford977 │ F │ Springfield │ +│ Lula998 │ Langosh790 │ F │ Springfield │ +│ Deeanna316 │ Koss676 │ F │ Springfield │ +│ Loriann967 │ Torp761 │ F │ Springfield │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Ashley34 │ Murazik203 │ M │ Springfield │ +│ Ulysses632 │ Donnelly343 │ M │ Springfield │ +│ Horace32 │ Hammes673 │ M │ Springfield │ +│ Roman389 │ Lubowitz58 │ M │ Springfield │ +│ Jerald662 │ Grady603 │ M │ Springfield │ +│ Damien170 │ Hoppe518 │ M │ Springfield │ +│ Charley358 │ Vandervort697 │ M │ Springfield │ +│ Carla633 │ Terán294 │ F │ Springfield │ +│ Ana María762 │ Cotto891 │ F │ Springfield │ +│ Juanita470 │ Connelly992 │ F │ Springfield │ +├──────────────┴────────────────┴─────────┴─────────────┤ +│ 241 rows (20 shown) 4 columns │ +└───────────────────────────────────────────────────────┘ +``` +### LIMIT +- LIMIT specifies the maximum number of records the query will return. + +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients LIMIT 20;") +``` +```python + +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients LIMIT 20;") +┌─────────────────┬────────────────┬─────────┬─────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ +│ varchar │ varchar │ varchar │ varchar │ +├─────────────────┼────────────────┼─────────┼─────────────┤ +│ Jacinto644 │ Kris249 │ M │ Springfield │ +│ Alva958 │ Krajcik437 │ F │ Walpole │ +│ Jimmie93 │ Harris789 │ F │ Pembroke │ +│ Gregorio366 │ Auer97 │ M │ Boston │ +│ Karyn217 │ Mueller846 │ F │ Colrain │ +│ Jayson808 │ Fadel536 │ M │ Chicopee │ +│ José Eduardo181 │ Gómez206 │ M │ Chicopee │ +│ Milo271 │ Feil794 │ M │ Somerville │ +│ Karyn217 │ Metz686 │ F │ Medfield │ +│ Jeffrey461 │ Greenfelder433 │ M │ Springfield │ +│ Mariana775 │ Gulgowski816 │ F │ Lowell │ +│ Leann224 │ Deckow585 │ F │ Needham │ +│ Isabel214 │ Hinojosa147 │ F │ Fall River │ +│ Christal240 │ Brown30 │ F │ Boston │ +│ Carmelia328 │ Konopelski743 │ F │ Ashland │ +│ Raye931 │ Wyman904 │ F │ Quincy │ +│ Lisbeth69 │ Rowe323 │ F │ Malden │ +│ Amada498 │ Spinka232 │ F │ Foxborough │ +│ Cythia210 │ Reichel38 │ F │ Peabody │ +│ María Soledad68 │ Aparicio848 │ F │ Boston │ +├─────────────────┴────────────────┴─────────┴─────────────┤ +│ 20 rows 4 columns │ +└──────────────────────────────────────────────────────────┘ +``` +### ORDER BY and ASC +This query returns the first 20 patients from the patients table, ordered alphabetically by their last name. +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients ORDER BY LAST ASC LIMIT 20;") +``` +```python +┌────────────────┬───────────┬─────────┬──────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ +│ varchar │ varchar │ varchar │ varchar │ +├────────────────┼───────────┼─────────┼──────────────┤ +│ Jc393 │ Abbott774 │ M │ Westfield │ +│ Lloyd546 │ Abbott774 │ M │ Northampton │ +│ Charles364 │ Abbott774 │ M │ Brockton │ +│ Lelia627 │ Abbott774 │ F │ Gloucester │ +│ Devorah937 │ Abbott774 │ F │ Cambridge │ +│ Warren653 │ Abbott774 │ M │ Tyngsborough │ +│ Rhona164 │ Abbott774 │ F │ Stoneham │ +│ Jackqueline794 │ Abbott774 │ F │ Randolph │ +│ Willa615 │ Abbott774 │ F │ Dartmouth │ +│ Cyril535 │ Abbott774 │ M │ Millis │ +│ Lorette239 │ Abbott774 │ F │ Dennis │ +│ Jimmy858 │ Abbott774 │ M │ Lowell │ +│ Laine739 │ Abbott774 │ F │ Agawam │ +│ Bernetta267 │ Abbott774 │ F │ Ware │ +│ Lauri399 │ Abbott774 │ F │ Springfield │ +│ Miesha237 │ Abbott774 │ F │ Stoneham │ +│ Darrin898 │ Abbott774 │ M │ Newton │ +│ Grant908 │ Abbott774 │ M │ Arlington │ +│ Arden380 │ Abbott774 │ M │ Worcester │ +│ German382 │ Abbott774 │ M │ Taunton │ +├────────────────┴───────────┴─────────┴──────────────┤ +│ 20 rows 4 columns │ +└─────────────────────────────────────────────────────┘ +``` +### ORDER BY and DESC +This query returns the first 20 patients from the patients table, ordered reverse alphabetically by their first name. +```python +conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients ORDER BY FIRST DESC LIMIT 20;") +``` +```python +┌──────────┬────────────────┬─────────┬────────────┐ +│ FIRST │ LAST │ GENDER │ CITY │ +│ varchar │ varchar │ varchar │ varchar │ +├──────────┼────────────────┼─────────┼────────────┤ +│ Óscar156 │ Ballesteros368 │ M │ Taunton │ +│ Óscar156 │ Curiel392 │ M │ Swampscott │ +│ Óscar156 │ Zelaya592 │ M │ Lynn │ +│ Óscar156 │ Puente961 │ M │ Greenfield │ +│ Óscar156 │ Olivas524 │ M │ Norwell │ +│ Óscar156 │ Rivero165 │ M │ Lancaster │ +│ Óscar156 │ Canales95 │ M │ Hamilton │ +│ Óscar156 │ Romero158 │ M │ Boston │ +│ Óscar156 │ Garza151 │ M │ Longmeadow │ +│ Óscar156 │ Delgado712 │ M │ Haverhill │ +│ Óscar156 │ Ureña88 │ M │ Bedford │ +│ Óscar156 │ Henríquez109 │ M │ Boxborough │ +│ Óscar156 │ Guerrero997 │ M │ Boston │ +│ Óscar156 │ Ojeda263 │ M │ Boston │ +│ Óscar156 │ Meléndez48 │ M │ Newton │ +│ Óscar156 │ Muro989 │ M │ Boston │ +│ Óscar156 │ Rendón540 │ M │ Wilbraham │ +│ Óscar156 │ Santacruz647 │ M │ Winthrop │ +│ Óscar156 │ Santacruz647 │ M │ Boston │ +│ Óscar156 │ Otero621 │ M │ Boston │ +├──────────┴────────────────┴─────────┴────────────┤ +│ 20 rows 4 columns │ +└──────────────────────────────────────────────────┘ +``` +## Aggregating Data using GROUP BY +#### Counting Patients by City + +```python +conn.sql("SELECT CITY, COUNT(*) AS patient_count FROM patients GROUP BY CITY;") ## each run gives random order +``` +```python +┌─────────────┬───────────────┐ +│ CITY │ patient_count │ +│ varchar │ int64 │ +├─────────────┼───────────────┤ +│ Dartmouth │ 656 │ +│ Fitchburg │ 743 │ +│ Plymouth │ 972 │ +│ Worcester │ 3263 │ +│ Beverly │ 778 │ +│ Westfield │ 781 │ +│ Hingham │ 431 │ +│ Rochester │ 103 │ +│ Sandwich │ 387 │ +│ Watertown │ 534 │ +│ · │ · │ +│ · │ · │ +│ · │ · │ +│ Ashfield │ 29 │ +│ Pelham │ 26 │ +│ Hancock │ 22 │ +│ Monterey │ 13 │ +│ Richmond │ 23 │ +│ Hinsdale │ 30 │ +│ Middlefield │ 11 │ +│ Westhampton │ 14 │ +│ Sandisfield │ 12 │ +│ Monroe │ 2 │ +├─────────────┴───────────────┤ +│ 351 rows (20 shown) │ +└─────────────────────────────┘ +``` +### Counting Patients by City and sorting them highest to lowest +```python +conn.sql("SELECT CITY, COUNT(*) AS patient_count FROM patients GROUP BY CITY ORDER BY patient_count DESC;") +``` +```python +┌─────────────┬───────────────┐ +│ CITY │ patient_count │ +│ varchar │ int64 │ +├─────────────┼───────────────┤ +│ Boston │ 11496 │ +│ Worcester │ 3263 │ +│ Springfield │ 2814 │ +│ Cambridge │ 2044 │ +│ Lowell │ 2027 │ +│ Brockton │ 1833 │ +│ Lynn │ 1714 │ +│ New Bedford │ 1706 │ +│ Quincy │ 1698 │ +│ Newton │ 1695 │ +│ · │ · │ +│ · │ · │ +│ · │ · │ +│ Blandford │ 10 │ +│ Heath │ 8 │ +│ Tolland │ 7 │ +│ Hawley │ 6 │ +│ Alford │ 5 │ +│ New Ashford │ 4 │ +│ Aquinnah │ 4 │ +│ Tyringham │ 3 │ +│ Monroe │ 2 │ +│ Gosnold │ 1 │ +├─────────────┴───────────────┤ +│ 351 rows (20 shown) │ +└─────────────────────────────┘ +``` +### Total Number of Patients by Gender in Each City +```python +conn.sql("SELECT CITY, GENDER, COUNT(*) AS total_patients FROM patients GROUP BY CITY, GENDER ORDER BY total_patients DESC LIMIT 10;") +``` +```python +┌─────────────┬─────────┬────────────────┐ +│ CITY │ GENDER │ total_patients │ +│ varchar │ varchar │ int64 │ +├─────────────┼─────────┼────────────────┤ +│ Boston │ F │ 5840 │ +│ Boston │ M │ 5656 │ +│ Worcester │ M │ 1694 │ +│ Worcester │ F │ 1569 │ +│ Springfield │ M │ 1463 │ +│ Springfield │ F │ 1351 │ +│ Cambridge │ M │ 1023 │ +│ Cambridge │ F │ 1021 │ +│ Lowell │ F │ 1015 │ +│ Lowell │ M │ 1012 │ +├─────────────┴─────────┴────────────────┤ +│ 10 rows 3 columns │ +└────────────────────────────────────────┘ +``` +Lets look at other Tables - observations and immunizations +```python +conn.sql("SELECT PATIENT,ENCOUNTER,CODE, DESCRIPTION, VALUE from observations;") +``` +```python +┌──────────────────────┬──────────────────────┬─────────┬───────────────────────────────────────────────┬──────────────┐ +│ PATIENT │ ENCOUNTER │ CODE │ DESCRIPTION │ VALUE │ +│ varchar │ varchar │ varchar │ varchar │ varchar │ +├──────────────────────┼──────────────────────┼─────────┼───────────────────────────────────────────────┼──────────────┤ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 8302-2 │ Body Height │ 82.7 │ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 72514-3 │ Pain severity - 0-10 verbal numeric rating … │ 2.0 │ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 29463-7 │ Body Weight │ 11.5 │ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 77606-2 │ Weight-for-length Per age and sex │ 47.0 │ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 9843-4 │ Head Occipital-frontal circumference │ 46.9 │ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 8462-4 │ Diastolic Blood Pressure │ 76.0 │ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 8480-6 │ Systolic Blood Pressure │ 107.0 │ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 8867-4 │ Heart rate │ 68.0 │ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 9279-1 │ Respiratory rate │ 13.0 │ +│ 1ff7f10f-a204-4bb1… │ 52051c30-c6c3-45fe… │ 72166-2 │ Tobacco smoking status NHIS │ Never smoker │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 2160-0 │ Creatinine [Mass/volume] in Serum or Plasma │ 2.9 │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 17861-6 │ Calcium [Mass/volume] in Serum or Plasma │ 9.2 │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 2951-2 │ Sodium [Moles/volume] in Serum or Plasma │ 143.8 │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 2823-3 │ Potassium [Moles/volume] in Serum or Plasma │ 4.3 │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 2075-0 │ Chloride [Moles/volume] in Serum or Plasma │ 106.4 │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 2028-9 │ Carbon dioxide total [Moles/volume] in Ser… │ 22.9 │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 33914-3 │ Glomerular filtration rate/1.73 sq M.predic… │ 9.9 │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 2885-2 │ Protein [Mass/volume] in Serum or Plasma │ 5.7 │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 1751-7 │ Albumin [Mass/volume] in Serum or Plasma │ 5.2 │ +│ 87537cb1-92e1-4a11… │ 69e8eaf0-7714-4c20… │ 1975-2 │ Bilirubin.total [Mass/volume] in Serum or P… │ 14.2 │ +├──────────────────────┴──────────────────────┴─────────┴───────────────────────────────────────────────┴──────────────┤ +│ ? rows (>9999 rows, 20 shown) 5 columns │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +``` +#### Average Heart Rate by Patient + +```python +conn.sql("SELECT PATIENT, ROUND(AVG(CAST(VALUE AS DECIMAL(10,6))), 2) AS average_heart_rate FROM observations WHERE DESCRIPTION LIKE '%Heart rate%' GROUP BY PATIENT ORDER BY average_heart_rate DESC LIMIT 20;") +``` +```python +┌──────────────────────────────────────┬────────────────────┐ +│ PATIENT │ average_heart_rate │ +│ varchar │ double │ +├──────────────────────────────────────┼────────────────────┤ +│ ff2209f2-a4c9-4737-9aa7-adc8d71b6961 │ 200.0 │ +│ 8cea2d7e-6227-4924-a340-18eafb8564ac │ 200.0 │ +│ 6ec823e7-839e-4491-8b6f-6b275951b456 │ 200.0 │ +│ 0191f8aa-96dc-41e6-b718-312686a7a867 │ 200.0 │ +│ c10db4e1-de51-495f-98ab-8322c6d9550a │ 200.0 │ +│ 46ea7226-63f6-404f-bbed-904ea39f706d │ 199.9 │ +│ ec1ce32c-57a3-44c4-8f64-e050f2f94e03 │ 199.9 │ +│ a05e3bd1-1c51-453a-9969-646cb0168d23 │ 199.9 │ +│ 9cc52681-4446-4e4a-80be-63418dd06e66 │ 199.9 │ +│ b0112890-a24a-4564-b2b3-9b77879e5e79 │ 199.9 │ +│ 18f53179-2449-47af-b4da-a9e51095076f │ 199.9 │ +│ c3d165d2-597e-45a8-8331-f3338ce19fdf │ 199.9 │ +│ 04dd3439-2116-4f9a-b40e-a84395cf6f21 │ 199.9 │ +│ 371e14a1-31bd-4407-bdbb-4078781fde05 │ 199.8 │ +│ 3e5c63bf-65fe-48bb-8361-6b21a909888a │ 199.8 │ +│ 423f79cf-f7b8-41cd-bb3c-8459084f88eb │ 199.8 │ +│ 2d4f57ee-4001-44f8-9a94-c1885f26b408 │ 199.8 │ +│ bd6784c0-3529-4a12-87bd-746affb17739 │ 199.8 │ +│ ef43848b-a4f1-4c45-a106-5928e29b72f2 │ 199.7 │ +│ 1d050245-097c-44a5-b29b-6a18a22731e9 │ 199.7 │ +├──────────────────────────────────────┴────────────────────┤ +│ 20 rows 2 columns │ +└───────────────────────────────────────────────────────────┘ +``` +#### Count of Heart Rate Records by Patient +```python +conn.sql("SELECT PATIENT, COUNT(*) AS heart_rate_records FROM observations WHERE DESCRIPTION LIKE '%Heart rate%' GROUP BY PATIENT ORDER BY heart_rate_records DESC LIMIT 20;") +``` +```python +┌──────────────────────────────────────┬────────────────────┐ +│ PATIENT │ heart_rate_records │ +│ varchar │ int64 │ +├──────────────────────────────────────┼────────────────────┤ +│ 3e21a156-da54-4fb7-815e-550fdf4afbbd │ 33 │ +│ ffbf0392-1643-4b05-819f-489072c8c4d4 │ 30 │ +│ b9ef6005-438e-4b47-afaf-9dba32184adf │ 30 │ +│ f8080701-2b5c-4128-9a7f-9a098b737b27 │ 30 │ +│ a2172279-3d63-4d14-867d-1a39d9280690 │ 30 │ +│ 01a5a5a6-ef7b-42ba-899c-de66e1b1e27e │ 30 │ +│ 62572c44-a802-40d1-8d60-d02bc287d548 │ 30 │ +│ 445953fd-15fa-424e-9926-f93ebf1bca7a │ 30 │ +│ c3ea3c46-f8d1-4abe-8fb2-8fc8f86b4ef4 │ 29 │ +│ bd18ba0d-2e65-4427-98f1-cbfc09dbaa33 │ 29 │ +│ 5736b489-0e15-4693-b9d6-05192a702a2d │ 29 │ +│ b5a0b060-96ae-44d5-bd10-f532a445009b │ 29 │ +│ ee9b44a1-36c6-456e-b77c-cd200611ca0f │ 29 │ +│ 0d5290dd-a3e3-4a81-91d5-6e722e58ec3e │ 29 │ +│ 54ec8b8e-fe2a-4cf5-a087-1a65df1ab1b8 │ 29 │ +│ e658626d-0f72-4570-8071-2dd63827a24c │ 29 │ +│ ec3f3da2-09a1-43c7-8cae-ed34847d2534 │ 28 │ +│ c7bd54b1-e036-4702-9062-acfa3677a6e8 │ 28 │ +│ 8b6ee161-eb5f-413a-8e86-f5b3adb3f5ff │ 28 │ +│ 03c1210e-d145-4aed-9522-14cff41b3c28 │ 28 │ +├──────────────────────────────────────┴────────────────────┤ +│ 20 rows 2 columns │ +└───────────────────────────────────────────────────────────┘ +``` +#### Total Cost of Vaccinations per Patient +```python +conn.sql("SELECT PATIENT, SUM(BASE_COST) AS total_cost FROM immunizations GROUP BY PATIENT ORDER BY total_cost DESC;") +``` +```python +┌──────────────────────────────────────┬────────────┐ +│ PATIENT │ total_cost │ +│ varchar │ double │ +├──────────────────────────────────────┼────────────┤ +│ 8eacdc17-de2f-4024-88bd-976401b30979 │ 2810.4 │ +│ bb14de6a-c77d-44bb-a3a8-338a6a246520 │ 2810.4 │ +│ a5b60d43-9776-4811-badf-67d49edbc175 │ 2810.4 │ +│ 1c8a4026-5dbe-478a-b647-ecb605775977 │ 2810.4 │ +│ 3e38e20b-6dd8-44fe-9ed3-81e1e3d8e2ab │ 2810.4 │ +│ e23c7c68-0319-41f5-90d9-1db160321a94 │ 2810.4 │ +│ 49e7d878-93a6-4e71-971b-213abacf2235 │ 2810.4 │ +│ 7ded4116-1783-4a93-9bb2-7a65370f55f5 │ 2810.4 │ +│ 594660e4-6a02-4e3c-a8b9-2c61d4d4af60 │ 2810.4 │ +│ fee1edbd-277d-4b12-901e-60b6135cf877 │ 2810.4 │ +│ · │ · │ +│ · │ · │ +│ · │ · │ +│ 594d1ecb-b1a4-4bad-9ccd-5cebceebd6f5 │ 281.04 │ +│ 456fa9f9-7eb7-4316-a74b-5a9ff87f8fc5 │ 281.04 │ +│ 52568520-7163-429d-bc90-fc245debb697 │ 281.04 │ +│ 30a16a19-ee93-4bfd-b1ed-bba90de36266 │ 281.04 │ +│ 0ef633b0-2996-44c5-936c-600820310223 │ 281.04 │ +│ 613319c8-2b76-494b-86b8-e776a2ee5e0b │ 281.04 │ +│ 4dceeb57-4fe3-4c32-a748-621df5ce3c30 │ 281.04 │ +│ 93d590e8-9f3e-4741-bee2-9b8e9e8b21e7 │ 281.04 │ +│ bad20ffc-48f9-4a7d-a2be-39a50fa4e0ac │ 281.04 │ +│ 3cf75ec3-5e1c-4783-bb20-282a8cf66f30 │ 281.04 │ +├──────────────────────────────────────┴────────────┤ +│ ? rows (>9999 rows, 20 shown) 2 columns │ +└───────────────────────────────────────────────────┘ +``` +#### Top 10 Most Common Vaccine Administered +```python +conn.sql("SELECT DESCRIPTION, COUNT(*) AS count FROM immunizations GROUP BY DESCRIPTION ORDER BY count DESC LIMIT 10;") +``` +```python +┌────────────────────────────────────────────────────┬────────┐ +│ DESCRIPTION │ count │ +│ varchar │ int64 │ +├────────────────────────────────────────────────────┼────────┤ +│ Influenza seasonal injectable preservative free │ 106564 │ +│ Td (adult) preservative free │ 9815 │ +│ Pneumococcal conjugate PCV 13 │ 5747 │ +│ DTaP │ 5735 │ +│ IPV │ 4962 │ +│ meningococcal MCV4P │ 4010 │ +│ Hib (PRP-OMP) │ 3615 │ +│ HPV quadrivalent │ 3494 │ +│ Hep B adolescent or pediatric │ 3490 │ +│ zoster │ 3469 │ +├────────────────────────────────────────────────────┴────────┤ +│ 10 rows 2 columns │ +└─────────────────────────────────────────────────────────────┘ +``` +## JOINS +#### SQL joins are used to combine rows from two or more tables, based on a related column between them. There are several types of joins in SQL, each serving different purposes depending on how you want to combine your data. + +![SQL JOINS](https://cdn.educba.com/academy/wp-content/uploads/2019/10/Types-of-Join-inSQL.jpg.webp) + - credit: educba.com + +___ + +For the following examples: Left table: Patients and Right Table: Immunizations + +__ + +### INNER JOIN +- The Inner join returns rows when there is at least one match in both tables. If there is no match, the rows are not returned. + +- Find Matching Records: Question: Which medical treatments have been administered to patients, including the patient's name and the cost of each treatment? +```python +conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM immunizations INNER JOIN patients ON immunizations.PATIENT = patients.Id ORDER BY patients.FIRST ASC;") +``` +```python +┌──────────┬───────────────┬────────────────────────────────────────────────────┬───────────┐ +│ FIRST │ LAST │ DESCRIPTION │ BASE_COST │ +│ varchar │ varchar │ varchar │ double │ +├──────────┼───────────────┼────────────────────────────────────────────────────┼───────────┤ +│ Aaron697 │ Cummings51 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Crooks415 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Anderson154 │ Pneumococcal conjugate PCV 13 │ 140.52 │ +│ Aaron697 │ Hartmann983 │ Hep B adolescent or pediatric │ 140.52 │ +│ Aaron697 │ Thompson596 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Rodriguez71 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Deckow585 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Swaniawski813 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Beer512 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Ullrich385 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Ariel183 │ Schuster709 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Ariel183 │ Mohr916 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Ariel183 │ Waters156 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Ariel183 │ Will178 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Ariel183 │ Leffler128 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Ariel183 │ Bode78 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Ariel183 │ Grady603 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Ariel183 │ Funk324 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Ariel183 │ McGlynn426 │ Td (adult) preservative free │ 140.52 │ +│ Ariel183 │ Boehm581 │ Influenza seasonal injectable preservative free │ 140.52 │ +├──────────┴───────────────┴────────────────────────────────────────────────────┴───────────┤ +│ ? rows (>9999 rows, 20 shown) 4 columns │ +└───────────────────────────────────────────────────────────────────────────────────────────┘ +``` + +### LEFT JOIN +- It returns all rows from the left table, and the matched rows from the right table. The result is NULL from the right side, if there is no match. +Question: For all patients, what treatments have they received, if any? +```python +conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM patients LEFT JOIN immunizations ON patients.Id = immunizations.PATIENT ORDER BY patients.FIRST ASC;") +``` +```python + +conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM patients LEFT JOIN immunizations ON patients.Id = immunizations.PATIENT ORDER BY patients.FIRST ASC;") +┌───────────┬─────────────┬────────────────────────────────────────────────────┬───────────┐ +│ FIRST │ LAST │ DESCRIPTION │ BASE_COST │ +│ varchar │ varchar │ varchar │ double │ +├───────────┼─────────────┼────────────────────────────────────────────────────┼───────────┤ +│ Aaron697 │ Legros616 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Deckow585 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Thompson596 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Auer97 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Crooks415 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Crooks415 │ Td (adult) preservative free │ 140.52 │ +│ Aaron697 │ Volkman526 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Hartmann983 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Orn563 │ NULL │ NULL │ +│ Aaron697 │ Hartmann983 │ IPV │ 140.52 │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Antonia30 │ Goodwin327 │ HPV quadrivalent │ 140.52 │ +│ Antonia30 │ Funk324 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Antonia30 │ West559 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Antonia30 │ Lowe577 │ NULL │ NULL │ +│ Antonia30 │ D'Amore443 │ Pneumococcal conjugate PCV 13 │ 140.52 │ +│ Antonia30 │ Montes106 │ meningococcal MCV4P │ 140.52 │ +│ Antonia30 │ González124 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Antonia30 │ Franecki195 │ zoster │ 140.52 │ +│ Antonia30 │ Rojo930 │ Pneumococcal conjugate PCV 13 │ 140.52 │ +│ Antonia30 │ Puente961 │ HPV quadrivalent │ 140.52 │ +├───────────┴─────────────┴────────────────────────────────────────────────────┴───────────┤ +│ ? rows (>9999 rows, 20 shown) 4 columns │ +└──────────────────────────────────────────────────────────────────────────────────────────┘ +``` +### RIGHT JOIN +- It returns all rows from the right table, and the matched rows from the left table. The result is NULL from the left side, if there is no match. +Question: For all Immunizations recorded, which patients received them, if identifiable? + +```python +conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM patients RIGHT JOIN immunizations ON patients.Id = immunizations.PATIENT ORDER BY immunizations.DESCRIPTION;") +``` +```python +┌────────────────┬──────────────┬──────────────┬───────────┐ +│ FIRST │ LAST │ DESCRIPTION │ BASE_COST │ +│ varchar │ varchar │ varchar │ double │ +├────────────────┼──────────────┼──────────────┼───────────┤ +│ Shantay950 │ Quitzon246 │ DTaP │ 140.52 │ +│ Cleveland582 │ VonRueden376 │ DTaP │ 140.52 │ +│ Flo729 │ Quigley282 │ DTaP │ 140.52 │ +│ Flo729 │ Quigley282 │ DTaP │ 140.52 │ +│ Werner409 │ Schaden604 │ DTaP │ 140.52 │ +│ Pasty639 │ Ortiz186 │ DTaP │ 140.52 │ +│ Pasty639 │ Ortiz186 │ DTaP │ 140.52 │ +│ Angelica442 │ Kovacek682 │ DTaP │ 140.52 │ +│ Cathie710 │ Hegmann834 │ DTaP │ 140.52 │ +│ Pasty639 │ Ortiz186 │ DTaP │ 140.52 │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Art115 │ Roberts511 │ Hep A adult │ 140.52 │ +│ Christopher407 │ Davis923 │ Hep A adult │ 140.52 │ +│ Michiko564 │ Dooley940 │ Hep A adult │ 140.52 │ +│ Celine582 │ Sipes176 │ Hep A adult │ 140.52 │ +│ Darci883 │ Miller503 │ Hep A adult │ 140.52 │ +│ Ethan766 │ Morar593 │ Hep A adult │ 140.52 │ +│ Georgiann138 │ Heathcote539 │ Hep A adult │ 140.52 │ +│ Herbert830 │ Wolff180 │ Hep A adult │ 140.52 │ +│ Ismael683 │ King743 │ Hep A adult │ 140.52 │ +│ Ellsworth48 │ Mertz280 │ Hep A adult │ 140.52 │ +├────────────────┴──────────────┴──────────────┴───────────┤ +│ ? rows (>9999 rows, 20 shown) 4 columns │ +└──────────────────────────────────────────────────────────┘ +``` +### FULL OUTER JOIN +- It returns rows when there is a match in one of the tables. It effectively combines the results of both LEFT JOIN and RIGHT JOIN. +Question: What is the complete list of patients and their Immunizations, including those without recorded treatments or identifiable patients? + +```python +conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM patients FULL OUTER JOIN immunizations ON patients.Id = immunizations.PATIENT ORDER BY patients.FIRST ASC;") +``` +```python +┌───────────┬─────────────┬────────────────────────────────────────────────────┬───────────┐ +│ FIRST │ LAST │ DESCRIPTION │ BASE_COST │ +│ varchar │ varchar │ varchar │ double │ +├───────────┼─────────────┼────────────────────────────────────────────────────┼───────────┤ +│ Aaron697 │ Legros616 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Deckow585 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Thompson596 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Auer97 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Crooks415 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Crooks415 │ Td (adult) preservative free │ 140.52 │ +│ Aaron697 │ Volkman526 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Hartmann983 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Aaron697 │ Orn563 │ NULL │ NULL │ +│ Aaron697 │ Hartmann983 │ IPV │ 140.52 │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Antonia30 │ Goodwin327 │ HPV quadrivalent │ 140.52 │ +│ Antonia30 │ Funk324 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Antonia30 │ West559 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Antonia30 │ Lowe577 │ NULL │ NULL │ +│ Antonia30 │ D'Amore443 │ Pneumococcal conjugate PCV 13 │ 140.52 │ +│ Antonia30 │ Montes106 │ meningococcal MCV4P │ 140.52 │ +│ Antonia30 │ González124 │ Influenza seasonal injectable preservative free │ 140.52 │ +│ Antonia30 │ Franecki195 │ zoster │ 140.52 │ +│ Antonia30 │ Rojo930 │ Pneumococcal conjugate PCV 13 │ 140.52 │ +│ Antonia30 │ Puente961 │ HPV quadrivalent │ 140.52 │ +├───────────┴─────────────┴────────────────────────────────────────────────────┴───────────┤ +│ ? rows (>9999 rows, 20 shown) 4 columns │ +└──────────────────────────────────────────────────────────────────────────────────────────┘ +``` +### CROSS JOIN +- It returns a Cartesian product of the two tables, i.e., it returns rows combining each row from the first table with each row from the second table. +Question: What are all possible combinations of patients and immunizations? +```python +conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION FROM patients CROSS JOIN immunizations;") +``` +```python +┌───────────┬─────────────┬────────────────────────────────────────────────────┐ +│ FIRST │ LAST │ DESCRIPTION │ +│ varchar │ varchar │ varchar │ +├───────────┼─────────────┼────────────────────────────────────────────────────┤ +│ Tammy740 │ Ernser583 │ Influenza seasonal injectable preservative free │ +│ Tammy740 │ Ernser583 │ Hep A ped/adol 2 dose │ +│ Tammy740 │ Ernser583 │ Influenza seasonal injectable preservative free │ +│ Tammy740 │ Ernser583 │ Influenza seasonal injectable preservative free │ +│ Tammy740 │ Ernser583 │ meningococcal MCV4P │ +│ Tammy740 │ Ernser583 │ Hep B adolescent or pediatric │ +│ Tammy740 │ Ernser583 │ Hep B adolescent or pediatric │ +│ Tammy740 │ Ernser583 │ Hib (PRP-OMP) │ +│ Tammy740 │ Ernser583 │ rotavirus monovalent │ +│ Tammy740 │ Ernser583 │ IPV │ +│ · │ · │ · │ +│ · │ · │ · │ +│ · │ · │ · │ +│ Iliana226 │ Schmeler639 │ Influenza seasonal injectable preservative free │ +│ Iliana226 │ Schmeler639 │ zoster │ +│ Iliana226 │ Schmeler639 │ Influenza seasonal injectable preservative free │ +│ Iliana226 │ Schmeler639 │ Influenza seasonal injectable preservative free │ +│ Iliana226 │ Schmeler639 │ Influenza seasonal injectable preservative free │ +│ Iliana226 │ Schmeler639 │ Influenza seasonal injectable preservative free │ +│ Iliana226 │ Schmeler639 │ Influenza seasonal injectable preservative free │ +│ Iliana226 │ Schmeler639 │ Influenza seasonal injectable preservative free │ +│ Iliana226 │ Schmeler639 │ Influenza seasonal injectable preservative free │ +│ Iliana226 │ Schmeler639 │ Influenza seasonal injectable preservative free │ +├───────────┴─────────────┴────────────────────────────────────────────────────┤ +│ ? rows (>9999 rows, 20 shown) 3 columns │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` +### SELF JOIN +- It is not a different type of join, but it's a regular join used to join a table to itself. It's useful for queries where you need to compare rows within the same table. +Question: Which pair of patients are from the same city? +```python +conn.sql("SELECT A.FIRST AS FirstPatient, B.FIRST AS SecondPatient, A.CITY FROM patients A, patients B WHERE A.CITY = B.CITY AND A.Id != B.Id ORDER BY A.CITY ASC;") +``` +```python +┌──────────────┬───────────────┬──────────┐ +│ FirstPatient │ SecondPatient │ CITY │ +│ varchar │ varchar │ varchar │ +├──────────────┼───────────────┼──────────┤ +│ Susan422 │ Stefani254 │ Abington │ +│ Jennell254 │ Lionel365 │ Abington │ +│ Robbie31 │ Antonio44 │ Abington │ +│ Joe656 │ Antonio44 │ Abington │ +│ Susan422 │ Stevie682 │ Abington │ +│ Shaun461 │ Stevie682 │ Abington │ +│ Shaun461 │ Weston546 │ Abington │ +│ Susan422 │ Elwood28 │ Abington │ +│ Susan422 │ Daria61 │ Abington │ +│ Shaun461 │ Lita714 │ Abington │ +│ · │ · │ · │ +│ · │ · │ · │ +│ · │ · │ · │ +│ Elane105 │ Conrad619 │ Abington │ +│ Logan497 │ Conrad619 │ Abington │ +│ Sherlene302 │ Conrad619 │ Abington │ +│ Milan77 │ Conrad619 │ Abington │ +│ Frankie174 │ Seema671 │ Abington │ +│ Elane105 │ Josefina523 │ Abington │ +│ Lisha487 │ Shawanna357 │ Abington │ +│ Marleen824 │ Marcelino726 │ Abington │ +│ Sandi885 │ Lionel365 │ Abington │ +│ Morton637 │ Viva686 │ Abington │ +├──────────────┴───────────────┴──────────┤ +│ ? rows (>9999 rows, 20 shown) │ +└─────────────────────────────────────────┘ +``` +___ +___ + +## Introduction to Subqueries and Nested Selects +- Subqueries, also known as inner queries or nested queries, are SQL queries nested inside a larger query. They allow you to perform operations that usually require multiple steps in a single query, making your data retrieval process more efficient and concise. Nested selects are a type of subquery used specifically within the SELECT, FROM, or WHERE clauses to provide a dataset for the outer query to process. +- Subqueries can return a single value, a single row, multiple rows, or a table. They are used for comparison, as a condition, or to provide a list of values for the outer query. The main distinction between correlated and non-correlated subqueries is that correlated subqueries reference column(s) from the outer query, thus running once for each row selected by the outer query, while non-correlated subqueries run independently of the outer query and can be run as standalone queries. + +### Types of subqueries +* Single-Row Subqueries: Single-row subqueries return only one row and are used with single row comparison operators like =, >, <, >=, <=. They are often used in the WHERE clause to compare a column value against the result of the subquery. +* Multi-Row Subqueries: Multi-row subqueries return more than one row and are used with operators like IN, ANY, ALL, which allow comparison against multiple values. They're useful for filtering based on a set of criteria returned by the subquery. +* Correlated Subqueries: Correlated subqueries reference column(s) from the outer query, making them dependent on the outer query. They are executed once for each row processed by the outer query, often leading to performance considerations. + +___ +#### Use subqueries to find patients based on specific criteria. + +#### Find all patients who have been prescribed medication with a base cost higher than the average base cost of all medications. +```python +query = """SELECT p.FIRST, p.LAST +FROM patients p +WHERE p.Id IN ( SELECT m.PATIENT FROM medications m WHERE m.BASE_COST > (SELECT AVG(BASE_COST) FROM medications)); +""" +conn.sql(query) +``` +```python +┌─────────────┬─────────────┐ +│ FIRST │ LAST │ +│ varchar │ varchar │ +├─────────────┼─────────────┤ +│ Dewitt635 │ Reichel38 │ +│ Anisa442 │ Purdy2 │ +│ Ross213 │ Mayert710 │ +│ Jim478 │ Mueller846 │ +│ Maria750 │ Schimmel440 │ +│ Ignacio928 │ Gorczany269 │ +│ Kip442 │ Zboncak558 │ +│ Jean712 │ Kuhlman484 │ +│ Mac103 │ Moen819 │ +│ Dianna917 │ Goldner995 │ +│ · │ · │ +│ · │ · │ +│ · │ · │ +│ Cameron381 │ Bogan287 │ +│ Andrew29 │ Donnelly343 │ +│ Charis952 │ Littel644 │ +│ Lyndon118 │ Swift555 │ +│ Edward499 │ Zieme486 │ +│ Myron933 │ Ritchie586 │ +│ Yolonda722 │ Champlin946 │ +│ Alayna598 │ Kozey370 │ +│ Kristian973 │ Ledner144 │ +│ Sydney660 │ Zulauf375 │ +├─────────────┴─────────────┤ +│ ? rows 2 columns │ +└───────────────────────────┘ +``` +### Correlated Subqueries +Learn how to use correlated subqueries to perform row-specific comparisons. +Find patients whose healthcare expenses are higher than the average expenses in their county. +```python +query = """SELECT + p.FIRST, + p.LAST, + p.COUNTY, + p.HEALTHCARE_EXPENSES, + ROUND(AVG_EXPENSES.COUNTY_AVG_EXPENSES, 2) AS ROUNDED_AVG_HEALTHCARE_EXPENSES +FROM + patients p +INNER JOIN ( + SELECT + COUNTY, + AVG(CAST(HEALTHCARE_EXPENSES AS DECIMAL(20,6))) AS COUNTY_AVG_EXPENSES + FROM + patients + GROUP BY + COUNTY +) AS AVG_EXPENSES ON p.COUNTY = AVG_EXPENSES.COUNTY +WHERE + p.HEALTHCARE_EXPENSES > ( + SELECT AVG(p2.HEALTHCARE_EXPENSES) + FROM patients p2 + WHERE p2.COUNTY = p.COUNTY + ); +""" +conn.sql(query) +``` +```python + +query = """SELECT + p.FIRST, + p.LAST, + p.COUNTY, + p.HEALTHCARE_EXPENSES, + ROUND(AVG_EXPENSES.COUNTY_AVG_EXPENSES, 2) AS ROUNDED_AVG_HEALTHCARE_EXPENSES +FROM + patients p +INNER JOIN ( + SELECT +… FROM patients p2 + WHERE p2.COUNTY = p.COUNTY + ); +""" +conn.sql(query) +┌────────────┬───────────────┬───────────────────┬─────────────────────┬─────────────────────────────────┐ +│ FIRST │ LAST │ COUNTY │ HEALTHCARE_EXPENSES │ ROUNDED_AVG_HEALTHCARE_EXPENSES │ +│ varchar │ varchar │ varchar │ double │ double │ +├────────────┼───────────────┼───────────────────┼─────────────────────┼─────────────────────────────────┤ +│ Tammy740 │ Ernser583 │ Bristol County │ 1546025.67 │ 793296.73 │ +│ Iliana226 │ Schmeler639 │ Barnstable County │ 1407960.93 │ 949155.23 │ +│ Anthony633 │ Yundt842 │ Essex County │ 1575731.48 │ 814437.7 │ +│ Jim478 │ Mueller846 │ Bristol County │ 1112473.78 │ 793296.73 │ +│ Sina65 │ Howell947 │ Middlesex County │ 1479425.58 │ 841491.73 │ +│ Maria750 │ Schimmel440 │ Plymouth County │ 952814.55 │ 856778.1 │ +│ Lorenzo669 │ Vandervort697 │ Middlesex County │ 873001.47 │ 841491.73 │ +│ David908 │ Carter549 │ Worcester County │ 834576.02 │ 804752.74 │ +│ Noah480 │ O'Reilly797 │ Essex County │ 915110.6 │ 814437.7 │ +│ Kip442 │ Zboncak558 │ Middlesex County │ 1704103.7200000002 │ 841491.73 │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ Tinisha932 │ Zulauf375 │ Bristol County │ 1035244.36 │ 793296.73 │ +│ Mario764 │ Estrada938 │ Suffolk County │ 1380401.5 │ 664340.79 │ +│ Glenn0 │ Schulist381 │ Barnstable County │ 1357123.8 │ 949155.23 │ +│ Porter490 │ Schulist381 │ Bristol County │ 1534216.57 │ 793296.73 │ +│ Darius626 │ Hackett68 │ Essex County │ 1715140.52 │ 814437.7 │ +│ Leonel449 │ Kunze215 │ Essex County │ 1492352.86 │ 814437.7 │ +│ Tyrell880 │ Kassulke119 │ Hampden County │ 1053520.21 │ 725636.71 │ +│ Arlen68 │ Gusikowski974 │ Worcester County │ 1363289.9 │ 804752.74 │ +│ Ashli227 │ Huel628 │ Worcester County │ 813730.02 │ 804752.74 │ +│ Loriann967 │ Mertz280 │ Norfolk County │ 1618397.57 │ 874351.83 │ +├────────────┴───────────────┴───────────────────┴─────────────────────┴─────────────────────────────────┤ +│ ? rows (>9999 rows, 20 shown) 5 columns │ +└────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +``` +Identify patients who have not been prescribed any medication. +```python +query = """ +SELECT p.FIRST, p.LAST +FROM patients p +WHERE NOT EXISTS ( + SELECT 1 FROM medications m WHERE m.PATIENT = p.Id +); +""" + +conn.sql(query) +``` +```python + +query = """ +SELECT p.FIRST, p.LAST +FROM patients p +WHERE NOT EXISTS ( + SELECT 1 FROM medications m WHERE m.PATIENT = p.Id +); +""" + +conn.sql(query) +┌────────────┬───────────────┐ +│ FIRST │ LAST │ +│ varchar │ varchar │ +├────────────┼───────────────┤ +│ Iliana226 │ Schmeler639 │ +│ Toshiko149 │ Swaniawski813 │ +│ Heath320 │ Streich926 │ +│ Rana586 │ Langworth352 │ +│ Dorathy429 │ Yost751 │ +│ Deon400 │ Littel644 │ +│ Emerson869 │ D'Amore443 │ +│ Alec433 │ Sanford861 │ +│ Roselyn270 │ McLaughlin530 │ +│ Claudia969 │ Smith67 │ +│ · │ · │ +│ · │ · │ +│ · │ · │ +│ Lou594 │ Gleason633 │ +│ Hank686 │ Russel238 │ +│ Earl438 │ Block661 │ +│ Mariano761 │ Heller342 │ +│ Sandra485 │ Fisher429 │ +│ Claire652 │ Murray856 │ +│ Doreen575 │ Graham902 │ +│ Fausto876 │ Kuhn96 │ +│ Vernon254 │ Gutmann970 │ +│ Ernest565 │ Block661 │ +├────────────┴───────────────┤ +│ ? rows 2 columns │ +└────────────────────────────┘ +``` +Find patients whose healthcare coverage is above the average of their respective city. +```python +query = """ +SELECT + p.FIRST, + p.LAST, + p.CITY, + ROUND(p.HEALTHCARE_COVERAGE,2) AS INDIVIDUAL_COVERAGE, + ROUND(c.AVG_COVERAGE,2) AS CITY_AVERAGE_COVERAGE +FROM + patients p +INNER JOIN ( + SELECT + CITY, + AVG(HEALTHCARE_COVERAGE) AS AVG_COVERAGE + FROM + patients + GROUP BY + CITY +) AS c ON p.CITY = c.CITY +WHERE + p.HEALTHCARE_COVERAGE > ( + SELECT AVG(HEALTHCARE_COVERAGE) + FROM patients + WHERE CITY = p.CITY + ); +""" +conn.sql(query) +``` +```python + +query = """ +SELECT + p.FIRST, + p.LAST, + p.CITY, + ROUND(p.HEALTHCARE_COVERAGE,2) AS INDIVIDUAL_COVERAGE, + ROUND(c.AVG_COVERAGE,2) AS CITY_AVERAGE_COVERAGE +FROM + patients p +INNER JOIN ( +… SELECT AVG(HEALTHCARE_COVERAGE) + FROM patients + WHERE CITY = p.CITY + ); +""" +conn.sql(query) +┌──────────────┬────────────────┬───────────────┬─────────────────────┬───────────────────────┐ +│ FIRST │ LAST │ CITY │ INDIVIDUAL_COVERAGE │ CITY_AVERAGE_COVERAGE │ +│ varchar │ varchar │ varchar │ double │ double │ +├──────────────┼────────────────┼───────────────┼─────────────────────┼───────────────────────┤ +│ Anisa442 │ Purdy2 │ Methuen │ 34393.57 │ 12949.31 │ +│ Sina65 │ Howell947 │ Sudbury │ 15481.43 │ 15385.85 │ +│ Maria750 │ Schimmel440 │ Plymouth │ 14231.22 │ 10826.53 │ +│ Kip442 │ Zboncak558 │ Hopkinton │ 155478.44 │ 15439.17 │ +│ Hans694 │ Wilkinson796 │ Framingham │ 13729.73 │ 11988.99 │ +│ Jean712 │ Kuhlman484 │ Boxford │ 11421.48 │ 8660.53 │ +│ Lorette239 │ Abbott774 │ Dennis │ 78740.23 │ 14918.28 │ +│ Tobi258 │ Bernier607 │ Wilmington │ 13889.6 │ 9411.52 │ +│ Rosia390 │ Reichel38 │ Rochester │ 16234.29 │ 9736.74 │ +│ Florencia449 │ Mercado213 │ Wilbraham │ 11427.25 │ 10468.26 │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ · │ · │ · │ · │ · │ +│ Jc393 │ Marks830 │ Brockton │ 16284.35 │ 12287.77 │ +│ Cedric746 │ Marvin195 │ Dedham │ 19806.72 │ 13840.99 │ +│ Milford485 │ Schmitt836 │ Hubbardston │ 10592.34 │ 8172.48 │ +│ Tegan755 │ Ruecker817 │ Waltham │ 30493.65 │ 14495.07 │ +│ Lindsay928 │ Lang846 │ Boston │ 11837.05 │ 10725.8 │ +│ Claudie965 │ Hermann103 │ Medford │ 15420.54 │ 12610.05 │ +│ Julie245 │ Nolan344 │ Essex │ 31903.53 │ 31787.13 │ +│ Mui729 │ Stoltenberg489 │ Wakefield │ 21528.91 │ 13699.32 │ +│ Oliver401 │ Lesch175 │ Yarmouth │ 27442.87 │ 19907.83 │ +│ Mi492 │ Gusikowski974 │ North Andover │ 18481.09 │ 17677.13 │ +├──────────────┴────────────────┴───────────────┴─────────────────────┴───────────────────────┤ +│ ? rows (>9999 rows, 20 shown) 5 columns │ +└─────────────────────────────────────────────────────────────────────────────────────────────┘ +``` +## FUNCTIONS IN SQL +___ +### String Functions +### CONCAT: Concatenates two or more strings. +```python +query = """SELECT CONCAT(FIRST, ' ', LAST) AS FullName FROM patients LIMIT 10; +;""" +conn.sql(query) +``` +```python +┌───────────────────────────┐ +│ FullName │ +│ varchar │ +├───────────────────────────┤ +│ Jacinto644 Kris249 │ +│ Alva958 Krajcik437 │ +│ Jimmie93 Harris789 │ +│ Gregorio366 Auer97 │ +│ Karyn217 Mueller846 │ +│ Jayson808 Fadel536 │ +│ José Eduardo181 Gómez206 │ +│ Milo271 Feil794 │ +│ Karyn217 Metz686 │ +│ Jeffrey461 Greenfelder433 │ +├───────────────────────────┤ +│ 10 rows │ +└───────────────────────────┘ +``` +### DATALENGTH: Returns the number of bytes used to represent any expre +```python +query = """ +SELECT FIRST, LENGTH(FIRST) AS LengthInBytes FROM patients LIMIT 10; +""" + +conn.sql(query) +``` +```python + +query = """ +SELECT FIRST, LENGTH(FIRST) AS LengthInBytes FROM patients LIMIT 10; +""" + +conn.sql(query) +┌─────────────────┬───────────────┐ +│ FIRST │ LengthInBytes │ +│ varchar │ int64 │ +├─────────────────┼───────────────┤ +│ Jacinto644 │ 10 │ +│ Alva958 │ 7 │ +│ Jimmie93 │ 8 │ +│ Gregorio366 │ 11 │ +│ Karyn217 │ 8 │ +│ Jayson808 │ 9 │ +│ José Eduardo181 │ 15 │ +│ Milo271 │ 7 │ +│ Karyn217 │ 8 │ +│ Jeffrey461 │ 10 │ +├─────────────────┴───────────────┤ +│ 10 rows 2 columns │ +└─────────────────────────────────┘ +``` +### LEFT: Returns the left part of a character string with the specified number of characters. +```python +query = """SELECT LEFT(FIRST, 5) AS Initial FROM patients LIMIT 10;""" +conn.sql(query) +``` +```python + +query = """SELECT LEFT(FIRST, 5) AS Initial FROM patients LIMIT 10;""" +conn.sql(query) +┌─────────┐ +│ Initial │ +│ varchar │ +├─────────┤ +│ Jacin │ +│ Alva9 │ +│ Jimmi │ +│ Grego │ +│ Karyn │ +│ Jayso │ +│ José │ +│ Milo2 │ +│ Karyn │ +│ Jeffr │ +├─────────┤ +│ 10 rows │ +└─────────┘ +``` +### LOWER: Converts all characters in the specified string to lowercase. +### UPPER: Convert to uppercase +```python +query = """ +SELECT LOWER(FIRST) AS LowercaseFirstName, UPPER(LAST) AS UppercaseLastName FROM patients LIMIT 10; +""" +conn.sql(query) +``` +```python + +query = """ +SELECT LOWER(FIRST) AS LowercaseFirstName, UPPER(LAST) AS UppercaseLastName FROM patients LIMIT 10; +""" +conn.sql(query) +┌────────────────────┬───────────────────┐ +│ LowercaseFirstName │ UppercaseLastName │ +│ varchar │ varchar │ +├────────────────────┼───────────────────┤ +│ jacinto644 │ KRIS249 │ +│ alva958 │ KRAJCIK437 │ +│ jimmie93 │ HARRIS789 │ +│ gregorio366 │ AUER97 │ +│ karyn217 │ MUELLER846 │ +│ jayson808 │ FADEL536 │ +│ josé eduardo181 │ GÓMEZ206 │ +│ milo271 │ FEIL794 │ +│ karyn217 │ METZ686 │ +│ jeffrey461 │ GREENFELDER433 │ +├────────────────────┴───────────────────┤ +│ 10 rows 2 columns │ +└────────────────────────────────────────┘ +``` +LTRIM: Removes leading spaces from a string. +```python +query = """SELECT LTRIM(ADDRESS) AS TrimmedAddress FROM patients LIMIT 10;""" + +conn.sql(query) + +``` +```python + +query = """SELECT LTRIM(ADDRESS) AS TrimmedAddress FROM patients LIMIT 10;""" + +conn.sql(query) + +┌────────────────────────────────┐ +│ TrimmedAddress │ +│ varchar │ +├────────────────────────────────┤ +│ 888 Hickle Ferry Suite 38 │ +│ 1048 Skiles Trailer │ +│ 201 Mitchell Lodge Unit 67 │ +│ 1050 Lindgren Extension Apt 38 │ +│ 570 Abshire Forge Suite 32 │ +│ 1056 Harris Lane Suite 70 │ +│ 427 Balistreri Way Unit 19 │ +│ 422 Farrell Path Unit 69 │ +│ 181 Feest Passage Suite 64 │ +│ 428 Wiza Glen Unit 91 │ +├────────────────────────────────┤ +│ 10 rows │ +└────────────────────────────────┘ +``` +REPLACE: Replaces all occurrences of a specified string value with another string value. +```python +query = """SELECT FIRST, LAST, + ADDRESS AS LongAddress, + REPLACE(ADDRESS, 'Street', 'St') AS ShortAddress +FROM + patients +WHERE + ADDRESS LIKE '%Street%' LIMIT 10; +""" + +conn.sql(query) +``` +```python +┌────────────┬───────────────┬───────────────────────────┬───────────────────────┐ +│ FIRST │ LAST │ LongAddress │ ShortAddress │ +│ varchar │ varchar │ varchar │ varchar │ +├────────────┼───────────────┼───────────────────────────┼───────────────────────┤ +│ Lorrie905 │ Leannon79 │ 813 Casper Street │ 813 Casper St │ +│ Asa127 │ Block661 │ 140 Rohan Street Suite 50 │ 140 Rohan St Suite 50 │ +│ Logan497 │ Brekke496 │ 1081 Orn Street │ 1081 Orn St │ +│ Cletus494 │ Strosin214 │ 1019 Haley Street │ 1019 Haley St │ +│ Cortney940 │ Stehr398 │ 458 Streich Street │ 458 Streich St │ +│ Latoyia537 │ Gaylord332 │ 200 Heaney Street │ 200 Heaney St │ +│ Clark193 │ Hilll811 │ 202 Tromp Street Suite 0 │ 202 Tromp St Suite 0 │ +│ Jc393 │ Bosco882 │ 792 Walsh Street │ 792 Walsh St │ +│ Noel608 │ Swaniawski813 │ 155 Walter Street │ 155 Walter St │ +│ Sari509 │ Hoppe518 │ 550 Lang Street Suite 65 │ 550 Lang St Suite 65 │ +├────────────┴───────────────┴───────────────────────────┴───────────────────────┤ +│ 10 rows 4 columns │ +└────────────────────────────────────────────────────────────────────────────────┘ +``` +RIGHT: Returns the right part of a character string with the specified number of characters. +```python +query = """SELECT FIRST, RIGHT(FIRST, 6) AS LastSixChars FROM patients LIMIT 10; +""" + +conn.sql(query) +``` +```python +┌─────────────────┬──────────────┐ +│ FIRST │ LastSixChars │ +│ varchar │ varchar │ +├─────────────────┼──────────────┤ +│ Jacinto644 │ nto644 │ +│ Alva958 │ lva958 │ +│ Jimmie93 │ mmie93 │ +│ Gregorio366 │ rio366 │ +│ Karyn217 │ ryn217 │ +│ Jayson808 │ son808 │ +│ José Eduardo181 │ rdo181 │ +│ Milo271 │ ilo271 │ +│ Karyn217 │ ryn217 │ +│ Jeffrey461 │ rey461 │ +├─────────────────┴──────────────┤ +│ 10 rows 2 columns │ +└────────────────────────────────┘ +``` +SUBSTRING: Returns part of a character, binary, text, or image expression in SQL Server. +```python +query = """SELECT FIRST, SUBSTRING(FIRST, 1, 4) AS FirstFourChars FROM patients LIMIT 5; +""" + +conn.sql(query) +``` +```python +┌─────────────┬────────────────┐ +│ FIRST │ FirstFourChars │ +│ varchar │ varchar │ +├─────────────┼────────────────┤ +│ Jacinto644 │ Jaci │ +│ Alva958 │ Alva │ +│ Jimmie93 │ Jimm │ +│ Gregorio366 │ Greg │ +│ Karyn217 │ Kary │ +└─────────────┴────────────────┘ +``` +REGEXP_REPLACE can use regular expressions to strip numeric characters: +```python +query = """SELECT +FIRST, REGEXP_REPLACE(FIRST, '[0-9]', '', 'g') AS FirstNameStripped, LAST, REGEXP_REPLACE(LAST, '[0-9]', '', 'g') AS LastNameStripped +FROM patients; +""" + +conn.sql(query) +``` +```python + +query = """SELECT +FIRST, REGEXP_REPLACE(FIRST, '[0-9]', '', 'g') AS FirstNameStripped, LAST, REGEXP_REPLACE(LAST, '[0-9]', '', 'g') AS LastNameStripped +FROM patients; +""" + +conn.sql(query) +┌─────────────────┬───────────────────┬────────────────┬──────────────────┐ +│ FIRST │ FirstNameStripped │ LAST │ LastNameStripped │ +│ varchar │ varchar │ varchar │ varchar │ +├─────────────────┼───────────────────┼────────────────┼──────────────────┤ +│ Jacinto644 │ Jacinto │ Kris249 │ Kris │ +│ Alva958 │ Alva │ Krajcik437 │ Krajcik │ +│ Jimmie93 │ Jimmie │ Harris789 │ Harris │ +│ Gregorio366 │ Gregorio │ Auer97 │ Auer │ +│ Karyn217 │ Karyn │ Mueller846 │ Mueller │ +│ Jayson808 │ Jayson │ Fadel536 │ Fadel │ +│ José Eduardo181 │ José Eduardo │ Gómez206 │ Gómez │ +│ Milo271 │ Milo │ Feil794 │ Feil │ +│ Karyn217 │ Karyn │ Metz686 │ Metz │ +│ Jeffrey461 │ Jeffrey │ Greenfelder433 │ Greenfelder │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ · │ · │ · │ · │ +│ Raymond398 │ Raymond │ Kuvalis369 │ Kuvalis │ +│ Gearldine455 │ Gearldine │ Boyer713 │ Boyer │ +│ Nichol11 │ Nichol │ Gleichner915 │ Gleichner │ +│ Louvenia131 │ Louvenia │ Marks830 │ Marks │ +│ Raymon366 │ Raymon │ Beer512 │ Beer │ +│ Camelia346 │ Camelia │ Stamm704 │ Stamm │ +│ William805 │ William │ Pacocha935 │ Pacocha │ +│ Guillermo498 │ Guillermo │ Téllez750 │ Téllez │ +│ Milton509 │ Milton │ Bailey598 │ Bailey │ +│ Cecilia788 │ Cecilia │ Wisozk929 │ Wisozk │ +├─────────────────┴───────────────────┴────────────────┴──────────────────┤ +│ ? rows (>9999 rows, 20 shown) 4 columns │ +└─────────────────────────────────────────────────────────────────────────┘ +``` +___ +___ + +### Creating a database and adding data to tables + +### CREATE, ALTER, DROP, INSERT commands +___ +First lets have a in-memory database +```python +newdb = duckdb.connect() +``` +CREATE TABLE: Create a books table to mimic a library catalog system. +```python +query = """CREATE TABLE books ( + book_id INTEGER PRIMARY KEY, + title VARCHAR, + author VARCHAR, + published_year INTEGER +); +""" + +newdb.sql(query) +``` +The CREATE TABLE statement defines the schema for this table: +This statement creates a table with columns for the book ID, title, author, and the year of publication. The book_id column is designated as the primary key, ensuring that each record in the table is unique. +___ +check +```python +newdb.sql("SHOW TABLES;") +``` +```python +┌─────────┐ +│ name │ +│ varchar │ +├─────────┤ +│ books │ +└─────────┘ +``` +check data +```python +newdb.sql("DESCRIBE books;") +``` +```python + +newdb.sql("DESCRIBE books;") +┌────────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐ +│ column_name │ column_type │ null │ key │ default │ extra │ +│ varchar │ varchar │ varchar │ varchar │ varchar │ int32 │ +├────────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤ +│ book_id │ INTEGER │ NO │ PRI │ NULL │ NULL │ +│ title │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ author │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ published_year │ INTEGER │ YES │ NULL │ NULL │ NULL │ +└────────────────┴─────────────┴─────────┴─────────┴─────────┴───────┘ +``` +Alter Table: After creating the table, you might need to add more information. Use the ALTER TABLE statement to add a new column: +```python +query = """ALTER TABLE books ADD COLUMN genre VARCHAR; +""" +newdb.sql(query) +# This adds a genre column to the books table, allowing you to categorize books. +``` +```python +newdb.sql("DESCRIBE books;") +``` +```python + +newdb.sql("DESCRIBE books;") +┌────────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐ +│ column_name │ column_type │ null │ key │ default │ extra │ +│ varchar │ varchar │ varchar │ varchar │ varchar │ int32 │ +├────────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤ +│ book_id │ INTEGER │ NO │ PRI │ NULL │ NULL │ +│ title │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ author │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +│ published_year │ INTEGER │ YES │ NULL │ NULL │ NULL │ +│ genre │ VARCHAR │ YES │ NULL │ NULL │ NULL │ +└────────────────┴─────────────┴─────────┴─────────┴─────────┴───────┘ +``` +let's add another table which we will DROP later; +```python +query = """CREATE TABLE authors ( + author_id INTEGER PRIMARY KEY, + firstname VARCHAR, + lastname VARCHAR, + birth_year INTEGER +); +""" + +newdb.sql(query) +``` + +```python +newdb.sql("SHOW TABLES;") +``` +Now let's DROP the author table +```python +query = """DROP TABLE IF EXISTS authors; +""" + +newdb.sql(query) +``` + +```python +newdb.sql("SHOW TABLES;") +``` +### Let's add some data now +### INSERT To populate the books table with data. This inserts 10 records into the books table, providing a diverse set of examples. +```python +query = """INSERT INTO books (book_id, title, author, published_year, genre) VALUES +(1, '1984', 'George Orwell', 1949, 'Dystopian'), +(2, 'The Great Gatsby', 'F. Scott Fitzgerald', 1925, 'Classic'), +(3, 'To Kill a Mockingbird', 'Harper Lee', 1960, 'Classic'), +(4, 'Pride and Prejudice', 'Jane Austen', 1813, 'Romance'), +(5, 'The Hobbit', 'J.R.R. Tolkien', 1937, 'Fantasy'), +(6, 'The Catcher in the Rye', 'J.D. Salinger', 1951, 'Literary Fiction'), +(7, 'Harry Potter and the Sorcerer’s Stone', 'J.K. Rowling', 1997, 'Fantasy'), +(8, 'The Da Vinci Code', 'Dan Brown', 2003, 'Thriller'), +(9, 'Sapiens: A Brief History of Humankind', 'Yuval Noah Harari', 2011, 'Non-fiction'), +(10, 'Beloved', 'Toni Morrison', 1987, 'Historical Fiction');""" + +newdb.sql(query) +``` + +```python +query = """SELECT title AS "Book Title", author AS "Author Name", Genre FROM books; +""" + +newdb.sql(query) +``` + +```python +┌───────────────────────────────────────┬─────────────────────┬────────────────────┐ +│ Book Title │ Author Name │ genre │ +│ varchar │ varchar │ varchar │ +├───────────────────────────────────────┼─────────────────────┼────────────────────┤ +│ 1984 │ George Orwell │ Dystopian │ +│ The Great Gatsby │ F. Scott Fitzgerald │ Classic │ +│ To Kill a Mockingbird │ Harper Lee │ Classic │ +│ Pride and Prejudice │ Jane Austen │ Romance │ +│ The Hobbit │ J.R.R. Tolkien │ Fantasy │ +│ The Catcher in the Rye │ J.D. Salinger │ Literary Fiction │ +│ Harry Potter and the Sorcerer’s Stone │ J.K. Rowling │ Fantasy │ +│ The Da Vinci Code │ Dan Brown │ Thriller │ +│ Sapiens: A Brief History of Humankind │ Yuval Noah Harari │ Non-fiction │ +│ Beloved │ Toni Morrison │ Historical Fiction │ +├───────────────────────────────────────┴─────────────────────┴────────────────────┤ +│ 10 rows 3 columns │ +└──────────────────────────────────────────────────────────────────────────────────┘ +``` +### Updating Data: UPDATE +This changes the genre of the book titled '1984' to 'Science Fiction'. +```python +query = """UPDATE books SET genre = 'Science Fiction' WHERE title = '1984'; +""" + +newdb.sql(query) +``` + +```python +query = """SELECT title AS "Book Title", author AS "Author Name", Genre FROM books; +""" + +newdb.sql(query) +``` + +```python +┌───────────────────────────────────────┬─────────────────────┬────────────────────┐ +│ Book Title │ Author Name │ genre │ +│ varchar │ varchar │ varchar │ +├───────────────────────────────────────┼─────────────────────┼────────────────────┤ +│ 1984 │ George Orwell │ Science Fiction │ +│ The Great Gatsby │ F. Scott Fitzgerald │ Classic │ +│ To Kill a Mockingbird │ Harper Lee │ Classic │ +│ Pride and Prejudice │ Jane Austen │ Romance │ +│ The Hobbit │ J.R.R. Tolkien │ Fantasy │ +│ The Catcher in the Rye │ J.D. Salinger │ Literary Fiction │ +│ Harry Potter and the Sorcerer’s Stone │ J.K. Rowling │ Fantasy │ +│ The Da Vinci Code │ Dan Brown │ Thriller │ +│ Sapiens: A Brief History of Humankind │ Yuval Noah Harari │ Non-fiction │ +│ Beloved │ Toni Morrison │ Historical Fiction │ +├───────────────────────────────────────┴─────────────────────┴────────────────────┤ +│ 10 rows 3 columns │ +└──────────────────────────────────────────────────────────────────────────────────┘ +``` +## DELETE +e.g. To remove a book from the catalog + +```python +query = """DELETE FROM books WHERE title = 'The Great Gatsby';""" +newdb.sql(query) +``` + +```python +query = """SELECT title AS "Book Title", author AS "Author Name", Genre FROM books; +""" + +newdb.sql(query) +``` + +```python +┌───────────────────────────────────────┬───────────────────┬────────────────────┐ +│ Book Title │ Author Name │ genre │ +│ varchar │ varchar │ varchar │ +├───────────────────────────────────────┼───────────────────┼────────────────────┤ +│ 1984 │ George Orwell │ Science Fiction │ +│ To Kill a Mockingbird │ Harper Lee │ Classic │ +│ Pride and Prejudice │ Jane Austen │ Romance │ +│ The Hobbit │ J.R.R. Tolkien │ Fantasy │ +│ The Catcher in the Rye │ J.D. Salinger │ Literary Fiction │ +│ Harry Potter and the Sorcerer’s Stone │ J.K. Rowling │ Fantasy │ +│ The Da Vinci Code │ Dan Brown │ Thriller │ +│ Sapiens: A Brief History of Humankind │ Yuval Noah Harari │ Non-fiction │ +│ Beloved │ Toni Morrison │ Historical Fiction │ +└───────────────────────────────────────┴───────────────────┴────────────────────┘ +``` +### TRUNCATE +- The TRUNCATE TABLE statement is used to delete all rows in a table without logging the individual row deletions. +- This is faster than using DELETE without a WHERE clause because TRUNCATE immediately deallocates data pages used by the table. +- Use TRUNCATE when you want to quickly remove all records from a table but keep the table structure for future use. +- Unlike DELETE, TRUNCATE does not generate a large number of transaction logs, making it more efficient for completely clearing a table. + +```python +query = """TRUNCATE TABLE books;""" + +newdb.sql(query) +``` + +```python +query = """SELECT * FROM books; +""" + +newdb.sql(query) +``` +#### Adding one more table; named 'authors' +```python +query = """CREATE TABLE authors ( + author_id INTEGER PRIMARY KEY, + name VARCHAR, + birth_year INTEGER, + nationality VARCHAR +); +""" + +newdb.sql(query) +``` +Add information the authors table +```python +query = """INSERT INTO authors (author_id, name, birth_year, nationality) VALUES +(1, 'George Orwell', 1903, 'British'), +(2, 'F. Scott Fitzgerald', 1896, 'American'), +(3, 'Harper Lee', 1926, 'American'), +(4, 'Jane Austen', 1775, 'British'), +(5, 'J.R.R. Tolkien', 1892, 'British'), +(6, 'J.D. Salinger', 1919, 'American'), +(7, 'J.K. Rowling', 1965, 'British'), +(8, 'Dan Brown', 1964, 'American'), +(9, 'Yuval Noah Harari', 1976, 'Israeli'), +(10, 'Toni Morrison', 1931, 'American'); +""" + + +newdb.sql(query) +``` + +```python +query = """SELECT * FROM books; +""" + +newdb.sql(query) +``` +```python +┌─────────┬───────────────────────────────────────┬─────────────────────┬────────────────┬────────────────────┐ +│ book_id │ title │ author │ published_year │ genre │ +│ int64 │ varchar │ varchar │ int64 │ varchar │ +├─────────┼───────────────────────────────────────┼─────────────────────┼────────────────┼────────────────────┤ +│ 1 │ 1984 │ George Orwell │ 1949 │ Dystopian │ +│ 2 │ The Great Gatsby │ F. Scott Fitzgerald │ 1925 │ Classic │ +│ 3 │ To Kill a Mockingbird │ Harper Lee │ 1960 │ Classic │ +│ 4 │ Pride and Prejudice │ Jane Austen │ 1813 │ Romance │ +│ 5 │ The Hobbit │ J.R.R. Tolkien │ 1937 │ Fantasy │ +│ 6 │ The Catcher in the Rye │ J.D. Salinger │ 1951 │ Literary Fiction │ +│ 7 │ Harry Potter and the Sorcerer’s Stone │ J.K. Rowling │ 1997 │ Fantasy │ +│ 8 │ The Da Vinci Code │ Dan Brown │ 2003 │ Thriller │ +│ 9 │ Sapiens: A Brief History of Humankind │ Yuval Noah Harari │ 2011 │ Non-fiction │ +│ 10 │ Beloved │ Toni Morrison │ 1987 │ Historical Fiction │ +├─────────┴───────────────────────────────────────┴─────────────────────┴────────────────┴────────────────────┤ +│ 10 rows 5 columns │ +└─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +``` + +```python +query = """SELECT * FROM authors; +""" + +newdb.sql(query) +``` + +```python +newdb.sql(query) +┌───────────┬─────────────────────┬────────────┬─────────────┐ +│ author_id │ name │ birth_year │ nationality │ +│ int32 │ varchar │ int32 │ varchar │ +├───────────┼─────────────────────┼────────────┼─────────────┤ +│ 1 │ George Orwell │ 1903 │ British │ +│ 2 │ F. Scott Fitzgerald │ 1896 │ American │ +│ 3 │ Harper Lee │ 1926 │ American │ +│ 4 │ Jane Austen │ 1775 │ British │ +│ 5 │ J.R.R. Tolkien │ 1892 │ British │ +│ 6 │ J.D. Salinger │ 1919 │ American │ +│ 7 │ J.K. Rowling │ 1965 │ British │ +│ 8 │ Dan Brown │ 1964 │ American │ +│ 9 │ Yuval Noah Harari │ 1976 │ Israeli │ +│ 10 │ Toni Morrison │ 1931 │ American │ +├───────────┴─────────────────────┴────────────┴─────────────┤ +│ 10 rows 4 columns │ +└────────────────────────────────────────────────────────────┘ +``` +## EXPORTING Data to csv from SQL +```python +query = """EXPORT DATABASE './' (FORMAT CSV, DELIMITER ',');""" + +newdb.sql(query) +``` +## Thank You! diff --git a/11_sql_duckdb/index.html b/11_sql_duckdb/index.html new file mode 100644 index 000000000..2a394295c --- /dev/null +++ b/11_sql_duckdb/index.html @@ -0,0 +1,3487 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Exploring SQL Databases with DuckDB - CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+ + + + + + + +

Exploring SQL Databases with DuckDB

+
+

Database

+
    +
  • A database is a structured collection of data.
  • +
  • It allows for the storage, retrieval, modification, and deletion of data in an organized manner.
  • +
  • Databases come in various types, including relational and NoSQL. Relational databases organize data into tables, which are interconnected.
  • +
  • Each table represents a related data collection and is organized into rows and columns.
  • +
+

Relational DB Image

+
+

SQL stands for Structured Query Language.

+

It's a programming language that stores and processes information in relational databases.

+

Types of SQL Statements

+
    +
  • DDL (Data Definition Language): DDL statements are used to define, modify, and remove database structures, but not the data within them. Common DDL statements include CREATE, ALTER, and DROP.
  • +
  • DML (Data Manipulation Language): DML statements manage data within schema objects. They include SELECT, INSERT, UPDATE, and DELETE.
  • +
+
+

DuckDB

+

Duck DB Logo

+
    +
  • An in-memory, columnar database management system optimized for analytical queries."
  • +
  • Open-source and designed for simplicity, speed, and efficiency in processing analytical workloads."
  • +
  • Supports SQL standards for easy integration with existing tools and workflows.
  • +
+
+

Install DuckDB

+

With Python set up, you can now install DuckDB using pip. In your terminal or command line, execute:

+
pip install duckdb
+
+
+
+

To ensure DuckDB is installed correctly, launch your jupyter Notebook and try importing the DuckDB module:

+
pip install duckdb
+
+

If no errors occur, DuckDB is successfully installed and ready to use.

+
+

In your Jupyter Notebook, import duckdb by running

+
import duckdb
+
+

For this tutorial, we will be using a pre-configured Duckdb Database.

+

Let's download the database file:

+
!wget --content-disposition https://arizona.box.com/shared/static/uozg0z86rtdjupwpc7i971xwzuzhp42o.duckdb
+
+

Connect to the database using:

+
conn = duckdb.connect(database='/content/my_database.duckdb', read_only=True)
+
+

Viewing which tables are available inside the database

+
conn.sql("SHOW TABLES;")
+
+
┌───────────────────┐
+       name        
+      varchar      
+├───────────────────┤
+ allergies         
+ careplans         
+ conditions        
+ devices           
+ encounters        
+ imaging_studies   
+ immunizations     
+ medications       
+ observations      
+ organizations     
+ patients          
+ payer_transitions 
+ payers            
+ procedures        
+ providers         
+ supplies          
+├───────────────────┤
+      16 rows      
+└───────────────────┘
+
+

Before running any query, we need to know the columns inside particular tables

+

conn.sql("DESCRIBE patients;")
+
+
┌─────────────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐
+     column_name      column_type   null      key    default  extra 
+       varchar          varchar    varchar  varchar  varchar  int32 
+├─────────────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤
+ Id                   VARCHAR      YES      NULL     NULL      NULL 
+ BIRTHDATE            VARCHAR      YES      NULL     NULL      NULL 
+ DEATHDATE            VARCHAR      YES      NULL     NULL      NULL 
+ SSN                  VARCHAR      YES      NULL     NULL      NULL 
+ DRIVERS              VARCHAR      YES      NULL     NULL      NULL 
+ PASSPORT             VARCHAR      YES      NULL     NULL      NULL 
+ PREFIX               VARCHAR      YES      NULL     NULL      NULL 
+ FIRST                VARCHAR      YES      NULL     NULL      NULL 
+ LAST                 VARCHAR      YES      NULL     NULL      NULL 
+ SUFFIX               VARCHAR      YES      NULL     NULL      NULL 
+   ·                     ·          ·        ·        ·          ·  
+   ·                     ·          ·        ·        ·          ·  
+   ·                     ·          ·        ·        ·          ·  
+ BIRTHPLACE           VARCHAR      YES      NULL     NULL      NULL 
+ ADDRESS              VARCHAR      YES      NULL     NULL      NULL 
+ CITY                 VARCHAR      YES      NULL     NULL      NULL 
+ STATE                VARCHAR      YES      NULL     NULL      NULL 
+ COUNTY               VARCHAR      YES      NULL     NULL      NULL 
+ ZIP                  DOUBLE       YES      NULL     NULL      NULL 
+ LAT                  DOUBLE       YES      NULL     NULL      NULL 
+ LON                  DOUBLE       YES      NULL     NULL      NULL 
+ HEALTHCARE_EXPENSES  DOUBLE       YES      NULL     NULL      NULL 
+ HEALTHCARE_COVERAGE  DOUBLE       YES      NULL     NULL      NULL 
+├─────────────────────┴─────────────┴─────────┴─────────┴─────────┴───────┤
+ 25 rows (20 shown)                                            6 columns 
+└─────────────────────────────────────────────────────────────────────────┘
+
+
conn.sql("DESCRIBE medications;")
+
+
conn.sql("DESCRIBE medications;")
+┌───────────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐
+    column_name     column_type   null      key    default  extra 
+      varchar         varchar    varchar  varchar  varchar  int32 
+├───────────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤
+ START              VARCHAR      YES      NULL     NULL      NULL 
+ STOP               VARCHAR      YES      NULL     NULL      NULL 
+ PATIENT            VARCHAR      YES      NULL     NULL      NULL 
+ PAYER              VARCHAR      YES      NULL     NULL      NULL 
+ ENCOUNTER          VARCHAR      YES      NULL     NULL      NULL 
+ CODE               BIGINT       YES      NULL     NULL      NULL 
+ DESCRIPTION        VARCHAR      YES      NULL     NULL      NULL 
+ BASE_COST          DOUBLE       YES      NULL     NULL      NULL 
+ PAYER_COVERAGE     DOUBLE       YES      NULL     NULL      NULL 
+ DISPENSES          BIGINT       YES      NULL     NULL      NULL 
+ TOTALCOST          DOUBLE       YES      NULL     NULL      NULL 
+ REASONCODE         DOUBLE       YES      NULL     NULL      NULL 
+ REASONDESCRIPTION  VARCHAR      YES      NULL     NULL      NULL 
+├───────────────────┴─────────────┴─────────┴─────────┴─────────┴───────┤
+ 13 rows                                                     6 columns 
+└───────────────────────────────────────────────────────────────────────┘
+
+
conn.sql("DESCRIBE immunizations;")
+
+
┌─────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐
+ column_name  column_type   null      key    default  extra 
+   varchar      varchar    varchar  varchar  varchar  int32 
+├─────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤
+ DATE         VARCHAR      YES      NULL     NULL      NULL 
+ PATIENT      VARCHAR      YES      NULL     NULL      NULL 
+ ENCOUNTER    VARCHAR      YES      NULL     NULL      NULL 
+ CODE         BIGINT       YES      NULL     NULL      NULL 
+ DESCRIPTION  VARCHAR      YES      NULL     NULL      NULL 
+ BASE_COST    DOUBLE       YES      NULL     NULL      NULL 
+└─────────────┴─────────────┴─────────┴─────────┴─────────┴───────┘
+

+
+
+

Querying Data

+

SELECT - The SELECT statement selects data from a database.

+

E.g.: Query the patients table and display patient first name, last name, gender, and the city they live in.

+

conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients;")
+
+
┌─────────────────┬────────────────┬─────────┬─────────────┐
+      FIRST            LAST       GENDER      CITY     
+     varchar         varchar      varchar    varchar   
+├─────────────────┼────────────────┼─────────┼─────────────┤
+ Jacinto644       Kris249         M        Springfield 
+ Alva958          Krajcik437      F        Walpole     
+ Jimmie93         Harris789       F        Pembroke    
+ Gregorio366      Auer97          M        Boston      
+ Karyn217         Mueller846      F        Colrain     
+ Jayson808        Fadel536        M        Chicopee    
+ José Eduardo181  Gómez206        M        Chicopee    
+ Milo271          Feil794         M        Somerville  
+ Karyn217         Metz686         F        Medfield    
+ Jeffrey461       Greenfelder433  M        Springfield 
+     ·                ·           ·           ·        
+     ·                ·           ·           ·        
+     ·                ·           ·           ·        
+ Raymond398       Kuvalis369      M        Hingham     
+ Gearldine455     Boyer713        F        Boston      
+ Nichol11         Gleichner915    F        Lynn        
+ Louvenia131      Marks830        F        Springfield 
+ Raymon366        Beer512         M        Springfield 
+ Camelia346       Stamm704        F        Dedham      
+ William805       Pacocha935      M        Brockton    
+ Guillermo498     Téllez750       M        Somerville  
+ Milton509        Bailey598       M        Hull        
+ Cecilia788       Wisozk929       F        Worcester   
+├─────────────────┴────────────────┴─────────┴─────────────┤
+ ? rows (>9999 rows, 20 shown)                  4 columns 
+└──────────────────────────────────────────────────────────┘
+

+

Count the number of rows in patients table

+

This query will count the total number of rows in the patients table, effectively giving you the total number of patients.

+

conn.sql("SELECT COUNT(*) FROM patients;")
+
+
┌──────────────┐
+ count_star() 
+    int64     
+├──────────────┤
+       124150 
+└──────────────┘
+

+

COUNT and DISTINCT

+

If you want to count distinct values of a specific column, for example, distinct cities, you can modify the query as follows: +

conn.sql("SELECT COUNT(DISTINCT CITY) FROM patients;")
+
+
┌──────────────────────┐
+ count(DISTINCT CITY) 
+        int64         
+├──────────────────────┤
+                  351 
+└──────────────────────┘
+

+

Filtering data based on a condition

+

The WHERE clause filters records that fulfill a specified condition. +e.g: Patients in City = 'Springfield';

+

conn.sql("SELECT FIRST, LAST, GENDER, CITY, PASSPORT FROM patients WHERE city = 'Springfield';")
+
+
┌──────────────┬────────────────┬─────────┬─────────────┬────────────┐
+    FIRST           LAST       GENDER      CITY       PASSPORT  
+   varchar        varchar      varchar    varchar     varchar   
+├──────────────┼────────────────┼─────────┼─────────────┼────────────┤
+ Jacinto644    Kris249         M        Springfield  NULL       
+ Jeffrey461    Greenfelder433  M        Springfield  NULL       
+ Sabina296     Flatley871      F        Springfield  X85058581X 
+ Theodora872   Johnson679      F        Springfield  X21164602X 
+ Lavera253     Anderson154     F        Springfield  X83686992X 
+ Golden321     Pollich983      F        Springfield  NULL       
+ Georgiann138  Greenfelder433  F        Springfield  X58134116X 
+ Fausto876     Bechtelar572    M        Springfield  NULL       
+ Talisha682    Brakus656       F        Springfield  X53645004X 
+ Golden321     Durgan499       F        Springfield  X49016634X 
+    ·             ·            ·             ·           ·      
+    ·             ·            ·             ·           ·      
+    ·             ·            ·             ·           ·      
+ Carla633      Terán294        F        Springfield  X50212869X 
+ Ángela136     Muñiz642        F        Springfield  NULL       
+ Marceline716  Kuhlman484      F        Springfield  X70432308X 
+ Ana María762  Cotto891        F        Springfield  X4604857X  
+ Josefina523   Vanegas191      F        Springfield  X85396222X 
+ Ester635      Sevilla788      F        Springfield  X50601786X 
+ Tomás404      Galarza986      M        Springfield  X76790663X 
+ Arturo47      Delafuente833   M        Springfield  X45087804X 
+ Gilberto712   Martínez540     M        Springfield  X89729075X 
+ Juanita470    Connelly992     F        Springfield  X71156217X 
+├──────────────┴────────────────┴─────────┴─────────────┴────────────┤
+ 2814 rows (20 shown)                                     5 columns 
+└────────────────────────────────────────────────────────────────────┘
+

+

Multiple conditions in Selection

+

e.g 1: Female Patients in City = 'Springfield'; +

conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE city = 'Springfield' AND gender = 'F';")
+
+
conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE city = 'Springfield' AND gender = 'F';")
+┌──────────────┬────────────────┬─────────┬─────────────┐
+    FIRST           LAST       GENDER      CITY     
+   varchar        varchar      varchar    varchar   
+├──────────────┼────────────────┼─────────┼─────────────┤
+ Sabina296     Flatley871      F        Springfield 
+ Theodora872   Johnson679      F        Springfield 
+ Lavera253     Anderson154     F        Springfield 
+ Golden321     Pollich983      F        Springfield 
+ Georgiann138  Greenfelder433  F        Springfield 
+ Talisha682    Brakus656       F        Springfield 
+ Golden321     Durgan499       F        Springfield 
+ Jerrie417     Gislason620     F        Springfield 
+ Venus149      Hodkiewicz467   F        Springfield 
+ Refugia211    Wintheiser220   F        Springfield 
+    ·              ·           ·             ·      
+    ·              ·           ·             ·      
+    ·              ·           ·             ·      
+ Tisha655      Renner328       F        Springfield 
+ Mona85        Senger904       F        Springfield 
+ Isabel214     Camacho176      F        Springfield 
+ Carla633      Terán294        F        Springfield 
+ Ángela136     Muñiz642        F        Springfield 
+ Marceline716  Kuhlman484      F        Springfield 
+ Ana María762  Cotto891        F        Springfield 
+ Josefina523   Vanegas191      F        Springfield 
+ Ester635      Sevilla788      F        Springfield 
+ Juanita470    Connelly992     F        Springfield 
+├──────────────┴────────────────┴─────────┴─────────────┤
+ 1351 rows (20 shown)                        4 columns 
+└───────────────────────────────────────────────────────┘
+

+

Nested selection

+

e.g. Female patients from City Springfield or Boston +

conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE gender = 'F' AND (city = 'Springfield' OR city = 'Boston');")
+
+
┌──────────────┬────────────────┬─────────┬─────────────┐
+    FIRST           LAST       GENDER      CITY     
+   varchar        varchar      varchar    varchar   
+├──────────────┼────────────────┼─────────┼─────────────┤
+ Sabina296     Flatley871      F        Springfield 
+ Theodora872   Johnson679      F        Springfield 
+ Lavera253     Anderson154     F        Springfield 
+ Golden321     Pollich983      F        Springfield 
+ Georgiann138  Greenfelder433  F        Springfield 
+ Talisha682    Brakus656       F        Springfield 
+ Golden321     Durgan499       F        Springfield 
+ Jerrie417     Gislason620     F        Springfield 
+ Venus149      Hodkiewicz467   F        Springfield 
+ Refugia211    Wintheiser220   F        Springfield 
+     ·            ·            ·          ·         
+     ·            ·            ·          ·         
+     ·            ·            ·          ·         
+ Freida957     Hand679         F        Boston      
+ Clora637      Rempel203       F        Boston      
+ Bonny428      Turner526       F        Boston      
+ Tien590       Gaylord332      F        Boston      
+ Arlyne429     Deckow585       F        Boston      
+ Doreen575     Johnson679      F        Boston      
+ Kym935        Hayes766        F        Boston      
+ Julia241      Nevárez403      F        Boston      
+ Ja391         Murray856       F        Boston      
+ Creola518     Spinka232       F        Boston      
+├──────────────┴────────────────┴─────────┴─────────────┤
+ 7191 rows (20 shown)                        4 columns 
+└───────────────────────────────────────────────────────┘
+

+

Alternative

+

conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE gender = 'F' AND (city IN ('Springfield','Boston'));")
+
+
┌──────────────┬────────────────┬─────────┬─────────────┐
+    FIRST           LAST       GENDER      CITY     
+   varchar        varchar      varchar    varchar   
+├──────────────┼────────────────┼─────────┼─────────────┤
+ Sabina296     Flatley871      F        Springfield 
+ Theodora872   Johnson679      F        Springfield 
+ Lavera253     Anderson154     F        Springfield 
+ Golden321     Pollich983      F        Springfield 
+ Georgiann138  Greenfelder433  F        Springfield 
+ Talisha682    Brakus656       F        Springfield 
+ Golden321     Durgan499       F        Springfield 
+ Jerrie417     Gislason620     F        Springfield 
+ Venus149      Hodkiewicz467   F        Springfield 
+ Refugia211    Wintheiser220   F        Springfield 
+     ·            ·            ·          ·         
+     ·            ·            ·          ·         
+     ·            ·            ·          ·         
+ Freida957     Hand679         F        Boston      
+ Clora637      Rempel203       F        Boston      
+ Bonny428      Turner526       F        Boston      
+ Tien590       Gaylord332      F        Boston      
+ Arlyne429     Deckow585       F        Boston      
+ Doreen575     Johnson679      F        Boston      
+ Kym935        Hayes766        F        Boston      
+ Julia241      Nevárez403      F        Boston      
+ Ja391         Murray856       F        Boston      
+ Creola518     Spinka232       F        Boston      
+├──────────────┴────────────────┴─────────┴─────────────┤
+ 7191 rows (20 shown)                        4 columns 
+└───────────────────────────────────────────────────────┘
+

+

Filter on missing data

+
    +
  • Filtering on missing data is crucial for maintaining data integrity and ensuring accurate analysis by identifying and handling incomplete records effectively.
  • +
  • Retrieve the first name, last name, gender, and city for all patients who are from Springfield and have a passport number recorded (i.e., the passport field is not empty).
  • +
+

conn.sql("SELECT FIRST, LAST, GENDER, CITY, PASSPORT FROM patients WHERE city = 'Springfield' AND PASSPORT IS NOT NULL;")
+
+
┌──────────────┬────────────────┬─────────┬─────────────┬────────────┐
+    FIRST           LAST       GENDER      CITY       PASSPORT  
+   varchar        varchar      varchar    varchar     varchar   
+├──────────────┼────────────────┼─────────┼─────────────┼────────────┤
+ Sabina296     Flatley871      F        Springfield  X85058581X 
+ Theodora872   Johnson679      F        Springfield  X21164602X 
+ Lavera253     Anderson154     F        Springfield  X83686992X 
+ Georgiann138  Greenfelder433  F        Springfield  X58134116X 
+ Talisha682    Brakus656       F        Springfield  X53645004X 
+ Golden321     Durgan499       F        Springfield  X49016634X 
+ Ty725         Schmeler639     M        Springfield  X22960735X 
+ Pilar644      Pouros728       F        Springfield  X21434326X 
+ Georgette866  Stark857        F        Springfield  X84034866X 
+ Annika454     Gutmann970      F        Springfield  X3275916X  
+     ·             ·           ·             ·           ·      
+     ·             ·           ·             ·           ·      
+     ·             ·           ·             ·           ·      
+ Isabel214     Camacho176      F        Springfield  X52173808X 
+ Carla633      Terán294        F        Springfield  X50212869X 
+ Marceline716  Kuhlman484      F        Springfield  X70432308X 
+ Ana María762  Cotto891        F        Springfield  X4604857X  
+ Josefina523   Vanegas191      F        Springfield  X85396222X 
+ Ester635      Sevilla788      F        Springfield  X50601786X 
+ Tomás404      Galarza986      M        Springfield  X76790663X 
+ Arturo47      Delafuente833   M        Springfield  X45087804X 
+ Gilberto712   Martínez540     M        Springfield  X89729075X 
+ Juanita470    Connelly992     F        Springfield  X71156217X 
+├──────────────┴────────────────┴─────────┴─────────────┴────────────┤
+ 2221 rows (20 shown)                                     5 columns 
+└────────────────────────────────────────────────────────────────────┘
+

+

Filter and select in numeric range

+

Patients in City = 'Springfield' where the HEALTHCARE_EXPENSES between 1.5M and 2M

+

conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients WHERE city = 'Springfield' AND HEALTHCARE_EXPENSES BETWEEN 1500000 AND 2000000;")
+
+
┌──────────────┬────────────────┬─────────┬─────────────┐
+    FIRST           LAST       GENDER      CITY     
+   varchar        varchar      varchar    varchar   
+├──────────────┼────────────────┼─────────┼─────────────┤
+ Talisha682    Brakus656       F        Springfield 
+ Jerold208     Harber290       M        Springfield 
+ Dean966       Tillman293      M        Springfield 
+ Orval846      Cartwright189   M        Springfield 
+ Jacinto644    Abernathy524    M        Springfield 
+ Bethel526     Satterfield305  F        Springfield 
+ Dorene845     Botsford977     F        Springfield 
+ Lula998       Langosh790      F        Springfield 
+ Deeanna316    Koss676         F        Springfield 
+ Loriann967    Torp761         F        Springfield 
+    ·             ·            ·             ·      
+    ·             ·            ·             ·      
+    ·             ·            ·             ·      
+ Ashley34      Murazik203      M        Springfield 
+ Ulysses632    Donnelly343     M        Springfield 
+ Horace32      Hammes673       M        Springfield 
+ Roman389      Lubowitz58      M        Springfield 
+ Jerald662     Grady603        M        Springfield 
+ Damien170     Hoppe518        M        Springfield 
+ Charley358    Vandervort697   M        Springfield 
+ Carla633      Terán294        F        Springfield 
+ Ana María762  Cotto891        F        Springfield 
+ Juanita470    Connelly992     F        Springfield 
+├──────────────┴────────────────┴─────────┴─────────────┤
+ 241 rows (20 shown)                         4 columns 
+└───────────────────────────────────────────────────────┘
+

+

LIMIT

+
    +
  • LIMIT specifies the maximum number of records the query will return.
  • +
+

conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients LIMIT 20;")
+
+
conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients LIMIT 20;")
+┌─────────────────┬────────────────┬─────────┬─────────────┐
+      FIRST            LAST       GENDER      CITY     
+     varchar         varchar      varchar    varchar   
+├─────────────────┼────────────────┼─────────┼─────────────┤
+ Jacinto644       Kris249         M        Springfield 
+ Alva958          Krajcik437      F        Walpole     
+ Jimmie93         Harris789       F        Pembroke    
+ Gregorio366      Auer97          M        Boston      
+ Karyn217         Mueller846      F        Colrain     
+ Jayson808        Fadel536        M        Chicopee    
+ José Eduardo181  Gómez206        M        Chicopee    
+ Milo271          Feil794         M        Somerville  
+ Karyn217         Metz686         F        Medfield    
+ Jeffrey461       Greenfelder433  M        Springfield 
+ Mariana775       Gulgowski816    F        Lowell      
+ Leann224         Deckow585       F        Needham     
+ Isabel214        Hinojosa147     F        Fall River  
+ Christal240      Brown30         F        Boston      
+ Carmelia328      Konopelski743   F        Ashland     
+ Raye931          Wyman904        F        Quincy      
+ Lisbeth69        Rowe323         F        Malden      
+ Amada498         Spinka232       F        Foxborough  
+ Cythia210        Reichel38       F        Peabody     
+ María Soledad68  Aparicio848     F        Boston      
+├─────────────────┴────────────────┴─────────┴─────────────┤
+ 20 rows                                        4 columns 
+└──────────────────────────────────────────────────────────┘
+

+

ORDER BY and ASC

+

This query returns the first 20 patients from the patients table, ordered alphabetically by their last name. +

conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients ORDER BY LAST ASC LIMIT 20;")
+
+
┌────────────────┬───────────┬─────────┬──────────────┐
+     FIRST         LAST     GENDER       CITY     
+    varchar       varchar   varchar    varchar    
+├────────────────┼───────────┼─────────┼──────────────┤
+ Jc393           Abbott774  M        Westfield    
+ Lloyd546        Abbott774  M        Northampton  
+ Charles364      Abbott774  M        Brockton     
+ Lelia627        Abbott774  F        Gloucester   
+ Devorah937      Abbott774  F        Cambridge    
+ Warren653       Abbott774  M        Tyngsborough 
+ Rhona164        Abbott774  F        Stoneham     
+ Jackqueline794  Abbott774  F        Randolph     
+ Willa615        Abbott774  F        Dartmouth    
+ Cyril535        Abbott774  M        Millis       
+ Lorette239      Abbott774  F        Dennis       
+ Jimmy858        Abbott774  M        Lowell       
+ Laine739        Abbott774  F        Agawam       
+ Bernetta267     Abbott774  F        Ware         
+ Lauri399        Abbott774  F        Springfield  
+ Miesha237       Abbott774  F        Stoneham     
+ Darrin898       Abbott774  M        Newton       
+ Grant908        Abbott774  M        Arlington    
+ Arden380        Abbott774  M        Worcester    
+ German382       Abbott774  M        Taunton      
+├────────────────┴───────────┴─────────┴──────────────┤
+ 20 rows                                   4 columns 
+└─────────────────────────────────────────────────────┘
+

+

ORDER BY and DESC

+

This query returns the first 20 patients from the patients table, ordered reverse alphabetically by their first name. +

conn.sql("SELECT FIRST, LAST, GENDER, CITY FROM patients ORDER BY FIRST DESC LIMIT 20;")
+
+
┌──────────┬────────────────┬─────────┬────────────┐
+  FIRST         LAST       GENDER      CITY    
+ varchar      varchar      varchar   varchar   
+├──────────┼────────────────┼─────────┼────────────┤
+ Óscar156  Ballesteros368  M        Taunton    
+ Óscar156  Curiel392       M        Swampscott 
+ Óscar156  Zelaya592       M        Lynn       
+ Óscar156  Puente961       M        Greenfield 
+ Óscar156  Olivas524       M        Norwell    
+ Óscar156  Rivero165       M        Lancaster  
+ Óscar156  Canales95       M        Hamilton   
+ Óscar156  Romero158       M        Boston     
+ Óscar156  Garza151        M        Longmeadow 
+ Óscar156  Delgado712      M        Haverhill  
+ Óscar156  Ureña88         M        Bedford    
+ Óscar156  Henríquez109    M        Boxborough 
+ Óscar156  Guerrero997     M        Boston     
+ Óscar156  Ojeda263        M        Boston     
+ Óscar156  Meléndez48      M        Newton     
+ Óscar156  Muro989         M        Boston     
+ Óscar156  Rendón540       M        Wilbraham  
+ Óscar156  Santacruz647    M        Winthrop   
+ Óscar156  Santacruz647    M        Boston     
+ Óscar156  Otero621        M        Boston     
+├──────────┴────────────────┴─────────┴────────────┤
+ 20 rows                                4 columns 
+└──────────────────────────────────────────────────┘
+

+

Aggregating Data using GROUP BY

+

Counting Patients by City

+

conn.sql("SELECT CITY, COUNT(*) AS patient_count FROM patients GROUP BY CITY;") ## each run gives random order
+
+
┌─────────────┬───────────────┐
+    CITY      patient_count 
+   varchar        int64     
+├─────────────┼───────────────┤
+ Dartmouth              656 
+ Fitchburg              743 
+ Plymouth               972 
+ Worcester             3263 
+ Beverly                778 
+ Westfield              781 
+ Hingham                431 
+ Rochester              103 
+ Sandwich               387 
+ Watertown              534 
+    ·                     · 
+    ·                     · 
+    ·                     · 
+ Ashfield                29 
+ Pelham                  26 
+ Hancock                 22 
+ Monterey                13 
+ Richmond                23 
+ Hinsdale                30 
+ Middlefield             11 
+ Westhampton             14 
+ Sandisfield             12 
+ Monroe                   2 
+├─────────────┴───────────────┤
+     351 rows (20 shown)     
+└─────────────────────────────┘
+

+

Counting Patients by City and sorting them highest to lowest

+

conn.sql("SELECT CITY, COUNT(*) AS patient_count FROM patients GROUP BY CITY ORDER BY patient_count DESC;")
+
+
┌─────────────┬───────────────┐
+    CITY      patient_count 
+   varchar        int64     
+├─────────────┼───────────────┤
+ Boston               11496 
+ Worcester             3263 
+ Springfield           2814 
+ Cambridge             2044 
+ Lowell                2027 
+ Brockton              1833 
+ Lynn                  1714 
+ New Bedford           1706 
+ Quincy                1698 
+ Newton                1695 
+   ·                      · 
+   ·                      · 
+   ·                      · 
+ Blandford               10 
+ Heath                    8 
+ Tolland                  7 
+ Hawley                   6 
+ Alford                   5 
+ New Ashford              4 
+ Aquinnah                 4 
+ Tyringham                3 
+ Monroe                   2 
+ Gosnold                  1 
+├─────────────┴───────────────┤
+     351 rows (20 shown)     
+└─────────────────────────────┘
+

+

Total Number of Patients by Gender in Each City

+

conn.sql("SELECT CITY, GENDER, COUNT(*) AS total_patients FROM patients GROUP BY CITY, GENDER ORDER BY total_patients DESC LIMIT 10;")
+
+
┌─────────────┬─────────┬────────────────┐
+    CITY      GENDER   total_patients 
+   varchar    varchar      int64      
+├─────────────┼─────────┼────────────────┤
+ Boston       F                  5840 
+ Boston       M                  5656 
+ Worcester    M                  1694 
+ Worcester    F                  1569 
+ Springfield  M                  1463 
+ Springfield  F                  1351 
+ Cambridge    M                  1023 
+ Cambridge    F                  1021 
+ Lowell       F                  1015 
+ Lowell       M                  1012 
+├─────────────┴─────────┴────────────────┤
+ 10 rows                      3 columns 
+└────────────────────────────────────────┘
+
+Lets look at other Tables - observations and immunizations +
conn.sql("SELECT PATIENT,ENCOUNTER,CODE, DESCRIPTION, VALUE  from observations;")
+
+
┌──────────────────────┬──────────────────────┬─────────┬───────────────────────────────────────────────┬──────────────┐
+       PATIENT              ENCOUNTER         CODE                     DESCRIPTION                      VALUE     
+       varchar               varchar         varchar                     varchar                       varchar    
+├──────────────────────┼──────────────────────┼─────────┼───────────────────────────────────────────────┼──────────────┤
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   8302-2   Body Height                                    82.7         
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   72514-3  Pain severity - 0-10 verbal numeric rating    2.0          
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   29463-7  Body Weight                                    11.5         
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   77606-2  Weight-for-length Per age and sex              47.0         
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   9843-4   Head Occipital-frontal circumference           46.9         
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   8462-4   Diastolic Blood Pressure                       76.0         
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   8480-6   Systolic Blood Pressure                        107.0        
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   8867-4   Heart rate                                     68.0         
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   9279-1   Respiratory rate                               13.0         
+ 1ff7f10f-a204-4bb1   52051c30-c6c3-45fe   72166-2  Tobacco smoking status NHIS                    Never smoker 
+          ·                     ·              ·                   ·                                  ·           
+          ·                     ·              ·                   ·                                  ·           
+          ·                     ·              ·                   ·                                  ·           
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   2160-0   Creatinine [Mass/volume] in Serum or Plasma    2.9          
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   17861-6  Calcium [Mass/volume] in Serum or Plasma       9.2          
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   2951-2   Sodium [Moles/volume] in Serum or Plasma       143.8        
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   2823-3   Potassium [Moles/volume] in Serum or Plasma    4.3          
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   2075-0   Chloride [Moles/volume] in Serum or Plasma     106.4        
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   2028-9   Carbon dioxide  total [Moles/volume] in Ser   22.9         
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   33914-3  Glomerular filtration rate/1.73 sq M.predic   9.9          
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   2885-2   Protein [Mass/volume] in Serum or Plasma       5.7          
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   1751-7   Albumin [Mass/volume] in Serum or Plasma       5.2          
+ 87537cb1-92e1-4a11   69e8eaf0-7714-4c20   1975-2   Bilirubin.total [Mass/volume] in Serum or P   14.2         
+├──────────────────────┴──────────────────────┴─────────┴───────────────────────────────────────────────┴──────────────┤
+ ? rows (>9999 rows, 20 shown)                                                                              5 columns 
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+

+

Average Heart Rate by Patient

+

conn.sql("SELECT PATIENT, ROUND(AVG(CAST(VALUE AS DECIMAL(10,6))), 2) AS average_heart_rate FROM observations WHERE DESCRIPTION LIKE '%Heart rate%' GROUP BY PATIENT ORDER BY average_heart_rate DESC LIMIT 20;")
+
+
┌──────────────────────────────────────┬────────────────────┐
+               PATIENT                 average_heart_rate 
+               varchar                       double       
+├──────────────────────────────────────┼────────────────────┤
+ ff2209f2-a4c9-4737-9aa7-adc8d71b6961               200.0 
+ 8cea2d7e-6227-4924-a340-18eafb8564ac               200.0 
+ 6ec823e7-839e-4491-8b6f-6b275951b456               200.0 
+ 0191f8aa-96dc-41e6-b718-312686a7a867               200.0 
+ c10db4e1-de51-495f-98ab-8322c6d9550a               200.0 
+ 46ea7226-63f6-404f-bbed-904ea39f706d               199.9 
+ ec1ce32c-57a3-44c4-8f64-e050f2f94e03               199.9 
+ a05e3bd1-1c51-453a-9969-646cb0168d23               199.9 
+ 9cc52681-4446-4e4a-80be-63418dd06e66               199.9 
+ b0112890-a24a-4564-b2b3-9b77879e5e79               199.9 
+ 18f53179-2449-47af-b4da-a9e51095076f               199.9 
+ c3d165d2-597e-45a8-8331-f3338ce19fdf               199.9 
+ 04dd3439-2116-4f9a-b40e-a84395cf6f21               199.9 
+ 371e14a1-31bd-4407-bdbb-4078781fde05               199.8 
+ 3e5c63bf-65fe-48bb-8361-6b21a909888a               199.8 
+ 423f79cf-f7b8-41cd-bb3c-8459084f88eb               199.8 
+ 2d4f57ee-4001-44f8-9a94-c1885f26b408               199.8 
+ bd6784c0-3529-4a12-87bd-746affb17739               199.8 
+ ef43848b-a4f1-4c45-a106-5928e29b72f2               199.7 
+ 1d050245-097c-44a5-b29b-6a18a22731e9               199.7 
+├──────────────────────────────────────┴────────────────────┤
+ 20 rows                                         2 columns 
+└───────────────────────────────────────────────────────────┘
+

+

Count of Heart Rate Records by Patient

+

conn.sql("SELECT PATIENT, COUNT(*) AS heart_rate_records FROM observations WHERE DESCRIPTION LIKE '%Heart rate%' GROUP BY PATIENT ORDER BY heart_rate_records DESC LIMIT 20;")
+
+
┌──────────────────────────────────────┬────────────────────┐
+               PATIENT                 heart_rate_records 
+               varchar                       int64        
+├──────────────────────────────────────┼────────────────────┤
+ 3e21a156-da54-4fb7-815e-550fdf4afbbd                  33 
+ ffbf0392-1643-4b05-819f-489072c8c4d4                  30 
+ b9ef6005-438e-4b47-afaf-9dba32184adf                  30 
+ f8080701-2b5c-4128-9a7f-9a098b737b27                  30 
+ a2172279-3d63-4d14-867d-1a39d9280690                  30 
+ 01a5a5a6-ef7b-42ba-899c-de66e1b1e27e                  30 
+ 62572c44-a802-40d1-8d60-d02bc287d548                  30 
+ 445953fd-15fa-424e-9926-f93ebf1bca7a                  30 
+ c3ea3c46-f8d1-4abe-8fb2-8fc8f86b4ef4                  29 
+ bd18ba0d-2e65-4427-98f1-cbfc09dbaa33                  29 
+ 5736b489-0e15-4693-b9d6-05192a702a2d                  29 
+ b5a0b060-96ae-44d5-bd10-f532a445009b                  29 
+ ee9b44a1-36c6-456e-b77c-cd200611ca0f                  29 
+ 0d5290dd-a3e3-4a81-91d5-6e722e58ec3e                  29 
+ 54ec8b8e-fe2a-4cf5-a087-1a65df1ab1b8                  29 
+ e658626d-0f72-4570-8071-2dd63827a24c                  29 
+ ec3f3da2-09a1-43c7-8cae-ed34847d2534                  28 
+ c7bd54b1-e036-4702-9062-acfa3677a6e8                  28 
+ 8b6ee161-eb5f-413a-8e86-f5b3adb3f5ff                  28 
+ 03c1210e-d145-4aed-9522-14cff41b3c28                  28 
+├──────────────────────────────────────┴────────────────────┤
+ 20 rows                                         2 columns 
+└───────────────────────────────────────────────────────────┘
+

+

Total Cost of Vaccinations per Patient

+

conn.sql("SELECT PATIENT, SUM(BASE_COST) AS total_cost FROM immunizations GROUP BY PATIENT ORDER BY total_cost DESC;")
+
+
┌──────────────────────────────────────┬────────────┐
+               PATIENT                 total_cost 
+               varchar                   double   
+├──────────────────────────────────────┼────────────┤
+ 8eacdc17-de2f-4024-88bd-976401b30979      2810.4 
+ bb14de6a-c77d-44bb-a3a8-338a6a246520      2810.4 
+ a5b60d43-9776-4811-badf-67d49edbc175      2810.4 
+ 1c8a4026-5dbe-478a-b647-ecb605775977      2810.4 
+ 3e38e20b-6dd8-44fe-9ed3-81e1e3d8e2ab      2810.4 
+ e23c7c68-0319-41f5-90d9-1db160321a94      2810.4 
+ 49e7d878-93a6-4e71-971b-213abacf2235      2810.4 
+ 7ded4116-1783-4a93-9bb2-7a65370f55f5      2810.4 
+ 594660e4-6a02-4e3c-a8b9-2c61d4d4af60      2810.4 
+ fee1edbd-277d-4b12-901e-60b6135cf877      2810.4 
+                  ·                           ·   
+                  ·                           ·   
+                  ·                           ·   
+ 594d1ecb-b1a4-4bad-9ccd-5cebceebd6f5      281.04 
+ 456fa9f9-7eb7-4316-a74b-5a9ff87f8fc5      281.04 
+ 52568520-7163-429d-bc90-fc245debb697      281.04 
+ 30a16a19-ee93-4bfd-b1ed-bba90de36266      281.04 
+ 0ef633b0-2996-44c5-936c-600820310223      281.04 
+ 613319c8-2b76-494b-86b8-e776a2ee5e0b      281.04 
+ 4dceeb57-4fe3-4c32-a748-621df5ce3c30      281.04 
+ 93d590e8-9f3e-4741-bee2-9b8e9e8b21e7      281.04 
+ bad20ffc-48f9-4a7d-a2be-39a50fa4e0ac      281.04 
+ 3cf75ec3-5e1c-4783-bb20-282a8cf66f30      281.04 
+├──────────────────────────────────────┴────────────┤
+ ? rows (>9999 rows, 20 shown)           2 columns 
+└───────────────────────────────────────────────────┘
+

+

Top 10 Most Common Vaccine Administered

+

conn.sql("SELECT DESCRIPTION, COUNT(*) AS count FROM immunizations GROUP BY DESCRIPTION ORDER BY count DESC LIMIT 10;")
+
+
┌────────────────────────────────────────────────────┬────────┐
+                    DESCRIPTION                      count  
+                      varchar                        int64  
+├────────────────────────────────────────────────────┼────────┤
+ Influenza  seasonal  injectable  preservative free  106564 
+ Td (adult) preservative free                          9815 
+ Pneumococcal conjugate PCV 13                         5747 
+ DTaP                                                  5735 
+ IPV                                                   4962 
+ meningococcal MCV4P                                   4010 
+ Hib (PRP-OMP)                                         3615 
+ HPV  quadrivalent                                     3494 
+ Hep B  adolescent or pediatric                        3490 
+ zoster                                                3469 
+├────────────────────────────────────────────────────┴────────┤
+ 10 rows                                           2 columns 
+└─────────────────────────────────────────────────────────────┘
+

+

JOINS

+ +

SQL JOINS + - credit: educba.com

+
+

For the following examples: Left table: Patients and Right Table: Immunizations

+

__

+

INNER JOIN

+
    +
  • +

    The Inner join returns rows when there is at least one match in both tables. If there is no match, the rows are not returned.

    +
  • +
  • +

    Find Matching Records: Question: Which medical treatments have been administered to patients, including the patient's name and the cost of each treatment? +

    conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM immunizations INNER JOIN patients ON immunizations.PATIENT = patients.Id ORDER BY patients.FIRST ASC;")
    +
    +
    ┌──────────┬───────────────┬────────────────────────────────────────────────────┬───────────┐
    +  FIRST        LAST                          DESCRIPTION                      BASE_COST 
    + varchar      varchar                          varchar                         double   
    +├──────────┼───────────────┼────────────────────────────────────────────────────┼───────────┤
    + Aaron697  Cummings51     Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697  Crooks415      Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697  Anderson154    Pneumococcal conjugate PCV 13                          140.52 
    + Aaron697  Hartmann983    Hep B  adolescent or pediatric                         140.52 
    + Aaron697  Thompson596    Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697  Rodriguez71    Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697  Deckow585      Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697  Swaniawski813  Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697  Beer512        Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697  Ullrich385     Influenza  seasonal  injectable  preservative free     140.52 
    +    ·          ·                                  ·                                 ·   
    +    ·          ·                                  ·                                 ·   
    +    ·          ·                                  ·                                 ·   
    + Ariel183  Schuster709    Influenza  seasonal  injectable  preservative free     140.52 
    + Ariel183  Mohr916        Influenza  seasonal  injectable  preservative free     140.52 
    + Ariel183  Waters156      Influenza  seasonal  injectable  preservative free     140.52 
    + Ariel183  Will178        Influenza  seasonal  injectable  preservative free     140.52 
    + Ariel183  Leffler128     Influenza  seasonal  injectable  preservative free     140.52 
    + Ariel183  Bode78         Influenza  seasonal  injectable  preservative free     140.52 
    + Ariel183  Grady603       Influenza  seasonal  injectable  preservative free     140.52 
    + Ariel183  Funk324        Influenza  seasonal  injectable  preservative free     140.52 
    + Ariel183  McGlynn426     Td (adult) preservative free                           140.52 
    + Ariel183  Boehm581       Influenza  seasonal  injectable  preservative free     140.52 
    +├──────────┴───────────────┴────────────────────────────────────────────────────┴───────────┤
    + ? rows (>9999 rows, 20 shown)                                                   4 columns 
    +└───────────────────────────────────────────────────────────────────────────────────────────┘
    +

    +
  • +
+

LEFT JOIN

+
    +
  • It returns all rows from the left table, and the matched rows from the right table. The result is NULL from the right side, if there is no match. +Question: For all patients, what treatments have they received, if any? +
    conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM patients LEFT JOIN immunizations ON patients.Id = immunizations.PATIENT ORDER BY patients.FIRST ASC;")
    +
    +
    conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM patients LEFT JOIN immunizations ON patients.Id = immunizations.PATIENT ORDER BY patients.FIRST ASC;")
    +┌───────────┬─────────────┬────────────────────────────────────────────────────┬───────────┐
    +   FIRST       LAST                         DESCRIPTION                      BASE_COST 
    +  varchar     varchar                         varchar                         double   
    +├───────────┼─────────────┼────────────────────────────────────────────────────┼───────────┤
    + Aaron697   Legros616    Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697   Deckow585    Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697   Thompson596  Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697   Auer97       Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697   Crooks415    Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697   Crooks415    Td (adult) preservative free                           140.52 
    + Aaron697   Volkman526   Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697   Hartmann983  Influenza  seasonal  injectable  preservative free     140.52 
    + Aaron697   Orn563       NULL                                                     NULL 
    + Aaron697   Hartmann983  IPV                                                    140.52 
    +    ·           ·         ·                                                        ·   
    +    ·           ·         ·                                                        ·   
    +    ·           ·         ·                                                        ·   
    + Antonia30  Goodwin327   HPV  quadrivalent                                      140.52 
    + Antonia30  Funk324      Influenza  seasonal  injectable  preservative free     140.52 
    + Antonia30  West559      Influenza  seasonal  injectable  preservative free     140.52 
    + Antonia30  Lowe577      NULL                                                     NULL 
    + Antonia30  D'Amore443  │ Pneumococcal conjugate PCV 13                      │    140.52 │
    + Antonia30  Montes106    meningococcal MCV4P                                    140.52 
    + Antonia30  González124  Influenza  seasonal  injectable  preservative free     140.52 
    + Antonia30  Franecki195  zoster                                                 140.52 
    + Antonia30  Rojo930      Pneumococcal conjugate PCV 13                          140.52 
    + Antonia30  Puente961    HPV  quadrivalent                                      140.52 
    +├───────────┴─────────────┴────────────────────────────────────────────────────┴───────────┤
    + ? rows (>9999 rows, 20 shown)                                                  4 columns 
    +└──────────────────────────────────────────────────────────────────────────────────────────┘
    +
  • +
+

RIGHT JOIN

+
    +
  • It returns all rows from the right table, and the matched rows from the left table. The result is NULL from the left side, if there is no match. +Question: For all Immunizations recorded, which patients received them, if identifiable?
  • +
+

conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM patients RIGHT JOIN immunizations ON patients.Id = immunizations.PATIENT ORDER BY immunizations.DESCRIPTION;")
+
+
┌────────────────┬──────────────┬──────────────┬───────────┐
+     FIRST           LAST      DESCRIPTION   BASE_COST 
+    varchar        varchar       varchar      double   
+├────────────────┼──────────────┼──────────────┼───────────┤
+ Shantay950      Quitzon246    DTaP             140.52 
+ Cleveland582    VonRueden376  DTaP             140.52 
+ Flo729          Quigley282    DTaP             140.52 
+ Flo729          Quigley282    DTaP             140.52 
+ Werner409       Schaden604    DTaP             140.52 
+ Pasty639        Ortiz186      DTaP             140.52 
+ Pasty639        Ortiz186      DTaP             140.52 
+ Angelica442     Kovacek682    DTaP             140.52 
+ Cathie710       Hegmann834    DTaP             140.52 
+ Pasty639        Ortiz186      DTaP             140.52 
+   ·                ·           ·                  ·   
+   ·                ·           ·                  ·   
+   ·                ·           ·                  ·   
+ Art115          Roberts511    Hep A  adult     140.52 
+ Christopher407  Davis923      Hep A  adult     140.52 
+ Michiko564      Dooley940     Hep A  adult     140.52 
+ Celine582       Sipes176      Hep A  adult     140.52 
+ Darci883        Miller503     Hep A  adult     140.52 
+ Ethan766        Morar593      Hep A  adult     140.52 
+ Georgiann138    Heathcote539  Hep A  adult     140.52 
+ Herbert830      Wolff180      Hep A  adult     140.52 
+ Ismael683       King743       Hep A  adult     140.52 
+ Ellsworth48     Mertz280      Hep A  adult     140.52 
+├────────────────┴──────────────┴──────────────┴───────────┤
+ ? rows (>9999 rows, 20 shown)                  4 columns 
+└──────────────────────────────────────────────────────────┘
+

+

FULL OUTER JOIN

+
    +
  • It returns rows when there is a match in one of the tables. It effectively combines the results of both LEFT JOIN and RIGHT JOIN. +Question: What is the complete list of patients and their Immunizations, including those without recorded treatments or identifiable patients?
  • +
+

conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION, immunizations.BASE_COST FROM patients FULL OUTER JOIN immunizations ON patients.Id = immunizations.PATIENT ORDER BY patients.FIRST ASC;")
+
+
┌───────────┬─────────────┬────────────────────────────────────────────────────┬───────────┐
+   FIRST       LAST                         DESCRIPTION                      BASE_COST 
+  varchar     varchar                         varchar                         double   
+├───────────┼─────────────┼────────────────────────────────────────────────────┼───────────┤
+ Aaron697   Legros616    Influenza  seasonal  injectable  preservative free     140.52 
+ Aaron697   Deckow585    Influenza  seasonal  injectable  preservative free     140.52 
+ Aaron697   Thompson596  Influenza  seasonal  injectable  preservative free     140.52 
+ Aaron697   Auer97       Influenza  seasonal  injectable  preservative free     140.52 
+ Aaron697   Crooks415    Influenza  seasonal  injectable  preservative free     140.52 
+ Aaron697   Crooks415    Td (adult) preservative free                           140.52 
+ Aaron697   Volkman526   Influenza  seasonal  injectable  preservative free     140.52 
+ Aaron697   Hartmann983  Influenza  seasonal  injectable  preservative free     140.52 
+ Aaron697   Orn563       NULL                                                     NULL 
+ Aaron697   Hartmann983  IPV                                                    140.52 
+    ·           ·         ·                                                        ·   
+    ·           ·         ·                                                        ·   
+    ·           ·         ·                                                        ·   
+ Antonia30  Goodwin327   HPV  quadrivalent                                      140.52 
+ Antonia30  Funk324      Influenza  seasonal  injectable  preservative free     140.52 
+ Antonia30  West559      Influenza  seasonal  injectable  preservative free     140.52 
+ Antonia30  Lowe577      NULL                                                     NULL 
+ Antonia30  D'Amore443  │ Pneumococcal conjugate PCV 13                      │    140.52 │
+ Antonia30  Montes106    meningococcal MCV4P                                    140.52 
+ Antonia30  González124  Influenza  seasonal  injectable  preservative free     140.52 
+ Antonia30  Franecki195  zoster                                                 140.52 
+ Antonia30  Rojo930      Pneumococcal conjugate PCV 13                          140.52 
+ Antonia30  Puente961    HPV  quadrivalent                                      140.52 
+├───────────┴─────────────┴────────────────────────────────────────────────────┴───────────┤
+ ? rows (>9999 rows, 20 shown)                                                  4 columns 
+└──────────────────────────────────────────────────────────────────────────────────────────┘
+

+

CROSS JOIN

+
    +
  • It returns a Cartesian product of the two tables, i.e., it returns rows combining each row from the first table with each row from the second table. +Question: What are all possible combinations of patients and immunizations? +
    conn.sql("SELECT patients.FIRST, patients.LAST, immunizations.DESCRIPTION FROM patients CROSS JOIN immunizations;")
    +
    +
    ┌───────────┬─────────────┬────────────────────────────────────────────────────┐
    +   FIRST       LAST                         DESCRIPTION                     
    +  varchar     varchar                         varchar                       
    +├───────────┼─────────────┼────────────────────────────────────────────────────┤
    + Tammy740   Ernser583    Influenza  seasonal  injectable  preservative free 
    + Tammy740   Ernser583    Hep A  ped/adol  2 dose                            
    + Tammy740   Ernser583    Influenza  seasonal  injectable  preservative free 
    + Tammy740   Ernser583    Influenza  seasonal  injectable  preservative free 
    + Tammy740   Ernser583    meningococcal MCV4P                                
    + Tammy740   Ernser583    Hep B  adolescent or pediatric                     
    + Tammy740   Ernser583    Hep B  adolescent or pediatric                     
    + Tammy740   Ernser583    Hib (PRP-OMP)                                      
    + Tammy740   Ernser583    rotavirus  monovalent                              
    + Tammy740   Ernser583    IPV                                                
    +    ·           ·         ·                                                 
    +    ·           ·         ·                                                 
    +    ·           ·         ·                                                 
    + Iliana226  Schmeler639  Influenza  seasonal  injectable  preservative free 
    + Iliana226  Schmeler639  zoster                                             
    + Iliana226  Schmeler639  Influenza  seasonal  injectable  preservative free 
    + Iliana226  Schmeler639  Influenza  seasonal  injectable  preservative free 
    + Iliana226  Schmeler639  Influenza  seasonal  injectable  preservative free 
    + Iliana226  Schmeler639  Influenza  seasonal  injectable  preservative free 
    + Iliana226  Schmeler639  Influenza  seasonal  injectable  preservative free 
    + Iliana226  Schmeler639  Influenza  seasonal  injectable  preservative free 
    + Iliana226  Schmeler639  Influenza  seasonal  injectable  preservative free 
    + Iliana226  Schmeler639  Influenza  seasonal  injectable  preservative free 
    +├───────────┴─────────────┴────────────────────────────────────────────────────┤
    + ? rows (>9999 rows, 20 shown)                                      3 columns 
    +└──────────────────────────────────────────────────────────────────────────────┘
    +
  • +
+

SELF JOIN

+
    +
  • It is not a different type of join, but it's a regular join used to join a table to itself. It's useful for queries where you need to compare rows within the same table. +Question: Which pair of patients are from the same city? +
    conn.sql("SELECT A.FIRST AS FirstPatient, B.FIRST AS SecondPatient, A.CITY FROM patients A, patients B WHERE A.CITY = B.CITY AND A.Id != B.Id ORDER BY A.CITY ASC;")
    +
    +
    ┌──────────────┬───────────────┬──────────┐
    + FirstPatient  SecondPatient    CITY   
    +   varchar        varchar     varchar  
    +├──────────────┼───────────────┼──────────┤
    + Susan422      Stefani254     Abington 
    + Jennell254    Lionel365      Abington 
    + Robbie31      Antonio44      Abington 
    + Joe656        Antonio44      Abington 
    + Susan422      Stevie682      Abington 
    + Shaun461      Stevie682      Abington 
    + Shaun461      Weston546      Abington 
    + Susan422      Elwood28       Abington 
    + Susan422      Daria61        Abington 
    + Shaun461      Lita714        Abington 
    +    ·             ·              ·     
    +    ·             ·              ·     
    +    ·             ·              ·     
    + Elane105      Conrad619      Abington 
    + Logan497      Conrad619      Abington 
    + Sherlene302   Conrad619      Abington 
    + Milan77       Conrad619      Abington 
    + Frankie174    Seema671       Abington 
    + Elane105      Josefina523    Abington 
    + Lisha487      Shawanna357    Abington 
    + Marleen824    Marcelino726   Abington 
    + Sandi885      Lionel365      Abington 
    + Morton637     Viva686        Abington 
    +├──────────────┴───────────────┴──────────┤
    +      ? rows (>9999 rows, 20 shown)      
    +└─────────────────────────────────────────┘
    +
  • +
+
+
+

Introduction to Subqueries and Nested Selects

+
    +
  • Subqueries, also known as inner queries or nested queries, are SQL queries nested inside a larger query. They allow you to perform operations that usually require multiple steps in a single query, making your data retrieval process more efficient and concise. Nested selects are a type of subquery used specifically within the SELECT, FROM, or WHERE clauses to provide a dataset for the outer query to process.
  • +
  • Subqueries can return a single value, a single row, multiple rows, or a table. They are used for comparison, as a condition, or to provide a list of values for the outer query. The main distinction between correlated and non-correlated subqueries is that correlated subqueries reference column(s) from the outer query, thus running once for each row selected by the outer query, while non-correlated subqueries run independently of the outer query and can be run as standalone queries.
  • +
+

Types of subqueries

+
    +
  • Single-Row Subqueries: Single-row subqueries return only one row and are used with single row comparison operators like =, >, <, >=, <=. They are often used in the WHERE clause to compare a column value against the result of the subquery.
  • +
  • Multi-Row Subqueries: Multi-row subqueries return more than one row and are used with operators like IN, ANY, ALL, which allow comparison against multiple values. They're useful for filtering based on a set of criteria returned by the subquery.
  • +
  • Correlated Subqueries: Correlated subqueries reference column(s) from the outer query, making them dependent on the outer query. They are executed once for each row processed by the outer query, often leading to performance considerations.
  • +
+
+

Use subqueries to find patients based on specific criteria.

+

Find all patients who have been prescribed medication with a base cost higher than the average base cost of all medications.

+

query = """SELECT p.FIRST, p.LAST
+FROM patients p
+WHERE p.Id IN ( SELECT m.PATIENT FROM medications m WHERE m.BASE_COST > (SELECT AVG(BASE_COST) FROM medications));
+"""
+conn.sql(query)
+
+
┌─────────────┬─────────────┐
+    FIRST        LAST     
+   varchar      varchar   
+├─────────────┼─────────────┤
+ Dewitt635    Reichel38   
+ Anisa442     Purdy2      
+ Ross213      Mayert710   
+ Jim478       Mueller846  
+ Maria750     Schimmel440 
+ Ignacio928   Gorczany269 
+ Kip442       Zboncak558  
+ Jean712      Kuhlman484  
+ Mac103       Moen819     
+ Dianna917    Goldner995  
+     ·           ·        
+     ·           ·        
+     ·           ·        
+ Cameron381   Bogan287    
+ Andrew29     Donnelly343 
+ Charis952    Littel644   
+ Lyndon118    Swift555    
+ Edward499    Zieme486    
+ Myron933     Ritchie586  
+ Yolonda722   Champlin946 
+ Alayna598    Kozey370    
+ Kristian973  Ledner144   
+ Sydney660    Zulauf375   
+├─────────────┴─────────────┤
+ ? rows          2 columns 
+└───────────────────────────┘
+

+

Correlated Subqueries

+

Learn how to use correlated subqueries to perform row-specific comparisons. +Find patients whose healthcare expenses are higher than the average expenses in their county. +

query = """SELECT
+    p.FIRST,
+    p.LAST,
+    p.COUNTY,
+    p.HEALTHCARE_EXPENSES,
+    ROUND(AVG_EXPENSES.COUNTY_AVG_EXPENSES, 2) AS ROUNDED_AVG_HEALTHCARE_EXPENSES
+FROM
+    patients p
+INNER JOIN (
+    SELECT
+        COUNTY,
+        AVG(CAST(HEALTHCARE_EXPENSES AS DECIMAL(20,6))) AS COUNTY_AVG_EXPENSES
+    FROM
+        patients
+    GROUP BY
+        COUNTY
+) AS AVG_EXPENSES ON p.COUNTY = AVG_EXPENSES.COUNTY
+WHERE
+    p.HEALTHCARE_EXPENSES > (
+        SELECT AVG(p2.HEALTHCARE_EXPENSES)
+        FROM patients p2
+        WHERE p2.COUNTY = p.COUNTY
+    );
+"""
+conn.sql(query)
+
+
query = """SELECT
+    p.FIRST,
+    p.LAST,
+    p.COUNTY,
+    p.HEALTHCARE_EXPENSES,
+    ROUND(AVG_EXPENSES.COUNTY_AVG_EXPENSES, 2) AS ROUNDED_AVG_HEALTHCARE_EXPENSES
+FROM
+    patients p
+INNER JOIN (
+    SELECT
+…        FROM patients p2
+        WHERE p2.COUNTY = p.COUNTY
+    );
+"""
+conn.sql(query)
+┌────────────┬───────────────┬───────────────────┬─────────────────────┬─────────────────────────────────┐
+   FIRST         LAST            COUNTY        HEALTHCARE_EXPENSES  ROUNDED_AVG_HEALTHCARE_EXPENSES 
+  varchar       varchar          varchar             double                     double              
+├────────────┼───────────────┼───────────────────┼─────────────────────┼─────────────────────────────────┤
+ Tammy740    Ernser583      Bristol County              1546025.67                        793296.73 
+ Iliana226   Schmeler639    Barnstable County           1407960.93                        949155.23 
+ Anthony633  Yundt842       Essex County                1575731.48                         814437.7 
+ Jim478      Mueller846     Bristol County              1112473.78                        793296.73 
+ Sina65      Howell947      Middlesex County            1479425.58                        841491.73 
+ Maria750    Schimmel440    Plymouth County              952814.55                         856778.1 
+ Lorenzo669  Vandervort697  Middlesex County             873001.47                        841491.73 
+ David908    Carter549      Worcester County             834576.02                        804752.74 
+ Noah480     O'Reilly797   │ Essex County      │            915110.6 │                        814437.7 │
+ Kip442      Zboncak558     Middlesex County    1704103.7200000002                        841491.73 
+   ·             ·                ·                          ·                                ·     
+   ·             ·                ·                          ·                                ·     
+   ·             ·                ·                          ·                                ·     
+ Tinisha932  Zulauf375      Bristol County              1035244.36                        793296.73 
+ Mario764    Estrada938     Suffolk County               1380401.5                        664340.79 
+ Glenn0      Schulist381    Barnstable County            1357123.8                        949155.23 
+ Porter490   Schulist381    Bristol County              1534216.57                        793296.73 
+ Darius626   Hackett68      Essex County                1715140.52                         814437.7 
+ Leonel449   Kunze215       Essex County                1492352.86                         814437.7 
+ Tyrell880   Kassulke119    Hampden County              1053520.21                        725636.71 
+ Arlen68     Gusikowski974  Worcester County             1363289.9                        804752.74 
+ Ashli227    Huel628        Worcester County             813730.02                        804752.74 
+ Loriann967  Mertz280       Norfolk County              1618397.57                        874351.83 
+├────────────┴───────────────┴───────────────────┴─────────────────────┴─────────────────────────────────┤
+ ? rows (>9999 rows, 20 shown)                                                                5 columns 
+└────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+Identify patients who have not been prescribed any medication. +
query = """
+SELECT p.FIRST, p.LAST
+FROM patients p
+WHERE NOT EXISTS (
+    SELECT 1 FROM medications m WHERE m.PATIENT = p.Id
+);
+"""
+
+conn.sql(query)
+
+
query = """
+SELECT p.FIRST, p.LAST
+FROM patients p
+WHERE NOT EXISTS (
+    SELECT 1 FROM medications m WHERE m.PATIENT = p.Id
+);
+"""
+
+conn.sql(query)
+┌────────────┬───────────────┐
+   FIRST         LAST      
+  varchar       varchar    
+├────────────┼───────────────┤
+ Iliana226   Schmeler639   
+ Toshiko149  Swaniawski813 
+ Heath320    Streich926    
+ Rana586     Langworth352  
+ Dorathy429  Yost751       
+ Deon400     Littel644     
+ Emerson869  D'Amore443    │
+ Alec433     Sanford861    
+ Roselyn270  McLaughlin530 
+ Claudia969  Smith67       
+   ·            ·          
+   ·            ·          
+   ·            ·          
+ Lou594      Gleason633    
+ Hank686     Russel238     
+ Earl438     Block661      
+ Mariano761  Heller342     
+ Sandra485   Fisher429     
+ Claire652   Murray856     
+ Doreen575   Graham902     
+ Fausto876   Kuhn96        
+ Vernon254   Gutmann970    
+ Ernest565   Block661      
+├────────────┴───────────────┤
+ ? rows           2 columns 
+└────────────────────────────┘
+
+Find patients whose healthcare coverage is above the average of their respective city. +
query = """
+SELECT
+    p.FIRST,
+    p.LAST,
+    p.CITY,
+    ROUND(p.HEALTHCARE_COVERAGE,2) AS INDIVIDUAL_COVERAGE,
+    ROUND(c.AVG_COVERAGE,2) AS CITY_AVERAGE_COVERAGE
+FROM
+    patients p
+INNER JOIN (
+    SELECT
+        CITY,
+        AVG(HEALTHCARE_COVERAGE) AS AVG_COVERAGE
+    FROM
+        patients
+    GROUP BY
+        CITY
+) AS c ON p.CITY = c.CITY
+WHERE
+    p.HEALTHCARE_COVERAGE > (
+        SELECT AVG(HEALTHCARE_COVERAGE)
+        FROM patients
+        WHERE CITY = p.CITY
+    );
+"""
+conn.sql(query)
+
+
query = """
+SELECT
+    p.FIRST,
+    p.LAST,
+    p.CITY,
+    ROUND(p.HEALTHCARE_COVERAGE,2) AS INDIVIDUAL_COVERAGE,
+    ROUND(c.AVG_COVERAGE,2) AS CITY_AVERAGE_COVERAGE
+FROM
+    patients p
+INNER JOIN (
+…        SELECT AVG(HEALTHCARE_COVERAGE)
+        FROM patients
+        WHERE CITY = p.CITY
+    );
+"""
+conn.sql(query)
+┌──────────────┬────────────────┬───────────────┬─────────────────────┬───────────────────────┐
+    FIRST           LAST           CITY       INDIVIDUAL_COVERAGE  CITY_AVERAGE_COVERAGE 
+   varchar        varchar         varchar           double                double         
+├──────────────┼────────────────┼───────────────┼─────────────────────┼───────────────────────┤
+ Anisa442      Purdy2          Methuen                   34393.57               12949.31 
+ Sina65        Howell947       Sudbury                   15481.43               15385.85 
+ Maria750      Schimmel440     Plymouth                  14231.22               10826.53 
+ Kip442        Zboncak558      Hopkinton                155478.44               15439.17 
+ Hans694       Wilkinson796    Framingham                13729.73               11988.99 
+ Jean712       Kuhlman484      Boxford                   11421.48                8660.53 
+ Lorette239    Abbott774       Dennis                    78740.23               14918.28 
+ Tobi258       Bernier607      Wilmington                 13889.6                9411.52 
+ Rosia390      Reichel38       Rochester                 16234.29                9736.74 
+ Florencia449  Mercado213      Wilbraham                 11427.25               10468.26 
+   ·              ·               ·                          ·                      ·    
+   ·              ·               ·                          ·                      ·    
+   ·              ·               ·                          ·                      ·    
+ Jc393         Marks830        Brockton                  16284.35               12287.77 
+ Cedric746     Marvin195       Dedham                    19806.72               13840.99 
+ Milford485    Schmitt836      Hubbardston               10592.34                8172.48 
+ Tegan755      Ruecker817      Waltham                   30493.65               14495.07 
+ Lindsay928    Lang846         Boston                    11837.05                10725.8 
+ Claudie965    Hermann103      Medford                   15420.54               12610.05 
+ Julie245      Nolan344        Essex                     31903.53               31787.13 
+ Mui729        Stoltenberg489  Wakefield                 21528.91               13699.32 
+ Oliver401     Lesch175        Yarmouth                  27442.87               19907.83 
+ Mi492         Gusikowski974   North Andover             18481.09               17677.13 
+├──────────────┴────────────────┴───────────────┴─────────────────────┴───────────────────────┤
+ ? rows (>9999 rows, 20 shown)                                                     5 columns 
+└─────────────────────────────────────────────────────────────────────────────────────────────┘
+

+

FUNCTIONS IN SQL

+
+

String Functions

+

CONCAT: Concatenates two or more strings.

+

query = """SELECT CONCAT(FIRST, ' ', LAST) AS FullName FROM patients LIMIT 10;
+;"""
+conn.sql(query)
+
+
┌───────────────────────────┐
+         FullName          
+          varchar          
+├───────────────────────────┤
+ Jacinto644 Kris249        
+ Alva958 Krajcik437        
+ Jimmie93 Harris789        
+ Gregorio366 Auer97        
+ Karyn217 Mueller846       
+ Jayson808 Fadel536        
+ José Eduardo181 Gómez206  
+ Milo271 Feil794           
+ Karyn217 Metz686          
+ Jeffrey461 Greenfelder433 
+├───────────────────────────┤
+          10 rows          
+└───────────────────────────┘
+

+

DATALENGTH: Returns the number of bytes used to represent any expre

+

query = """
+SELECT FIRST, LENGTH(FIRST) AS LengthInBytes FROM patients LIMIT 10;
+"""
+
+conn.sql(query)
+
+
query = """
+SELECT FIRST, LENGTH(FIRST) AS LengthInBytes FROM patients LIMIT 10;
+"""
+
+conn.sql(query)
+┌─────────────────┬───────────────┐
+      FIRST       LengthInBytes 
+     varchar          int64     
+├─────────────────┼───────────────┤
+ Jacinto644                  10 
+ Alva958                      7 
+ Jimmie93                     8 
+ Gregorio366                 11 
+ Karyn217                     8 
+ Jayson808                    9 
+ José Eduardo181             15 
+ Milo271                      7 
+ Karyn217                     8 
+ Jeffrey461                  10 
+├─────────────────┴───────────────┤
+ 10 rows               2 columns 
+└─────────────────────────────────┘
+

+

LEFT: Returns the left part of a character string with the specified number of characters.

+

query = """SELECT LEFT(FIRST, 5) AS Initial FROM patients LIMIT 10;"""
+conn.sql(query)
+
+
query = """SELECT LEFT(FIRST, 5) AS Initial FROM patients LIMIT 10;"""
+conn.sql(query)
+┌─────────┐
+ Initial 
+ varchar 
+├─────────┤
+ Jacin   
+ Alva9   
+ Jimmi   
+ Grego   
+ Karyn   
+ Jayso   
+ José    
+ Milo2   
+ Karyn   
+ Jeffr   
+├─────────┤
+ 10 rows 
+└─────────┘
+

+

LOWER: Converts all characters in the specified string to lowercase.

+

UPPER: Convert to uppercase

+

query = """
+SELECT LOWER(FIRST) AS LowercaseFirstName, UPPER(LAST) AS UppercaseLastName FROM patients LIMIT 10;
+"""
+conn.sql(query)
+
+
query = """
+SELECT LOWER(FIRST) AS LowercaseFirstName, UPPER(LAST) AS UppercaseLastName FROM patients LIMIT 10;
+"""
+conn.sql(query)
+┌────────────────────┬───────────────────┐
+ LowercaseFirstName  UppercaseLastName 
+      varchar             varchar      
+├────────────────────┼───────────────────┤
+ jacinto644          KRIS249           
+ alva958             KRAJCIK437        
+ jimmie93            HARRIS789         
+ gregorio366         AUER97            
+ karyn217            MUELLER846        
+ jayson808           FADEL536          
+ josé eduardo181     GÓMEZ206          
+ milo271             FEIL794           
+ karyn217            METZ686           
+ jeffrey461          GREENFELDER433    
+├────────────────────┴───────────────────┤
+ 10 rows                      2 columns 
+└────────────────────────────────────────┘
+
+LTRIM: Removes leading spaces from a string. +
query = """SELECT LTRIM(ADDRESS) AS TrimmedAddress FROM patients LIMIT 10;"""
+
+conn.sql(query)
+
+
query = """SELECT LTRIM(ADDRESS) AS TrimmedAddress FROM patients LIMIT 10;"""
+
+conn.sql(query)
+
+┌────────────────────────────────┐
+         TrimmedAddress         
+            varchar             
+├────────────────────────────────┤
+ 888 Hickle Ferry Suite 38      
+ 1048 Skiles Trailer            
+ 201 Mitchell Lodge Unit 67     
+ 1050 Lindgren Extension Apt 38 
+ 570 Abshire Forge Suite 32     
+ 1056 Harris Lane Suite 70      
+ 427 Balistreri Way Unit 19     
+ 422 Farrell Path Unit 69       
+ 181 Feest Passage Suite 64     
+ 428 Wiza Glen Unit 91          
+├────────────────────────────────┤
+            10 rows             
+└────────────────────────────────┘
+
+REPLACE: Replaces all occurrences of a specified string value with another string value. +
query = """SELECT FIRST, LAST,
+    ADDRESS AS LongAddress,
+    REPLACE(ADDRESS, 'Street', 'St') AS ShortAddress
+FROM
+    patients
+WHERE
+    ADDRESS LIKE '%Street%' LIMIT 10;
+"""
+
+conn.sql(query)
+
+
┌────────────┬───────────────┬───────────────────────────┬───────────────────────┐
+   FIRST         LAST              LongAddress             ShortAddress      
+  varchar       varchar              varchar                  varchar        
+├────────────┼───────────────┼───────────────────────────┼───────────────────────┤
+ Lorrie905   Leannon79      813 Casper Street          813 Casper St         
+ Asa127      Block661       140 Rohan Street Suite 50  140 Rohan St Suite 50 
+ Logan497    Brekke496      1081 Orn Street            1081 Orn St           
+ Cletus494   Strosin214     1019 Haley Street          1019 Haley St         
+ Cortney940  Stehr398       458 Streich Street         458 Streich St        
+ Latoyia537  Gaylord332     200 Heaney Street          200 Heaney St         
+ Clark193    Hilll811       202 Tromp Street Suite 0   202 Tromp St Suite 0  
+ Jc393       Bosco882       792 Walsh Street           792 Walsh St          
+ Noel608     Swaniawski813  155 Walter Street          155 Walter St         
+ Sari509     Hoppe518       550 Lang Street Suite 65   550 Lang St Suite 65  
+├────────────┴───────────────┴───────────────────────────┴───────────────────────┤
+ 10 rows                                                              4 columns 
+└────────────────────────────────────────────────────────────────────────────────┘
+
+RIGHT: Returns the right part of a character string with the specified number of characters. +
query = """SELECT FIRST, RIGHT(FIRST, 6) AS LastSixChars FROM patients LIMIT 10;
+"""
+
+conn.sql(query)
+
+
┌─────────────────┬──────────────┐
+      FIRST       LastSixChars 
+     varchar        varchar    
+├─────────────────┼──────────────┤
+ Jacinto644       nto644       
+ Alva958          lva958       
+ Jimmie93         mmie93       
+ Gregorio366      rio366       
+ Karyn217         ryn217       
+ Jayson808        son808       
+ José Eduardo181  rdo181       
+ Milo271          ilo271       
+ Karyn217         ryn217       
+ Jeffrey461       rey461       
+├─────────────────┴──────────────┤
+ 10 rows              2 columns 
+└────────────────────────────────┘
+
+SUBSTRING: Returns part of a character, binary, text, or image expression in SQL Server. +
query = """SELECT FIRST, SUBSTRING(FIRST, 1, 4) AS FirstFourChars FROM patients LIMIT 5;
+"""
+
+conn.sql(query)
+
+
┌─────────────┬────────────────┐
+    FIRST     FirstFourChars 
+   varchar       varchar     
+├─────────────┼────────────────┤
+ Jacinto644   Jaci           
+ Alva958      Alva           
+ Jimmie93     Jimm           
+ Gregorio366  Greg           
+ Karyn217     Kary           
+└─────────────┴────────────────┘
+
+REGEXP_REPLACE can use regular expressions to strip numeric characters: +
query = """SELECT
+FIRST, REGEXP_REPLACE(FIRST, '[0-9]', '', 'g') AS FirstNameStripped, LAST, REGEXP_REPLACE(LAST, '[0-9]', '', 'g') AS LastNameStripped
+FROM patients;
+"""
+
+conn.sql(query)
+
+
query = """SELECT
+FIRST, REGEXP_REPLACE(FIRST, '[0-9]', '', 'g') AS FirstNameStripped, LAST, REGEXP_REPLACE(LAST, '[0-9]', '', 'g') AS LastNameStripped
+FROM patients;
+"""
+
+conn.sql(query)
+┌─────────────────┬───────────────────┬────────────────┬──────────────────┐
+      FIRST       FirstNameStripped       LAST       LastNameStripped 
+     varchar           varchar          varchar          varchar      
+├─────────────────┼───────────────────┼────────────────┼──────────────────┤
+ Jacinto644       Jacinto            Kris249         Kris             
+ Alva958          Alva               Krajcik437      Krajcik          
+ Jimmie93         Jimmie             Harris789       Harris           
+ Gregorio366      Gregorio           Auer97          Auer             
+ Karyn217         Karyn              Mueller846      Mueller          
+ Jayson808        Jayson             Fadel536        Fadel            
+ José Eduardo181  José Eduardo       Gómez206        Gómez            
+ Milo271          Milo               Feil794         Feil             
+ Karyn217         Karyn              Metz686         Metz             
+ Jeffrey461       Jeffrey            Greenfelder433  Greenfelder      
+     ·               ·                   ·              ·             
+     ·               ·                   ·              ·             
+     ·               ·                   ·              ·             
+ Raymond398       Raymond            Kuvalis369      Kuvalis          
+ Gearldine455     Gearldine          Boyer713        Boyer            
+ Nichol11         Nichol             Gleichner915    Gleichner        
+ Louvenia131      Louvenia           Marks830        Marks            
+ Raymon366        Raymon             Beer512         Beer             
+ Camelia346       Camelia            Stamm704        Stamm            
+ William805       William            Pacocha935      Pacocha          
+ Guillermo498     Guillermo          Téllez750       Téllez           
+ Milton509        Milton             Bailey598       Bailey           
+ Cecilia788       Cecilia            Wisozk929       Wisozk           
+├─────────────────┴───────────────────┴────────────────┴──────────────────┤
+ ? rows (>9999 rows, 20 shown)                                 4 columns 
+└─────────────────────────────────────────────────────────────────────────┘
+

+
+
+

Creating a database and adding data to tables

+

CREATE, ALTER, DROP, INSERT commands

+
+

First lets have a in-memory database +

newdb = duckdb.connect()
+
+CREATE TABLE: Create a books table to mimic a library catalog system. +
query = """CREATE TABLE books (
+    book_id INTEGER PRIMARY KEY,
+    title VARCHAR,
+    author VARCHAR,
+    published_year INTEGER
+);
+"""
+
+newdb.sql(query)
+
+The CREATE TABLE statement defines the schema for this table: +This statement creates a table with columns for the book ID, title, author, and the year of publication. The book_id column is designated as the primary key, ensuring that each record in the table is unique.

+
+

check +

newdb.sql("SHOW TABLES;")
+
+
┌─────────┐
+  name   
+ varchar 
+├─────────┤
+ books   
+└─────────┘
+
+check data +
newdb.sql("DESCRIBE books;")
+
+
newdb.sql("DESCRIBE books;")
+┌────────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐
+  column_name    column_type   null      key    default  extra 
+    varchar        varchar    varchar  varchar  varchar  int32 
+├────────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤
+ book_id         INTEGER      NO       PRI      NULL      NULL 
+ title           VARCHAR      YES      NULL     NULL      NULL 
+ author          VARCHAR      YES      NULL     NULL      NULL 
+ published_year  INTEGER      YES      NULL     NULL      NULL 
+└────────────────┴─────────────┴─────────┴─────────┴─────────┴───────┘
+
+Alter Table: After creating the table, you might need to add more information. Use the ALTER TABLE statement to add a new column: +
query = """ALTER TABLE books ADD COLUMN genre VARCHAR;
+"""
+newdb.sql(query)
+# This adds a genre column to the books table, allowing you to categorize books.
+
+
newdb.sql("DESCRIBE books;")
+
+
newdb.sql("DESCRIBE books;")
+┌────────────────┬─────────────┬─────────┬─────────┬─────────┬───────┐
+  column_name    column_type   null      key    default  extra 
+    varchar        varchar    varchar  varchar  varchar  int32 
+├────────────────┼─────────────┼─────────┼─────────┼─────────┼───────┤
+ book_id         INTEGER      NO       PRI      NULL      NULL 
+ title           VARCHAR      YES      NULL     NULL      NULL 
+ author          VARCHAR      YES      NULL     NULL      NULL 
+ published_year  INTEGER      YES      NULL     NULL      NULL 
+ genre           VARCHAR      YES      NULL     NULL      NULL 
+└────────────────┴─────────────┴─────────┴─────────┴─────────┴───────┘
+
+let's add another table which we will DROP later; +
query = """CREATE TABLE authors (
+    author_id INTEGER PRIMARY KEY,
+    firstname VARCHAR,
+    lastname VARCHAR,
+    birth_year INTEGER
+);
+"""
+
+newdb.sql(query)
+

+

newdb.sql("SHOW TABLES;")
+
+Now let's DROP the author table +
query = """DROP TABLE IF EXISTS authors;
+"""
+
+newdb.sql(query)
+

+
newdb.sql("SHOW TABLES;")
+
+

Let's add some data now

+

INSERT To populate the books table with data. This inserts 10 records into the books table, providing a diverse set of examples.

+
query = """INSERT INTO books (book_id, title, author, published_year, genre) VALUES
+(1, '1984', 'George Orwell', 1949, 'Dystopian'),
+(2, 'The Great Gatsby', 'F. Scott Fitzgerald', 1925, 'Classic'),
+(3, 'To Kill a Mockingbird', 'Harper Lee', 1960, 'Classic'),
+(4, 'Pride and Prejudice', 'Jane Austen', 1813, 'Romance'),
+(5, 'The Hobbit', 'J.R.R. Tolkien', 1937, 'Fantasy'),
+(6, 'The Catcher in the Rye', 'J.D. Salinger', 1951, 'Literary Fiction'),
+(7, 'Harry Potter and the Sorcerer’s Stone', 'J.K. Rowling', 1997, 'Fantasy'),
+(8, 'The Da Vinci Code', 'Dan Brown', 2003, 'Thriller'),
+(9, 'Sapiens: A Brief History of Humankind', 'Yuval Noah Harari', 2011, 'Non-fiction'),
+(10, 'Beloved', 'Toni Morrison', 1987, 'Historical Fiction');"""
+
+newdb.sql(query)
+
+
query = """SELECT title AS "Book Title", author AS "Author Name", Genre FROM books;
+"""
+
+newdb.sql(query)
+
+
┌───────────────────────────────────────┬─────────────────────┬────────────────────┐
+              Book Title                    Author Name            genre        
+                varchar                       varchar             varchar       
+├───────────────────────────────────────┼─────────────────────┼────────────────────┤
+ 1984                                   George Orwell        Dystopian          
+ The Great Gatsby                       F. Scott Fitzgerald  Classic            
+ To Kill a Mockingbird                  Harper Lee           Classic            
+ Pride and Prejudice                    Jane Austen          Romance            
+ The Hobbit                             J.R.R. Tolkien       Fantasy            
+ The Catcher in the Rye                 J.D. Salinger        Literary Fiction   
+ Harry Potter and the Sorcerers Stone  J.K. Rowling         Fantasy            
+ The Da Vinci Code                      Dan Brown            Thriller           
+ Sapiens: A Brief History of Humankind  Yuval Noah Harari    Non-fiction        
+ Beloved                                Toni Morrison        Historical Fiction 
+├───────────────────────────────────────┴─────────────────────┴────────────────────┤
+ 10 rows                                                                3 columns 
+└──────────────────────────────────────────────────────────────────────────────────┘
+
+

Updating Data: UPDATE

+

This changes the genre of the book titled '1984' to 'Science Fiction'. +

query = """UPDATE books SET genre = 'Science Fiction' WHERE title = '1984';
+"""
+
+newdb.sql(query)
+

+
query = """SELECT title AS "Book Title", author AS "Author Name", Genre FROM books;
+"""
+
+newdb.sql(query)
+
+
┌───────────────────────────────────────┬─────────────────────┬────────────────────┐
+              Book Title                    Author Name            genre        
+                varchar                       varchar             varchar       
+├───────────────────────────────────────┼─────────────────────┼────────────────────┤
+ 1984                                   George Orwell        Science Fiction    
+ The Great Gatsby                       F. Scott Fitzgerald  Classic            
+ To Kill a Mockingbird                  Harper Lee           Classic            
+ Pride and Prejudice                    Jane Austen          Romance            
+ The Hobbit                             J.R.R. Tolkien       Fantasy            
+ The Catcher in the Rye                 J.D. Salinger        Literary Fiction   
+ Harry Potter and the Sorcerers Stone  J.K. Rowling         Fantasy            
+ The Da Vinci Code                      Dan Brown            Thriller           
+ Sapiens: A Brief History of Humankind  Yuval Noah Harari    Non-fiction        
+ Beloved                                Toni Morrison        Historical Fiction 
+├───────────────────────────────────────┴─────────────────────┴────────────────────┤
+ 10 rows                                                                3 columns 
+└──────────────────────────────────────────────────────────────────────────────────┘
+
+

DELETE

+

e.g. To remove a book from the catalog

+
query = """DELETE FROM books WHERE title = 'The Great Gatsby';"""
+newdb.sql(query)
+
+
query = """SELECT title AS "Book Title", author AS "Author Name", Genre FROM books;
+"""
+
+newdb.sql(query)
+
+
┌───────────────────────────────────────┬───────────────────┬────────────────────┐
+              Book Title                   Author Name           genre        
+                varchar                      varchar            varchar       
+├───────────────────────────────────────┼───────────────────┼────────────────────┤
+ 1984                                   George Orwell      Science Fiction    
+ To Kill a Mockingbird                  Harper Lee         Classic            
+ Pride and Prejudice                    Jane Austen        Romance            
+ The Hobbit                             J.R.R. Tolkien     Fantasy            
+ The Catcher in the Rye                 J.D. Salinger      Literary Fiction   
+ Harry Potter and the Sorcerers Stone  J.K. Rowling       Fantasy            
+ The Da Vinci Code                      Dan Brown          Thriller           
+ Sapiens: A Brief History of Humankind  Yuval Noah Harari  Non-fiction        
+ Beloved                                Toni Morrison      Historical Fiction 
+└───────────────────────────────────────┴───────────────────┴────────────────────┘
+
+

TRUNCATE

+
    +
  • The TRUNCATE TABLE statement is used to delete all rows in a table without logging the individual row deletions.
  • +
  • This is faster than using DELETE without a WHERE clause because TRUNCATE immediately deallocates data pages used by the table.
  • +
  • Use TRUNCATE when you want to quickly remove all records from a table but keep the table structure for future use.
  • +
  • Unlike DELETE, TRUNCATE does not generate a large number of transaction logs, making it more efficient for completely clearing a table.
  • +
+
query = """TRUNCATE TABLE books;"""
+
+newdb.sql(query)
+
+
query = """SELECT * FROM books;
+"""
+
+newdb.sql(query)
+
+

Adding one more table; named 'authors'

+

query = """CREATE TABLE authors (
+    author_id INTEGER PRIMARY KEY,
+    name VARCHAR,
+    birth_year INTEGER,
+    nationality VARCHAR
+);
+"""
+
+newdb.sql(query)
+
+Add information the authors table +
query = """INSERT INTO authors (author_id, name, birth_year, nationality) VALUES
+(1, 'George Orwell', 1903, 'British'),
+(2, 'F. Scott Fitzgerald', 1896, 'American'),
+(3, 'Harper Lee', 1926, 'American'),
+(4, 'Jane Austen', 1775, 'British'),
+(5, 'J.R.R. Tolkien', 1892, 'British'),
+(6, 'J.D. Salinger', 1919, 'American'),
+(7, 'J.K. Rowling', 1965, 'British'),
+(8, 'Dan Brown', 1964, 'American'),
+(9, 'Yuval Noah Harari', 1976, 'Israeli'),
+(10, 'Toni Morrison', 1931, 'American');
+"""
+
+
+newdb.sql(query)
+

+

query = """SELECT * FROM books;
+"""
+
+newdb.sql(query)
+
+
┌─────────┬───────────────────────────────────────┬─────────────────────┬────────────────┬────────────────────┐
+ book_id                  title                        author         published_year        genre        
+  int64                  varchar                       varchar            int64            varchar       
+├─────────┼───────────────────────────────────────┼─────────────────────┼────────────────┼────────────────────┤
+       1  1984                                   George Orwell                  1949  Dystopian          
+       2  The Great Gatsby                       F. Scott Fitzgerald            1925  Classic            
+       3  To Kill a Mockingbird                  Harper Lee                     1960  Classic            
+       4  Pride and Prejudice                    Jane Austen                    1813  Romance            
+       5  The Hobbit                             J.R.R. Tolkien                 1937  Fantasy            
+       6  The Catcher in the Rye                 J.D. Salinger                  1951  Literary Fiction   
+       7  Harry Potter and the Sorcerers Stone  J.K. Rowling                   1997  Fantasy            
+       8  The Da Vinci Code                      Dan Brown                      2003  Thriller           
+       9  Sapiens: A Brief History of Humankind  Yuval Noah Harari              2011  Non-fiction        
+      10  Beloved                                Toni Morrison                  1987  Historical Fiction 
+├─────────┴───────────────────────────────────────┴─────────────────────┴────────────────┴────────────────────┤
+ 10 rows                                                                                           5 columns 
+└─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+

+
query = """SELECT * FROM authors;
+"""
+
+newdb.sql(query)
+
+
newdb.sql(query)
+┌───────────┬─────────────────────┬────────────┬─────────────┐
+ author_id         name          birth_year  nationality 
+   int32          varchar          int32       varchar   
+├───────────┼─────────────────────┼────────────┼─────────────┤
+         1  George Orwell              1903  British     
+         2  F. Scott Fitzgerald        1896  American    
+         3  Harper Lee                 1926  American    
+         4  Jane Austen                1775  British     
+         5  J.R.R. Tolkien             1892  British     
+         6  J.D. Salinger              1919  American    
+         7  J.K. Rowling               1965  British     
+         8  Dan Brown                  1964  American    
+         9  Yuval Noah Harari          1976  Israeli     
+        10  Toni Morrison              1931  American    
+├───────────┴─────────────────────┴────────────┴─────────────┤
+ 10 rows                                          4 columns 
+└────────────────────────────────────────────────────────────┘
+
+

EXPORTING Data to csv from SQL

+
query = """EXPORT DATABASE './' (FORMAT CSV, DELIMITER ',');"""
+
+newdb.sql(query)
+
+

Thank You!

+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 000000000..98883fb21 --- /dev/null +++ b/404.html @@ -0,0 +1,979 @@ + + + + + + + + + + + + + + + + + + + + + + + CyVerse Foundational Open Science Skills 2024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 000000000..2ebddff2a --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +foss.cyverse.org diff --git a/assets/DataCommons_DrkBlue.png b/assets/DataCommons_DrkBlue.png new file mode 100644 index 000000000..43fd02552 Binary files /dev/null and b/assets/DataCommons_DrkBlue.png differ diff --git a/assets/DataCommons_DrkBlue0.png b/assets/DataCommons_DrkBlue0.png new file mode 100644 index 000000000..43fd02552 Binary files /dev/null and b/assets/DataCommons_DrkBlue0.png differ diff --git a/assets/JupyterShellPrompt.png b/assets/JupyterShellPrompt.png new file mode 100644 index 000000000..f2c69516d Binary files /dev/null and b/assets/JupyterShellPrompt.png differ diff --git a/assets/Layer_Cake_Updated.png b/assets/Layer_Cake_Updated.png new file mode 100644 index 000000000..9be70bf2d Binary files /dev/null and b/assets/Layer_Cake_Updated.png differ diff --git a/assets/Layer_Cake_Updated0.png b/assets/Layer_Cake_Updated0.png new file mode 100644 index 000000000..9be70bf2d Binary files /dev/null and b/assets/Layer_Cake_Updated0.png differ diff --git a/assets/Learningcenter_DkBlue.png b/assets/Learningcenter_DkBlue.png new file mode 100644 index 000000000..876d2da30 Binary files /dev/null and b/assets/Learningcenter_DkBlue.png differ diff --git a/assets/Learningcenter_DkBlue0.png b/assets/Learningcenter_DkBlue0.png new file mode 100644 index 000000000..876d2da30 Binary files /dev/null and b/assets/Learningcenter_DkBlue0.png differ diff --git a/assets/Powered-By-CyVerse-blue.png b/assets/Powered-By-CyVerse-blue.png new file mode 100644 index 000000000..e9f257eda Binary files /dev/null and b/assets/Powered-By-CyVerse-blue.png differ diff --git a/assets/PoweredbyCyverse_LogoSquare.png b/assets/PoweredbyCyverse_LogoSquare.png new file mode 100644 index 000000000..e4bdc73cd Binary files /dev/null and b/assets/PoweredbyCyverse_LogoSquare.png differ diff --git a/assets/PoweredbyCyverse_LogoSquare0.png b/assets/PoweredbyCyverse_LogoSquare0.png new file mode 100644 index 000000000..e4bdc73cd Binary files /dev/null and b/assets/PoweredbyCyverse_LogoSquare0.png differ diff --git a/assets/RO_crate.png b/assets/RO_crate.png new file mode 100644 index 000000000..b0e48fda7 Binary files /dev/null and b/assets/RO_crate.png differ diff --git a/assets/_mkdocstrings.css b/assets/_mkdocstrings.css new file mode 100644 index 000000000..e69de29bb diff --git a/assets/addtool_VICE_1.png b/assets/addtool_VICE_1.png new file mode 100644 index 000000000..f6b66750b Binary files /dev/null and b/assets/addtool_VICE_1.png differ diff --git a/assets/addtool_VICE_10.png b/assets/addtool_VICE_10.png new file mode 100644 index 000000000..f6b66750b Binary files /dev/null and b/assets/addtool_VICE_10.png differ diff --git a/assets/agave/agave.png b/assets/agave/agave.png new file mode 100644 index 000000000..40743beb5 Binary files /dev/null and b/assets/agave/agave.png differ diff --git a/assets/atmo-1.1.png b/assets/atmo-1.1.png new file mode 100644 index 000000000..dc9bfdc0d Binary files /dev/null and b/assets/atmo-1.1.png differ diff --git a/assets/atmo-1.png b/assets/atmo-1.png new file mode 100644 index 000000000..b164f4810 Binary files /dev/null and b/assets/atmo-1.png differ diff --git a/assets/atmo-10.1.png b/assets/atmo-10.1.png new file mode 100644 index 000000000..dc9bfdc0d Binary files /dev/null and b/assets/atmo-10.1.png differ diff --git a/assets/atmo-10.png b/assets/atmo-10.png new file mode 100644 index 000000000..b164f4810 Binary files /dev/null and b/assets/atmo-10.png differ diff --git a/assets/atmo-6.png b/assets/atmo-6.png new file mode 100644 index 000000000..97be7d35c Binary files /dev/null and b/assets/atmo-6.png differ diff --git a/assets/atmo-60.png b/assets/atmo-60.png new file mode 100644 index 000000000..97be7d35c Binary files /dev/null and b/assets/atmo-60.png differ diff --git a/assets/atmo-7.png b/assets/atmo-7.png new file mode 100644 index 000000000..a946626b6 Binary files /dev/null and b/assets/atmo-7.png differ diff --git a/assets/atmo-70.png b/assets/atmo-70.png new file mode 100644 index 000000000..a946626b6 Binary files /dev/null and b/assets/atmo-70.png differ diff --git a/assets/atmo-8.png b/assets/atmo-8.png new file mode 100644 index 000000000..63cacf470 Binary files /dev/null and b/assets/atmo-8.png differ diff --git a/assets/atmo-80.png b/assets/atmo-80.png new file mode 100644 index 000000000..63cacf470 Binary files /dev/null and b/assets/atmo-80.png differ diff --git a/assets/atmo-9.png b/assets/atmo-9.png new file mode 100644 index 000000000..fb5061fce Binary files /dev/null and b/assets/atmo-9.png differ diff --git a/assets/atmo-90.png b/assets/atmo-90.png new file mode 100644 index 000000000..fb5061fce Binary files /dev/null and b/assets/atmo-90.png differ diff --git a/assets/atmo_cp.png b/assets/atmo_cp.png new file mode 100644 index 000000000..9f782b7ff Binary files /dev/null and b/assets/atmo_cp.png differ diff --git a/assets/atmo_cp0.png b/assets/atmo_cp0.png new file mode 100644 index 000000000..9f782b7ff Binary files /dev/null and b/assets/atmo_cp0.png differ diff --git a/assets/atmo_cp00.png b/assets/atmo_cp00.png new file mode 100644 index 000000000..ce2d5924e Binary files /dev/null and b/assets/atmo_cp00.png differ diff --git a/assets/atmo_launch.png b/assets/atmo_launch.png new file mode 100644 index 000000000..1b8222762 Binary files /dev/null and b/assets/atmo_launch.png differ diff --git a/assets/atmo_launch0.png b/assets/atmo_launch0.png new file mode 100644 index 000000000..1b8222762 Binary files /dev/null and b/assets/atmo_launch0.png differ diff --git a/assets/atmo_launch00.png b/assets/atmo_launch00.png new file mode 100644 index 000000000..0e2204d7c Binary files /dev/null and b/assets/atmo_launch00.png differ diff --git a/assets/atmo_launch1.png b/assets/atmo_launch1.png new file mode 100644 index 000000000..d47ef1a6e Binary files /dev/null and b/assets/atmo_launch1.png differ diff --git a/assets/atmo_request.png b/assets/atmo_request.png new file mode 100644 index 000000000..c526d1113 Binary files /dev/null and b/assets/atmo_request.png differ diff --git a/assets/atmo_request0.png b/assets/atmo_request0.png new file mode 100644 index 000000000..c526d1113 Binary files /dev/null and b/assets/atmo_request0.png differ diff --git a/assets/atmo_resources.png b/assets/atmo_resources.png new file mode 100644 index 000000000..3298fec16 Binary files /dev/null and b/assets/atmo_resources.png differ diff --git a/assets/atmo_resources0.png b/assets/atmo_resources0.png new file mode 100644 index 000000000..3298fec16 Binary files /dev/null and b/assets/atmo_resources0.png differ diff --git a/assets/atmosphere/atmosphere-icon.png b/assets/atmosphere/atmosphere-icon.png new file mode 100644 index 000000000..1bd08de30 Binary files /dev/null and b/assets/atmosphere/atmosphere-icon.png differ diff --git a/assets/atmosphere/instance_status.png b/assets/atmosphere/instance_status.png new file mode 100644 index 000000000..4c6d17cf4 Binary files /dev/null and b/assets/atmosphere/instance_status.png differ diff --git a/assets/atmosphere/launch_window.png b/assets/atmosphere/launch_window.png new file mode 100644 index 000000000..0f4086ae1 Binary files /dev/null and b/assets/atmosphere/launch_window.png differ diff --git a/assets/atmosphere/need_more.png b/assets/atmosphere/need_more.png new file mode 100644 index 000000000..8e8bd3601 Binary files /dev/null and b/assets/atmosphere/need_more.png differ diff --git a/assets/atmosphere/project_icon.png b/assets/atmosphere/project_icon.png new file mode 100644 index 000000000..bc1955620 Binary files /dev/null and b/assets/atmosphere/project_icon.png differ diff --git a/assets/atmosphere/request_access.png b/assets/atmosphere/request_access.png new file mode 100644 index 000000000..c8fd3ecbc Binary files /dev/null and b/assets/atmosphere/request_access.png differ diff --git a/assets/atmosphere/vnc_desktop.png b/assets/atmosphere/vnc_desktop.png new file mode 100644 index 000000000..323ce2a0b Binary files /dev/null and b/assets/atmosphere/vnc_desktop.png differ diff --git a/assets/auto-1.png b/assets/auto-1.png new file mode 100644 index 000000000..9d6a9a8fe Binary files /dev/null and b/assets/auto-1.png differ diff --git a/assets/auto-10.png b/assets/auto-10.png new file mode 100644 index 000000000..9d6a9a8fe Binary files /dev/null and b/assets/auto-10.png differ diff --git a/assets/auto-2.png b/assets/auto-2.png new file mode 100644 index 000000000..332eca34b Binary files /dev/null and b/assets/auto-2.png differ diff --git a/assets/auto-20.png b/assets/auto-20.png new file mode 100644 index 000000000..332eca34b Binary files /dev/null and b/assets/auto-20.png differ diff --git a/assets/auto-3.png b/assets/auto-3.png new file mode 100644 index 000000000..581be0014 Binary files /dev/null and b/assets/auto-3.png differ diff --git a/assets/auto-30.png b/assets/auto-30.png new file mode 100644 index 000000000..581be0014 Binary files /dev/null and b/assets/auto-30.png differ diff --git a/assets/auto-4.png b/assets/auto-4.png new file mode 100644 index 000000000..fa9277b0c Binary files /dev/null and b/assets/auto-4.png differ diff --git a/assets/auto-40.png b/assets/auto-40.png new file mode 100644 index 000000000..fa9277b0c Binary files /dev/null and b/assets/auto-40.png differ diff --git a/assets/auto_build-1.png b/assets/auto_build-1.png new file mode 100644 index 000000000..d39894edf Binary files /dev/null and b/assets/auto_build-1.png differ diff --git a/assets/auto_build-10.png b/assets/auto_build-10.png new file mode 100644 index 000000000..d39894edf Binary files /dev/null and b/assets/auto_build-10.png differ diff --git a/assets/auto_build-2.1.png b/assets/auto_build-2.1.png new file mode 100644 index 000000000..fccbf6844 Binary files /dev/null and b/assets/auto_build-2.1.png differ diff --git a/assets/auto_build-2.png b/assets/auto_build-2.png new file mode 100644 index 000000000..3abbc828c Binary files /dev/null and b/assets/auto_build-2.png differ diff --git a/assets/auto_build-20.1.png b/assets/auto_build-20.1.png new file mode 100644 index 000000000..fccbf6844 Binary files /dev/null and b/assets/auto_build-20.1.png differ diff --git a/assets/auto_build-20.png b/assets/auto_build-20.png new file mode 100644 index 000000000..3abbc828c Binary files /dev/null and b/assets/auto_build-20.png differ diff --git a/assets/auto_build-3.png b/assets/auto_build-3.png new file mode 100644 index 000000000..c07414273 Binary files /dev/null and b/assets/auto_build-3.png differ diff --git a/assets/auto_build-30.png b/assets/auto_build-30.png new file mode 100644 index 000000000..c07414273 Binary files /dev/null and b/assets/auto_build-30.png differ diff --git a/assets/auto_build-4.png b/assets/auto_build-4.png new file mode 100644 index 000000000..8c9d53eef Binary files /dev/null and b/assets/auto_build-4.png differ diff --git a/assets/auto_build-40.png b/assets/auto_build-40.png new file mode 100644 index 000000000..8c9d53eef Binary files /dev/null and b/assets/auto_build-40.png differ diff --git a/assets/auto_build-5.png b/assets/auto_build-5.png new file mode 100644 index 000000000..71aa68c69 Binary files /dev/null and b/assets/auto_build-5.png differ diff --git a/assets/auto_build-50.png b/assets/auto_build-50.png new file mode 100644 index 000000000..71aa68c69 Binary files /dev/null and b/assets/auto_build-50.png differ diff --git a/assets/auto_build-6.png b/assets/auto_build-6.png new file mode 100644 index 000000000..2787d2ceb Binary files /dev/null and b/assets/auto_build-6.png differ diff --git a/assets/auto_build-60.png b/assets/auto_build-60.png new file mode 100644 index 000000000..2787d2ceb Binary files /dev/null and b/assets/auto_build-60.png differ diff --git a/assets/auto_build-7.png b/assets/auto_build-7.png new file mode 100644 index 000000000..2f8882681 Binary files /dev/null and b/assets/auto_build-7.png differ diff --git a/assets/auto_build-70.png b/assets/auto_build-70.png new file mode 100644 index 000000000..2f8882681 Binary files /dev/null and b/assets/auto_build-70.png differ diff --git a/assets/bind_mount.png b/assets/bind_mount.png new file mode 100644 index 000000000..2d8d4091b Binary files /dev/null and b/assets/bind_mount.png differ diff --git a/assets/bind_mount0.png b/assets/bind_mount0.png new file mode 100644 index 000000000..2d8d4091b Binary files /dev/null and b/assets/bind_mount0.png differ diff --git a/assets/biocontainer-1.png b/assets/biocontainer-1.png new file mode 100644 index 000000000..b06247b0b Binary files /dev/null and b/assets/biocontainer-1.png differ diff --git a/assets/biocontainer-10.png b/assets/biocontainer-10.png new file mode 100644 index 000000000..b06247b0b Binary files /dev/null and b/assets/biocontainer-10.png differ diff --git a/assets/biocontainer-2.png b/assets/biocontainer-2.png new file mode 100644 index 000000000..1daf92967 Binary files /dev/null and b/assets/biocontainer-2.png differ diff --git a/assets/biocontainer-20.png b/assets/biocontainer-20.png new file mode 100644 index 000000000..1daf92967 Binary files /dev/null and b/assets/biocontainer-20.png differ diff --git a/assets/biocontainer-3.png b/assets/biocontainer-3.png new file mode 100644 index 000000000..2f1bca1bb Binary files /dev/null and b/assets/biocontainer-3.png differ diff --git a/assets/biocontainer-30.png b/assets/biocontainer-30.png new file mode 100644 index 000000000..2f1bca1bb Binary files /dev/null and b/assets/biocontainer-30.png differ diff --git a/assets/biocontainer-4.png b/assets/biocontainer-4.png new file mode 100644 index 000000000..111d84177 Binary files /dev/null and b/assets/biocontainer-4.png differ diff --git a/assets/biocontainer-40.png b/assets/biocontainer-40.png new file mode 100644 index 000000000..111d84177 Binary files /dev/null and b/assets/biocontainer-40.png differ diff --git a/assets/biocontainer-5.png b/assets/biocontainer-5.png new file mode 100644 index 000000000..7bf238e60 Binary files /dev/null and b/assets/biocontainer-5.png differ diff --git a/assets/biocontainer-50.png b/assets/biocontainer-50.png new file mode 100644 index 000000000..7bf238e60 Binary files /dev/null and b/assets/biocontainer-50.png differ diff --git a/assets/biocontainer15.png b/assets/biocontainer15.png new file mode 100644 index 000000000..ff0f92adf Binary files /dev/null and b/assets/biocontainer15.png differ diff --git a/assets/biocontainer150.png b/assets/biocontainer150.png new file mode 100644 index 000000000..ff0f92adf Binary files /dev/null and b/assets/biocontainer150.png differ diff --git a/assets/biocontainers1.png b/assets/biocontainers1.png new file mode 100644 index 000000000..139729073 Binary files /dev/null and b/assets/biocontainers1.png differ diff --git a/assets/biocontainers10.png b/assets/biocontainers10.png new file mode 100644 index 000000000..139729073 Binary files /dev/null and b/assets/biocontainers10.png differ diff --git a/assets/biocontainers11.png b/assets/biocontainers11.png new file mode 100644 index 000000000..459cdbc07 Binary files /dev/null and b/assets/biocontainers11.png differ diff --git a/assets/biocontainers110.png b/assets/biocontainers110.png new file mode 100644 index 000000000..459cdbc07 Binary files /dev/null and b/assets/biocontainers110.png differ diff --git a/assets/biocontainers12.png b/assets/biocontainers12.png new file mode 100644 index 000000000..9205ce7a5 Binary files /dev/null and b/assets/biocontainers12.png differ diff --git a/assets/biocontainers120.png b/assets/biocontainers120.png new file mode 100644 index 000000000..9205ce7a5 Binary files /dev/null and b/assets/biocontainers120.png differ diff --git a/assets/biocontainers13.png b/assets/biocontainers13.png new file mode 100644 index 000000000..699513e25 Binary files /dev/null and b/assets/biocontainers13.png differ diff --git a/assets/biocontainers130.png b/assets/biocontainers130.png new file mode 100644 index 000000000..699513e25 Binary files /dev/null and b/assets/biocontainers130.png differ diff --git a/assets/biocontainers15.png b/assets/biocontainers15.png new file mode 100644 index 000000000..ff0f92adf Binary files /dev/null and b/assets/biocontainers15.png differ diff --git a/assets/biocontainers150.png b/assets/biocontainers150.png new file mode 100644 index 000000000..ff0f92adf Binary files /dev/null and b/assets/biocontainers150.png differ diff --git a/assets/biocontainers16.png b/assets/biocontainers16.png new file mode 100644 index 000000000..d58230f00 Binary files /dev/null and b/assets/biocontainers16.png differ diff --git a/assets/biocontainers160.png b/assets/biocontainers160.png new file mode 100644 index 000000000..d58230f00 Binary files /dev/null and b/assets/biocontainers160.png differ diff --git a/assets/biocontainers18.png b/assets/biocontainers18.png new file mode 100644 index 000000000..48fd18589 Binary files /dev/null and b/assets/biocontainers18.png differ diff --git a/assets/biocontainers180.png b/assets/biocontainers180.png new file mode 100644 index 000000000..48fd18589 Binary files /dev/null and b/assets/biocontainers180.png differ diff --git a/assets/biocontainers19.png b/assets/biocontainers19.png new file mode 100644 index 000000000..deed89ff4 Binary files /dev/null and b/assets/biocontainers19.png differ diff --git a/assets/biocontainers190.png b/assets/biocontainers190.png new file mode 100644 index 000000000..deed89ff4 Binary files /dev/null and b/assets/biocontainers190.png differ diff --git a/assets/biocontainers2.png b/assets/biocontainers2.png new file mode 100644 index 000000000..d64cf4c19 Binary files /dev/null and b/assets/biocontainers2.png differ diff --git a/assets/biocontainers20.png b/assets/biocontainers20.png new file mode 100644 index 000000000..9afa2df07 Binary files /dev/null and b/assets/biocontainers20.png differ diff --git a/assets/biocontainers200.png b/assets/biocontainers200.png new file mode 100644 index 000000000..9afa2df07 Binary files /dev/null and b/assets/biocontainers200.png differ diff --git a/assets/biocontainers21.png b/assets/biocontainers21.png new file mode 100644 index 000000000..5ef304cff Binary files /dev/null and b/assets/biocontainers21.png differ diff --git a/assets/biocontainers210.png b/assets/biocontainers210.png new file mode 100644 index 000000000..5ef304cff Binary files /dev/null and b/assets/biocontainers210.png differ diff --git a/assets/biocontainers22.png b/assets/biocontainers22.png new file mode 100644 index 000000000..d64cf4c19 Binary files /dev/null and b/assets/biocontainers22.png differ diff --git a/assets/biocontainers3.png b/assets/biocontainers3.png new file mode 100644 index 000000000..c6f5ae2db Binary files /dev/null and b/assets/biocontainers3.png differ diff --git a/assets/biocontainers30.png b/assets/biocontainers30.png new file mode 100644 index 000000000..c6f5ae2db Binary files /dev/null and b/assets/biocontainers30.png differ diff --git a/assets/biocontainers5a.png b/assets/biocontainers5a.png new file mode 100644 index 000000000..bbcb0d3c6 Binary files /dev/null and b/assets/biocontainers5a.png differ diff --git a/assets/biocontainers5a0.png b/assets/biocontainers5a0.png new file mode 100644 index 000000000..bbcb0d3c6 Binary files /dev/null and b/assets/biocontainers5a0.png differ diff --git a/assets/biocontainers6.png b/assets/biocontainers6.png new file mode 100644 index 000000000..0332127f3 Binary files /dev/null and b/assets/biocontainers6.png differ diff --git a/assets/biocontainers60.png b/assets/biocontainers60.png new file mode 100644 index 000000000..0332127f3 Binary files /dev/null and b/assets/biocontainers60.png differ diff --git a/assets/biocontainers8.png b/assets/biocontainers8.png new file mode 100644 index 000000000..c5b6d1d96 Binary files /dev/null and b/assets/biocontainers8.png differ diff --git a/assets/biocontainers80.png b/assets/biocontainers80.png new file mode 100644 index 000000000..c5b6d1d96 Binary files /dev/null and b/assets/biocontainers80.png differ diff --git a/assets/biocontainers9.png b/assets/biocontainers9.png new file mode 100644 index 000000000..286589b8b Binary files /dev/null and b/assets/biocontainers9.png differ diff --git a/assets/biocontainers90.png b/assets/biocontainers90.png new file mode 100644 index 000000000..286589b8b Binary files /dev/null and b/assets/biocontainers90.png differ diff --git a/assets/bisque/bisque-icon.png b/assets/bisque/bisque-icon.png new file mode 100644 index 000000000..3c15e3d11 Binary files /dev/null and b/assets/bisque/bisque-icon.png differ diff --git a/assets/catpic-1.png b/assets/catpic-1.png new file mode 100644 index 000000000..941eaf8f3 Binary files /dev/null and b/assets/catpic-1.png differ diff --git a/assets/catpic-10.png b/assets/catpic-10.png new file mode 100644 index 000000000..941eaf8f3 Binary files /dev/null and b/assets/catpic-10.png differ diff --git a/assets/cc-main.png b/assets/cc-main.png new file mode 100644 index 000000000..46856609b Binary files /dev/null and b/assets/cc-main.png differ diff --git a/assets/cc-main0.png b/assets/cc-main0.png new file mode 100644 index 000000000..46856609b Binary files /dev/null and b/assets/cc-main0.png differ diff --git a/assets/cli.gif b/assets/cli.gif new file mode 100644 index 000000000..b58f98b63 Binary files /dev/null and b/assets/cli.gif differ diff --git a/assets/cmd1.png b/assets/cmd1.png new file mode 100644 index 000000000..011674092 Binary files /dev/null and b/assets/cmd1.png differ diff --git a/assets/cmd10.png b/assets/cmd10.png new file mode 100644 index 000000000..011674092 Binary files /dev/null and b/assets/cmd10.png differ diff --git a/assets/cmd15.png b/assets/cmd15.png new file mode 100644 index 000000000..1646a9961 Binary files /dev/null and b/assets/cmd15.png differ diff --git a/assets/cmd150.png b/assets/cmd150.png new file mode 100644 index 000000000..1646a9961 Binary files /dev/null and b/assets/cmd150.png differ diff --git a/assets/cmd16.png b/assets/cmd16.png new file mode 100644 index 000000000..a478d3b12 Binary files /dev/null and b/assets/cmd16.png differ diff --git a/assets/cmd160.png b/assets/cmd160.png new file mode 100644 index 000000000..a478d3b12 Binary files /dev/null and b/assets/cmd160.png differ diff --git a/assets/cmd2.png b/assets/cmd2.png new file mode 100644 index 000000000..6800b124d Binary files /dev/null and b/assets/cmd2.png differ diff --git a/assets/cmd20.png b/assets/cmd20.png new file mode 100644 index 000000000..6800b124d Binary files /dev/null and b/assets/cmd20.png differ diff --git a/assets/cmd3.png b/assets/cmd3.png new file mode 100644 index 000000000..2884dd17e Binary files /dev/null and b/assets/cmd3.png differ diff --git a/assets/cmd30.png b/assets/cmd30.png new file mode 100644 index 000000000..2884dd17e Binary files /dev/null and b/assets/cmd30.png differ diff --git a/assets/code.jpg b/assets/code.jpg new file mode 100644 index 000000000..8f2e8e82a Binary files /dev/null and b/assets/code.jpg differ diff --git a/assets/containers1.png b/assets/containers1.png new file mode 100644 index 000000000..c1c0734a7 Binary files /dev/null and b/assets/containers1.png differ diff --git a/assets/create_repo.png b/assets/create_repo.png new file mode 100644 index 000000000..54a6e3f3d Binary files /dev/null and b/assets/create_repo.png differ diff --git a/assets/create_repo0.png b/assets/create_repo0.png new file mode 100644 index 000000000..54a6e3f3d Binary files /dev/null and b/assets/create_repo0.png differ diff --git a/assets/create_repo2.png b/assets/create_repo2.png new file mode 100644 index 000000000..7b8ad535c Binary files /dev/null and b/assets/create_repo2.png differ diff --git a/assets/create_repo20.png b/assets/create_repo20.png new file mode 100644 index 000000000..7b8ad535c Binary files /dev/null and b/assets/create_repo20.png differ diff --git a/assets/createapp_VICE_1.png b/assets/createapp_VICE_1.png new file mode 100644 index 000000000..8b9e7ab26 Binary files /dev/null and b/assets/createapp_VICE_1.png differ diff --git a/assets/createnewrepo.png b/assets/createnewrepo.png new file mode 100644 index 000000000..81f76cc91 Binary files /dev/null and b/assets/createnewrepo.png differ diff --git a/assets/cycle_prereg.png b/assets/cycle_prereg.png new file mode 100644 index 000000000..6014f3603 Binary files /dev/null and b/assets/cycle_prereg.png differ diff --git a/assets/cyverse_cmyk.png b/assets/cyverse_cmyk.png new file mode 100644 index 000000000..0e5845473 Binary files /dev/null and b/assets/cyverse_cmyk.png differ diff --git a/assets/cyverse_cmyk0.png b/assets/cyverse_cmyk0.png new file mode 100644 index 000000000..0e5845473 Binary files /dev/null and b/assets/cyverse_cmyk0.png differ diff --git a/assets/cyverse_float.gif b/assets/cyverse_float.gif new file mode 100644 index 000000000..d5c94009e Binary files /dev/null and b/assets/cyverse_float.gif differ diff --git a/assets/cyverse_globe_cmyk.png b/assets/cyverse_globe_cmyk.png new file mode 100644 index 000000000..33b2fa200 Binary files /dev/null and b/assets/cyverse_globe_cmyk.png differ diff --git a/assets/cyverse_globe_cmyk0.png b/assets/cyverse_globe_cmyk0.png new file mode 100644 index 000000000..33b2fa200 Binary files /dev/null and b/assets/cyverse_globe_cmyk0.png differ diff --git a/assets/cyverse_icon_tm_space.png b/assets/cyverse_icon_tm_space.png new file mode 100644 index 000000000..a5cb4244f Binary files /dev/null and b/assets/cyverse_icon_tm_space.png differ diff --git a/assets/cyverse_icon_tm_space0.png b/assets/cyverse_icon_tm_space0.png new file mode 100644 index 000000000..a5cb4244f Binary files /dev/null and b/assets/cyverse_icon_tm_space0.png differ diff --git a/assets/cyverse_icon_tm_space1.png b/assets/cyverse_icon_tm_space1.png new file mode 100644 index 000000000..a5cb4244f Binary files /dev/null and b/assets/cyverse_icon_tm_space1.png differ diff --git a/assets/cyverse_learning.png b/assets/cyverse_learning.png new file mode 100644 index 000000000..d1d5de1f6 Binary files /dev/null and b/assets/cyverse_learning.png differ diff --git a/assets/cyverse_platform_stack.png b/assets/cyverse_platform_stack.png new file mode 100644 index 000000000..095e884b7 Binary files /dev/null and b/assets/cyverse_platform_stack.png differ diff --git a/assets/cyverse_platform_stack0.png b/assets/cyverse_platform_stack0.png new file mode 100644 index 000000000..095e884b7 Binary files /dev/null and b/assets/cyverse_platform_stack0.png differ diff --git a/assets/cyverse_rgb.png b/assets/cyverse_rgb.png new file mode 100644 index 000000000..90378af57 Binary files /dev/null and b/assets/cyverse_rgb.png differ diff --git a/assets/cyverse_rgb0.png b/assets/cyverse_rgb0.png new file mode 100644 index 000000000..90378af57 Binary files /dev/null and b/assets/cyverse_rgb0.png differ diff --git a/assets/cyverse_rgb1.png b/assets/cyverse_rgb1.png new file mode 100644 index 000000000..90378af57 Binary files /dev/null and b/assets/cyverse_rgb1.png differ diff --git a/assets/data_life_cycle.png b/assets/data_life_cycle.png new file mode 100644 index 000000000..c24e64b9a Binary files /dev/null and b/assets/data_life_cycle.png differ diff --git a/assets/data_life_cycle0.png b/assets/data_life_cycle0.png new file mode 100644 index 000000000..c24e64b9a Binary files /dev/null and b/assets/data_life_cycle0.png differ diff --git a/assets/data_store/datastore-icon.png b/assets/data_store/datastore-icon.png new file mode 100644 index 000000000..dc55b3e3d Binary files /dev/null and b/assets/data_store/datastore-icon.png differ diff --git a/assets/data_store/datastore_plchldr.png b/assets/data_store/datastore_plchldr.png new file mode 100644 index 000000000..886defb3e Binary files /dev/null and b/assets/data_store/datastore_plchldr.png differ diff --git a/assets/dc-1.png b/assets/dc-1.png new file mode 100644 index 000000000..003f402ad Binary files /dev/null and b/assets/dc-1.png differ diff --git a/assets/dc-10.png b/assets/dc-10.png new file mode 100644 index 000000000..003f402ad Binary files /dev/null and b/assets/dc-10.png differ diff --git a/assets/de/analyses_icon.png b/assets/de/analyses_icon.png new file mode 100644 index 000000000..a79efc402 Binary files /dev/null and b/assets/de/analyses_icon.png differ diff --git a/assets/de/apps_icon.png b/assets/de/apps_icon.png new file mode 100644 index 000000000..7f12f4eac Binary files /dev/null and b/assets/de/apps_icon.png differ diff --git a/assets/de/beta.png b/assets/de/beta.png new file mode 100644 index 000000000..8c988ab68 Binary files /dev/null and b/assets/de/beta.png differ diff --git a/assets/de/comment.png b/assets/de/comment.png new file mode 100644 index 000000000..cb6be4ce4 Binary files /dev/null and b/assets/de/comment.png differ diff --git a/assets/de/data_icon.png b/assets/de/data_icon.png new file mode 100644 index 000000000..ff73ec4b7 Binary files /dev/null and b/assets/de/data_icon.png differ diff --git a/assets/de/data_window.png b/assets/de/data_window.png new file mode 100644 index 000000000..c833cebde Binary files /dev/null and b/assets/de/data_window.png differ diff --git a/assets/de/de-icon.png b/assets/de/de-icon.png new file mode 100644 index 000000000..b009be1e7 Binary files /dev/null and b/assets/de/de-icon.png differ diff --git a/assets/de/de_app_icon.png b/assets/de/de_app_icon.png new file mode 100644 index 000000000..40cbe528e Binary files /dev/null and b/assets/de/de_app_icon.png differ diff --git a/assets/de/delete_icon.png b/assets/de/delete_icon.png new file mode 100644 index 000000000..86dd8b0c6 Binary files /dev/null and b/assets/de/delete_icon.png differ diff --git a/assets/de/favorite.png b/assets/de/favorite.png new file mode 100644 index 000000000..c0da08bdd Binary files /dev/null and b/assets/de/favorite.png differ diff --git a/assets/de/info.png b/assets/de/info.png new file mode 100644 index 000000000..cc9481416 Binary files /dev/null and b/assets/de/info.png differ diff --git a/assets/de/link_icon.png b/assets/de/link_icon.png new file mode 100644 index 000000000..60973cb39 Binary files /dev/null and b/assets/de/link_icon.png differ diff --git a/assets/de/manage_data_links.png b/assets/de/manage_data_links.png new file mode 100644 index 000000000..8e318d122 Binary files /dev/null and b/assets/de/manage_data_links.png differ diff --git a/assets/de/manage_sharing_menu.png b/assets/de/manage_sharing_menu.png new file mode 100644 index 000000000..8d0c90d1a Binary files /dev/null and b/assets/de/manage_sharing_menu.png differ diff --git a/assets/de/muscle_app_window.png b/assets/de/muscle_app_window.png new file mode 100644 index 000000000..5d4f63607 Binary files /dev/null and b/assets/de/muscle_app_window.png differ diff --git a/assets/de/person_icon.png b/assets/de/person_icon.png new file mode 100644 index 000000000..b5d5c42d9 Binary files /dev/null and b/assets/de/person_icon.png differ diff --git a/assets/de/private.png b/assets/de/private.png new file mode 100644 index 000000000..41213cddf Binary files /dev/null and b/assets/de/private.png differ diff --git a/assets/de/rating.png b/assets/de/rating.png new file mode 100644 index 000000000..0fd4c26fc Binary files /dev/null and b/assets/de/rating.png differ diff --git a/assets/de/unavailable.png b/assets/de/unavailable.png new file mode 100644 index 000000000..b5f390a1f Binary files /dev/null and b/assets/de/unavailable.png differ diff --git a/assets/de/viewing_window.png b/assets/de/viewing_window.png new file mode 100644 index 000000000..75b791827 Binary files /dev/null and b/assets/de/viewing_window.png differ diff --git a/assets/de2_datastore1.png b/assets/de2_datastore1.png new file mode 100644 index 000000000..d99c0afb7 Binary files /dev/null and b/assets/de2_datastore1.png differ diff --git a/assets/de2_datastore2.png b/assets/de2_datastore2.png new file mode 100644 index 000000000..ae01390de Binary files /dev/null and b/assets/de2_datastore2.png differ diff --git a/assets/de2_datastore3.png b/assets/de2_datastore3.png new file mode 100644 index 000000000..669c4e6d5 Binary files /dev/null and b/assets/de2_datastore3.png differ diff --git a/assets/de2_datastore4.png b/assets/de2_datastore4.png new file mode 100644 index 000000000..aba2cef99 Binary files /dev/null and b/assets/de2_datastore4.png differ diff --git a/assets/de2_select_input.png b/assets/de2_select_input.png new file mode 100644 index 000000000..8851fb256 Binary files /dev/null and b/assets/de2_select_input.png differ diff --git a/assets/dependency.png b/assets/dependency.png new file mode 100644 index 000000000..8df0e416d Binary files /dev/null and b/assets/dependency.png differ diff --git a/assets/dna_subway/dnasubway-icon.png b/assets/dna_subway/dnasubway-icon.png new file mode 100644 index 000000000..0c3af3668 Binary files /dev/null and b/assets/dna_subway/dnasubway-icon.png differ diff --git a/assets/docker.png b/assets/docker.png new file mode 100644 index 000000000..d1dad458b Binary files /dev/null and b/assets/docker.png differ diff --git a/assets/docker0.png b/assets/docker0.png new file mode 100644 index 000000000..d1dad458b Binary files /dev/null and b/assets/docker0.png differ diff --git a/assets/docker_image.png b/assets/docker_image.png new file mode 100644 index 000000000..f00d38d8d Binary files /dev/null and b/assets/docker_image.png differ diff --git a/assets/docker_image0.png b/assets/docker_image0.png new file mode 100644 index 000000000..f00d38d8d Binary files /dev/null and b/assets/docker_image0.png differ diff --git a/assets/dockerhub_autobuild.png b/assets/dockerhub_autobuild.png new file mode 100644 index 000000000..d726d4e5b Binary files /dev/null and b/assets/dockerhub_autobuild.png differ diff --git a/assets/dockerhub_autobuild0.png b/assets/dockerhub_autobuild0.png new file mode 100644 index 000000000..d726d4e5b Binary files /dev/null and b/assets/dockerhub_autobuild0.png differ diff --git a/assets/dockerhub_autobuilds.png b/assets/dockerhub_autobuilds.png new file mode 100644 index 000000000..28fff8ace Binary files /dev/null and b/assets/dockerhub_autobuilds.png differ diff --git a/assets/dockerhub_autobuilds0.png b/assets/dockerhub_autobuilds0.png new file mode 100644 index 000000000..28fff8ace Binary files /dev/null and b/assets/dockerhub_autobuilds0.png differ diff --git a/assets/dockerhub_buildsettings.png b/assets/dockerhub_buildsettings.png new file mode 100644 index 000000000..aa2950e59 Binary files /dev/null and b/assets/dockerhub_buildsettings.png differ diff --git a/assets/dockerhub_buildsettings0.png b/assets/dockerhub_buildsettings0.png new file mode 100644 index 000000000..aa2950e59 Binary files /dev/null and b/assets/dockerhub_buildsettings0.png differ diff --git a/assets/dockerhub_create.png b/assets/dockerhub_create.png new file mode 100644 index 000000000..b998ab277 Binary files /dev/null and b/assets/dockerhub_create.png differ diff --git a/assets/dockerhub_create0.png b/assets/dockerhub_create0.png new file mode 100644 index 000000000..b998ab277 Binary files /dev/null and b/assets/dockerhub_create0.png differ diff --git a/assets/dockerhub_createrepo.png b/assets/dockerhub_createrepo.png new file mode 100644 index 000000000..877d39804 Binary files /dev/null and b/assets/dockerhub_createrepo.png differ diff --git a/assets/dockerhub_createrepo0.png b/assets/dockerhub_createrepo0.png new file mode 100644 index 000000000..877d39804 Binary files /dev/null and b/assets/dockerhub_createrepo0.png differ diff --git a/assets/envs.png b/assets/envs.png new file mode 100644 index 000000000..f65abcb7d Binary files /dev/null and b/assets/envs.png differ diff --git a/assets/evolutionofcyverse.png b/assets/evolutionofcyverse.png new file mode 100644 index 000000000..7451c9d8a Binary files /dev/null and b/assets/evolutionofcyverse.png differ diff --git a/assets/evolutionofcyverse0.png b/assets/evolutionofcyverse0.png new file mode 100644 index 000000000..7451c9d8a Binary files /dev/null and b/assets/evolutionofcyverse0.png differ diff --git a/assets/f1000.png b/assets/f1000.png new file mode 100644 index 000000000..15c5a33b5 Binary files /dev/null and b/assets/f1000.png differ diff --git a/assets/f10000.png b/assets/f10000.png new file mode 100644 index 000000000..15c5a33b5 Binary files /dev/null and b/assets/f10000.png differ diff --git a/assets/fastqe.png b/assets/fastqe.png new file mode 100644 index 000000000..6656437ed Binary files /dev/null and b/assets/fastqe.png differ diff --git a/assets/fastqe0.png b/assets/fastqe0.png new file mode 100644 index 000000000..6656437ed Binary files /dev/null and b/assets/fastqe0.png differ diff --git a/assets/five_schools.png b/assets/five_schools.png new file mode 100644 index 000000000..285f75af4 Binary files /dev/null and b/assets/five_schools.png differ diff --git a/assets/foss-main.png b/assets/foss-main.png new file mode 100644 index 000000000..3ce696219 Binary files /dev/null and b/assets/foss-main.png differ diff --git a/assets/foss-main0.png b/assets/foss-main0.png new file mode 100644 index 000000000..13a42a2cb Binary files /dev/null and b/assets/foss-main0.png differ diff --git a/assets/foss_folder_contents_de2.png b/assets/foss_folder_contents_de2.png new file mode 100644 index 000000000..c88162c1c Binary files /dev/null and b/assets/foss_folder_contents_de2.png differ diff --git a/assets/foss_folder_de2.png b/assets/foss_folder_de2.png new file mode 100644 index 000000000..c9cf5b401 Binary files /dev/null and b/assets/foss_folder_de2.png differ diff --git a/assets/foss_title.png b/assets/foss_title.png new file mode 100644 index 000000000..16819c7c3 Binary files /dev/null and b/assets/foss_title.png differ diff --git a/assets/foss_title_2024F.jpg b/assets/foss_title_2024F.jpg new file mode 100644 index 000000000..1e41eb8d3 Binary files /dev/null and b/assets/foss_title_2024F.jpg differ diff --git a/assets/git_1.png b/assets/git_1.png new file mode 100644 index 000000000..e5060458e Binary files /dev/null and b/assets/git_1.png differ diff --git a/assets/git_10.png b/assets/git_10.png new file mode 100644 index 000000000..ed8f905d7 Binary files /dev/null and b/assets/git_10.png differ diff --git a/assets/git_11.png b/assets/git_11.png new file mode 100644 index 000000000..76a10ce82 Binary files /dev/null and b/assets/git_11.png differ diff --git a/assets/git_12.png b/assets/git_12.png new file mode 100644 index 000000000..ac7543e7b Binary files /dev/null and b/assets/git_12.png differ diff --git a/assets/git_13.png b/assets/git_13.png new file mode 100644 index 000000000..a6dcc6629 Binary files /dev/null and b/assets/git_13.png differ diff --git a/assets/git_14.png b/assets/git_14.png new file mode 100644 index 000000000..ba600054b Binary files /dev/null and b/assets/git_14.png differ diff --git a/assets/git_15.png b/assets/git_15.png new file mode 100644 index 000000000..8444cd048 Binary files /dev/null and b/assets/git_15.png differ diff --git a/assets/git_16.png b/assets/git_16.png new file mode 100644 index 000000000..1ba4e9c66 Binary files /dev/null and b/assets/git_16.png differ diff --git a/assets/git_17.png b/assets/git_17.png new file mode 100644 index 000000000..67b95cbba Binary files /dev/null and b/assets/git_17.png differ diff --git a/assets/git_18.png b/assets/git_18.png new file mode 100644 index 000000000..cfb6e7cb9 Binary files /dev/null and b/assets/git_18.png differ diff --git a/assets/git_19.png b/assets/git_19.png new file mode 100644 index 000000000..0334a29ad Binary files /dev/null and b/assets/git_19.png differ diff --git a/assets/git_2.png b/assets/git_2.png new file mode 100644 index 000000000..a4b4914c0 Binary files /dev/null and b/assets/git_2.png differ diff --git a/assets/git_20.png b/assets/git_20.png new file mode 100644 index 000000000..98d722459 Binary files /dev/null and b/assets/git_20.png differ diff --git a/assets/git_21.png b/assets/git_21.png new file mode 100644 index 000000000..e52fc27e8 Binary files /dev/null and b/assets/git_21.png differ diff --git a/assets/git_3.png b/assets/git_3.png new file mode 100644 index 000000000..82622e57c Binary files /dev/null and b/assets/git_3.png differ diff --git a/assets/git_4.png b/assets/git_4.png new file mode 100644 index 000000000..2ceec6cfd Binary files /dev/null and b/assets/git_4.png differ diff --git a/assets/git_5.png b/assets/git_5.png new file mode 100644 index 000000000..356d7a23a Binary files /dev/null and b/assets/git_5.png differ diff --git a/assets/git_6.png b/assets/git_6.png new file mode 100644 index 000000000..ca9cee5e2 Binary files /dev/null and b/assets/git_6.png differ diff --git a/assets/git_7.png b/assets/git_7.png new file mode 100644 index 000000000..9489dd5f1 Binary files /dev/null and b/assets/git_7.png differ diff --git a/assets/git_8.png b/assets/git_8.png new file mode 100644 index 000000000..8f4242fa3 Binary files /dev/null and b/assets/git_8.png differ diff --git a/assets/git_9.png b/assets/git_9.png new file mode 100644 index 000000000..02183eee6 Binary files /dev/null and b/assets/git_9.png differ diff --git a/assets/github-logo.png b/assets/github-logo.png new file mode 100644 index 000000000..8075d5d0a Binary files /dev/null and b/assets/github-logo.png differ diff --git a/assets/github-logo_large.png b/assets/github-logo_large.png new file mode 100644 index 000000000..cf5cdcb01 Binary files /dev/null and b/assets/github-logo_large.png differ diff --git a/assets/github_shot1.png b/assets/github_shot1.png new file mode 100644 index 000000000..ec4f8c60a Binary files /dev/null and b/assets/github_shot1.png differ diff --git a/assets/github_shot2.png b/assets/github_shot2.png new file mode 100644 index 000000000..889040a54 Binary files /dev/null and b/assets/github_shot2.png differ diff --git a/assets/github_shot3.png b/assets/github_shot3.png new file mode 100644 index 000000000..5aa2611e0 Binary files /dev/null and b/assets/github_shot3.png differ diff --git a/assets/gitlab_logo.png b/assets/gitlab_logo.png new file mode 100644 index 000000000..76d54d1c2 Binary files /dev/null and b/assets/gitlab_logo.png differ diff --git a/assets/gitrepo.png b/assets/gitrepo.png new file mode 100644 index 000000000..cfd5f24fb Binary files /dev/null and b/assets/gitrepo.png differ diff --git a/assets/homeicon.png b/assets/homeicon.png new file mode 100644 index 000000000..f06bed059 Binary files /dev/null and b/assets/homeicon.png differ diff --git a/assets/homeicon0.png b/assets/homeicon0.png new file mode 100644 index 000000000..f06bed059 Binary files /dev/null and b/assets/homeicon0.png differ diff --git a/assets/homeicon1.png b/assets/homeicon1.png new file mode 100644 index 000000000..f06bed059 Binary files /dev/null and b/assets/homeicon1.png differ diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 000000000..1cf13b9f9 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/img_building_1.png b/assets/img_building_1.png new file mode 100644 index 000000000..c3e77ac24 Binary files /dev/null and b/assets/img_building_1.png differ diff --git a/assets/img_building_10.png b/assets/img_building_10.png new file mode 100644 index 000000000..c3e77ac24 Binary files /dev/null and b/assets/img_building_10.png differ diff --git a/assets/img_building_3.png b/assets/img_building_3.png new file mode 100644 index 000000000..7aefe8b18 Binary files /dev/null and b/assets/img_building_3.png differ diff --git a/assets/img_building_30.png b/assets/img_building_30.png new file mode 100644 index 000000000..7aefe8b18 Binary files /dev/null and b/assets/img_building_30.png differ diff --git a/assets/img_building_4.png b/assets/img_building_4.png new file mode 100644 index 000000000..3907fbe9b Binary files /dev/null and b/assets/img_building_4.png differ diff --git a/assets/img_building_40.png b/assets/img_building_40.png new file mode 100644 index 000000000..3907fbe9b Binary files /dev/null and b/assets/img_building_40.png differ diff --git a/assets/img_building_5.png b/assets/img_building_5.png new file mode 100644 index 000000000..3233b95ec Binary files /dev/null and b/assets/img_building_5.png differ diff --git a/assets/img_building_50.png b/assets/img_building_50.png new file mode 100644 index 000000000..3233b95ec Binary files /dev/null and b/assets/img_building_50.png differ diff --git a/assets/img_building_6.png b/assets/img_building_6.png new file mode 100644 index 000000000..38477c0d1 Binary files /dev/null and b/assets/img_building_6.png differ diff --git a/assets/img_building_60.png b/assets/img_building_60.png new file mode 100644 index 000000000..38477c0d1 Binary files /dev/null and b/assets/img_building_60.png differ diff --git a/assets/img_building_7.png b/assets/img_building_7.png new file mode 100644 index 000000000..1b59f438e Binary files /dev/null and b/assets/img_building_7.png differ diff --git a/assets/img_building_70.png b/assets/img_building_70.png new file mode 100644 index 000000000..1b59f438e Binary files /dev/null and b/assets/img_building_70.png differ diff --git a/assets/img_building_8.png b/assets/img_building_8.png new file mode 100644 index 000000000..8d02ed2c6 Binary files /dev/null and b/assets/img_building_8.png differ diff --git a/assets/img_building_80.png b/assets/img_building_80.png new file mode 100644 index 000000000..8d02ed2c6 Binary files /dev/null and b/assets/img_building_80.png differ diff --git a/assets/img_building_9.png b/assets/img_building_9.png new file mode 100644 index 000000000..6f1117b1b Binary files /dev/null and b/assets/img_building_9.png differ diff --git a/assets/img_building_90.png b/assets/img_building_90.png new file mode 100644 index 000000000..6f1117b1b Binary files /dev/null and b/assets/img_building_90.png differ diff --git a/assets/instructors_code.png b/assets/instructors_code.png new file mode 100644 index 000000000..0a3fdc8e1 Binary files /dev/null and b/assets/instructors_code.png differ diff --git a/assets/instructors_code0.png b/assets/instructors_code0.png new file mode 100644 index 000000000..0a3fdc8e1 Binary files /dev/null and b/assets/instructors_code0.png differ diff --git a/assets/intercom.png b/assets/intercom.png new file mode 100644 index 000000000..6c578f63f Binary files /dev/null and b/assets/intercom.png differ diff --git a/assets/intercomlogo.png b/assets/intercomlogo.png new file mode 100644 index 000000000..5b4247cac Binary files /dev/null and b/assets/intercomlogo.png differ diff --git a/assets/intercomlogo0.png b/assets/intercomlogo0.png new file mode 100644 index 000000000..5b4247cac Binary files /dev/null and b/assets/intercomlogo0.png differ diff --git a/assets/javascripts/bundle.56dfad97.min.js b/assets/javascripts/bundle.56dfad97.min.js new file mode 100644 index 000000000..1df62cd7d --- /dev/null +++ b/assets/javascripts/bundle.56dfad97.min.js @@ -0,0 +1,16 @@ +"use strict";(()=>{var Fi=Object.create;var gr=Object.defineProperty;var Wi=Object.getOwnPropertyDescriptor;var Ui=Object.getOwnPropertyNames,Vt=Object.getOwnPropertySymbols,Di=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,io=Object.prototype.propertyIsEnumerable;var no=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,$=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&no(e,r,t[r]);if(Vt)for(var r of Vt(t))io.call(t,r)&&no(e,r,t[r]);return e};var ao=(e,t)=>{var r={};for(var o in e)yr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Vt)for(var o of Vt(e))t.indexOf(o)<0&&io.call(e,o)&&(r[o]=e[o]);return r};var xr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Vi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ui(t))!yr.call(e,n)&&n!==r&&gr(e,n,{get:()=>t[n],enumerable:!(o=Wi(t,n))||o.enumerable});return e};var Lt=(e,t,r)=>(r=e!=null?Fi(Di(e)):{},Vi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var so=(e,t,r)=>new Promise((o,n)=>{var i=p=>{try{s(r.next(p))}catch(c){n(c)}},a=p=>{try{s(r.throw(p))}catch(c){n(c)}},s=p=>p.done?o(p.value):Promise.resolve(p.value).then(i,a);s((r=r.apply(e,t)).next())});var po=xr((Er,co)=>{(function(e,t){typeof Er=="object"&&typeof co!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(Er,function(){"use strict";function e(r){var o=!0,n=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function p(k){var ft=k.type,qe=k.tagName;return!!(qe==="INPUT"&&a[ft]&&!k.readOnly||qe==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function c(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(s(r.activeElement)&&c(r.activeElement),o=!0)}function u(k){o=!1}function d(k){s(k.target)&&(o||p(k.target))&&c(k.target)}function y(k){s(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function M(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function te(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,te())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",M,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",y,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var qr=xr((ly,Sn)=>{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var ka=/["'&<>]/;Sn.exports=Ha;function Ha(e){var t=""+e,r=ka.exec(t);if(!r)return t;var o,n="",i=0,a=0;for(i=r.index;i{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof It=="object"&&typeof Yr=="object"?Yr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof It=="object"?It.ClipboardJS=r():t.ClipboardJS=r()})(It,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return ji}});var a=i(279),s=i.n(a),p=i(370),c=i.n(p),l=i(817),f=i.n(l);function u(V){try{return document.execCommand(V)}catch(A){return!1}}var d=function(A){var L=f()(A);return u("cut"),L},y=d;function M(V){var A=document.documentElement.getAttribute("dir")==="rtl",L=document.createElement("textarea");L.style.fontSize="12pt",L.style.border="0",L.style.padding="0",L.style.margin="0",L.style.position="absolute",L.style[A?"right":"left"]="-9999px";var F=window.pageYOffset||document.documentElement.scrollTop;return L.style.top="".concat(F,"px"),L.setAttribute("readonly",""),L.value=V,L}var X=function(A,L){var F=M(A);L.container.appendChild(F);var D=f()(F);return u("copy"),F.remove(),D},te=function(A){var L=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},F="";return typeof A=="string"?F=X(A,L):A instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(A==null?void 0:A.type)?F=X(A.value,L):(F=f()(A),u("copy")),F},J=te;function k(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(L){return typeof L}:k=function(L){return L&&typeof Symbol=="function"&&L.constructor===Symbol&&L!==Symbol.prototype?"symbol":typeof L},k(V)}var ft=function(){var A=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},L=A.action,F=L===void 0?"copy":L,D=A.container,Y=A.target,$e=A.text;if(F!=="copy"&&F!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(Y!==void 0)if(Y&&k(Y)==="object"&&Y.nodeType===1){if(F==="copy"&&Y.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(F==="cut"&&(Y.hasAttribute("readonly")||Y.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if($e)return J($e,{container:D});if(Y)return F==="cut"?y(Y):J(Y,{container:D})},qe=ft;function Fe(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Fe=function(L){return typeof L}:Fe=function(L){return L&&typeof Symbol=="function"&&L.constructor===Symbol&&L!==Symbol.prototype?"symbol":typeof L},Fe(V)}function Ai(V,A){if(!(V instanceof A))throw new TypeError("Cannot call a class as a function")}function oo(V,A){for(var L=0;L0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof D.action=="function"?D.action:this.defaultAction,this.target=typeof D.target=="function"?D.target:this.defaultTarget,this.text=typeof D.text=="function"?D.text:this.defaultText,this.container=Fe(D.container)==="object"?D.container:document.body}},{key:"listenClick",value:function(D){var Y=this;this.listener=c()(D,"click",function($e){return Y.onClick($e)})}},{key:"onClick",value:function(D){var Y=D.delegateTarget||D.currentTarget,$e=this.action(Y)||"copy",Dt=qe({action:$e,container:this.container,target:this.target(Y),text:this.text(Y)});this.emit(Dt?"success":"error",{action:$e,text:Dt,trigger:Y,clearSelection:function(){Y&&Y.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(D){return vr("action",D)}},{key:"defaultTarget",value:function(D){var Y=vr("target",D);if(Y)return document.querySelector(Y)}},{key:"defaultText",value:function(D){return vr("text",D)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(D){var Y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(D,Y)}},{key:"cut",value:function(D){return y(D)}},{key:"isSupported",value:function(){var D=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],Y=typeof D=="string"?[D]:D,$e=!!document.queryCommandSupported;return Y.forEach(function(Dt){$e=$e&&!!document.queryCommandSupported(Dt)}),$e}}]),L}(s()),ji=Ii},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,p){for(;s&&s.nodeType!==n;){if(typeof s.matches=="function"&&s.matches(p))return s;s=s.parentNode}}o.exports=a},438:function(o,n,i){var a=i(828);function s(l,f,u,d,y){var M=c.apply(this,arguments);return l.addEventListener(u,M,y),{destroy:function(){l.removeEventListener(u,M,y)}}}function p(l,f,u,d,y){return typeof l.addEventListener=="function"?s.apply(null,arguments):typeof u=="function"?s.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(M){return s(M,f,u,d,y)}))}function c(l,f,u,d){return function(y){y.delegateTarget=a(y.target,f),y.delegateTarget&&d.call(l,y)}}o.exports=p},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(o,n,i){var a=i(879),s=i(438);function p(u,d,y){if(!u&&!d&&!y)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(y))throw new TypeError("Third argument must be a Function");if(a.node(u))return c(u,d,y);if(a.nodeList(u))return l(u,d,y);if(a.string(u))return f(u,d,y);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(u,d,y){return u.addEventListener(d,y),{destroy:function(){u.removeEventListener(d,y)}}}function l(u,d,y){return Array.prototype.forEach.call(u,function(M){M.addEventListener(d,y)}),{destroy:function(){Array.prototype.forEach.call(u,function(M){M.removeEventListener(d,y)})}}}function f(u,d,y){return s(document.body,u,d,y)}o.exports=p},817:function(o){function n(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var p=window.getSelection(),c=document.createRange();c.selectNodeContents(i),p.removeAllRanges(),p.addRange(c),a=p.toString()}return a}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,a,s){var p=this.e||(this.e={});return(p[i]||(p[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var p=this;function c(){p.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),p=0,c=s.length;for(p;p0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function N(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],a;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(s){a={error:s}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(a)throw a.error}}return i}function q(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||p(d,M)})},y&&(n[d]=y(n[d])))}function p(d,y){try{c(o[d](y))}catch(M){u(i[0][3],M)}}function c(d){d.value instanceof nt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){p("next",d)}function f(d){p("throw",d)}function u(d,y){d(y),i.shift(),i.length&&p(i[0][0],i[0][1])}}function fo(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof he=="function"?he(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(a){return new Promise(function(s,p){a=e[i](a),n(s,p,a.done,a.value)})}}function n(i,a,s,p){Promise.resolve(p).then(function(c){i({value:c,done:s})},a)}}function H(e){return typeof e=="function"}function ut(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var zt=ut(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Qe(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var We=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=he(a),p=s.next();!p.done;p=s.next()){var c=p.value;c.remove(this)}}catch(M){t={error:M}}finally{try{p&&!p.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var l=this.initialTeardown;if(H(l))try{l()}catch(M){i=M instanceof zt?M.errors:[M]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=he(f),d=u.next();!d.done;d=u.next()){var y=d.value;try{uo(y)}catch(M){i=i!=null?i:[],M instanceof zt?i=q(q([],N(i)),N(M.errors)):i.push(M)}}}catch(M){o={error:M}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new zt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)uo(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Qe(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Qe(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Tr=We.EMPTY;function qt(e){return e instanceof We||e&&"closed"in e&&H(e.remove)&&H(e.add)&&H(e.unsubscribe)}function uo(e){H(e)?e():e.unsubscribe()}var Pe={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var dt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,a=n.isStopped,s=n.observers;return i||a?Tr:(this.currentObservers=null,s.push(r),new We(function(){o.currentObservers=null,Qe(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,a=o.isStopped;n?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new wo(r,o)},t}(j);var wo=function(e){oe(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Tr},t}(g);var _r=function(e){oe(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t}(g);var At={now:function(){return(At.delegate||Date).now()},delegate:void 0};var Ct=function(e){oe(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=At);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,a=o._infiniteTimeWindow,s=o._timestampProvider,p=o._windowTime;n||(i.push(r),!a&&i.push(s.now()+p)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,a=n._buffer,s=a.slice(),p=0;p0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t}(gt);var Oo=function(e){oe(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t}(yt);var kr=new Oo(So);var Mo=function(e){oe(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=vt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var a=r.actions;o!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==o&&(vt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(gt);var Lo=function(e){oe(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(yt);var me=new Lo(Mo);var S=new j(function(e){return e.complete()});function Yt(e){return e&&H(e.schedule)}function Hr(e){return e[e.length-1]}function Xe(e){return H(Hr(e))?e.pop():void 0}function ke(e){return Yt(Hr(e))?e.pop():void 0}function Bt(e,t){return typeof Hr(e)=="number"?e.pop():t}var xt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Gt(e){return H(e==null?void 0:e.then)}function Jt(e){return H(e[bt])}function Xt(e){return Symbol.asyncIterator&&H(e==null?void 0:e[Symbol.asyncIterator])}function Zt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ji(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var er=Ji();function tr(e){return H(e==null?void 0:e[er])}function rr(e){return mo(this,arguments,function(){var r,o,n,i;return Nt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,nt(r.read())];case 3:return o=a.sent(),n=o.value,i=o.done,i?[4,nt(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,nt(n)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function or(e){return H(e==null?void 0:e.getReader)}function W(e){if(e instanceof j)return e;if(e!=null){if(Jt(e))return Xi(e);if(xt(e))return Zi(e);if(Gt(e))return ea(e);if(Xt(e))return _o(e);if(tr(e))return ta(e);if(or(e))return ra(e)}throw Zt(e)}function Xi(e){return new j(function(t){var r=e[bt]();if(H(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Zi(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?b(function(n,i){return e(n,i,o)}):le,Te(1),r?De(t):qo(function(){return new ir}))}}function jr(e){return e<=0?function(){return S}:E(function(t,r){var o=[];t.subscribe(T(r,function(n){o.push(n),e=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new g}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,p=s===void 0?!0:s;return function(c){var l,f,u,d=0,y=!1,M=!1,X=function(){f==null||f.unsubscribe(),f=void 0},te=function(){X(),l=u=void 0,y=M=!1},J=function(){var k=l;te(),k==null||k.unsubscribe()};return E(function(k,ft){d++,!M&&!y&&X();var qe=u=u!=null?u:r();ft.add(function(){d--,d===0&&!M&&!y&&(f=Wr(J,p))}),qe.subscribe(ft),!l&&d>0&&(l=new at({next:function(Fe){return qe.next(Fe)},error:function(Fe){M=!0,X(),f=Wr(te,n,Fe),qe.error(Fe)},complete:function(){y=!0,X(),f=Wr(te,a),qe.complete()}}),W(k).subscribe(l))})(c)}}function Wr(e,t){for(var r=[],o=2;oe.next(document)),e}function P(e,t=document){return Array.from(t.querySelectorAll(e))}function R(e,t=document){let r=fe(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function fe(e,t=document){return t.querySelector(e)||void 0}function Ie(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var xa=O(h(document.body,"focusin"),h(document.body,"focusout")).pipe(_e(1),Q(void 0),m(()=>Ie()||document.body),G(1));function et(e){return xa.pipe(m(t=>e.contains(t)),K())}function $t(e,t){return C(()=>O(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?Ht(r=>Me(+!r*t)):le,Q(e.matches(":hover"))))}function Go(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)Go(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)Go(o,n);return o}function sr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function Tt(e){let t=x("script",{src:e});return C(()=>(document.head.appendChild(t),O(h(t,"load"),h(t,"error").pipe(v(()=>$r(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),_(()=>document.head.removeChild(t)),Te(1))))}var Jo=new g,Ea=C(()=>typeof ResizeObserver=="undefined"?Tt("https://unpkg.com/resize-observer-polyfill"):I(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>Jo.next(t)))),v(e=>O(Ye,I(e)).pipe(_(()=>e.disconnect()))),G(1));function ce(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ea.pipe(w(r=>r.observe(t)),v(r=>Jo.pipe(b(o=>o.target===t),_(()=>r.unobserve(t)))),m(()=>ce(e)),Q(ce(e)))}function St(e){return{width:e.scrollWidth,height:e.scrollHeight}}function cr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function Xo(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Ve(e){return{x:e.offsetLeft,y:e.offsetTop}}function Zo(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function en(e){return O(h(window,"load"),h(window,"resize")).pipe(Le(0,me),m(()=>Ve(e)),Q(Ve(e)))}function pr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ne(e){return O(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe(Le(0,me),m(()=>pr(e)),Q(pr(e)))}var tn=new g,wa=C(()=>I(new IntersectionObserver(e=>{for(let t of e)tn.next(t)},{threshold:0}))).pipe(v(e=>O(Ye,I(e)).pipe(_(()=>e.disconnect()))),G(1));function tt(e){return wa.pipe(w(t=>t.observe(e)),v(t=>tn.pipe(b(({target:r})=>r===e),_(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function rn(e,t=16){return Ne(e).pipe(m(({y:r})=>{let o=ce(e),n=St(e);return r>=n.height-o.height-t}),K())}var lr={drawer:R("[data-md-toggle=drawer]"),search:R("[data-md-toggle=search]")};function on(e){return lr[e].checked}function Je(e,t){lr[e].checked!==t&&lr[e].click()}function ze(e){let t=lr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function Ta(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Sa(){return O(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function nn(){let e=h(window,"keydown").pipe(b(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:on("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),b(({mode:t,type:r})=>{if(t==="global"){let o=Ie();if(typeof o!="undefined")return!Ta(o,r)}return!0}),pe());return Sa().pipe(v(t=>t?S:e))}function ye(){return new URL(location.href)}function lt(e,t=!1){if(B("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function an(){return new g}function sn(){return location.hash.slice(1)}function cn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Oa(e){return O(h(window,"hashchange"),e).pipe(m(sn),Q(sn()),b(t=>t.length>0),G(1))}function pn(e){return Oa(e).pipe(m(t=>fe(`[id="${t}"]`)),b(t=>typeof t!="undefined"))}function Pt(e){let t=matchMedia(e);return ar(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function ln(){let e=matchMedia("print");return O(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function Nr(e,t){return e.pipe(v(r=>r?t():S))}function zr(e,t){return new j(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let a=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+a*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function je(e,t){return zr(e,t).pipe(v(r=>r.text()),m(r=>JSON.parse(r)),G(1))}function mn(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),G(1))}function fn(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),G(1))}function un(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function dn(){return O(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(un),Q(un()))}function hn(){return{width:innerWidth,height:innerHeight}}function bn(){return h(window,"resize",{passive:!0}).pipe(m(hn),Q(hn()))}function vn(){return z([dn(),bn()]).pipe(m(([e,t])=>({offset:e,size:t})),G(1))}function mr(e,{viewport$:t,header$:r}){let o=t.pipe(ee("size")),n=z([o,r]).pipe(m(()=>Ve(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:a,size:s},{x:p,y:c}])=>({offset:{x:a.x-p,y:a.y-c+i},size:s})))}function Ma(e){return h(e,"message",t=>t.data)}function La(e){let t=new g;return t.subscribe(r=>e.postMessage(r)),t}function gn(e,t=new Worker(e)){let r=Ma(t),o=La(t),n=new g;n.subscribe(o);let i=o.pipe(Z(),ie(!0));return n.pipe(Z(),Re(r.pipe(U(i))),pe())}var _a=R("#__config"),Ot=JSON.parse(_a.textContent);Ot.base=`${new URL(Ot.base,ye())}`;function xe(){return Ot}function B(e){return Ot.features.includes(e)}function Ee(e,t){return typeof t!="undefined"?Ot.translations[e].replace("#",t.toString()):Ot.translations[e]}function Se(e,t=document){return R(`[data-md-component=${e}]`,t)}function ae(e,t=document){return P(`[data-md-component=${e}]`,t)}function Aa(e){let t=R(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>R(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function yn(e){if(!B("announce.dismiss")||!e.childElementCount)return S;if(!e.hidden){let t=R(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return C(()=>{let t=new g;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Aa(e).pipe(w(r=>t.next(r)),_(()=>t.complete()),m(r=>$({ref:e},r)))})}function Ca(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function xn(e,t){let r=new g;return r.subscribe(({hidden:o})=>{e.hidden=o}),Ca(e,t).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))}function Rt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function En(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function wn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Rt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Rt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Tn(e){return x("button",{class:"md-clipboard md-icon",title:Ee("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}var On=Lt(qr());function Qr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(p=>!e.terms[p]).reduce((p,c)=>[...p,x("del",null,(0,On.default)(c))," "],[]).slice(0,-1),i=xe(),a=new URL(e.location,i.base);B("search.highlight")&&a.searchParams.set("h",Object.entries(e.terms).filter(([,p])=>p).reduce((p,[c])=>`${p} ${c}`.trim(),""));let{tags:s}=xe();return x("a",{href:`${a}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(p=>{let c=s?p in s?`md-tag-icon md-tag--${s[p]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${c}`},p)}),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Ee("search.result.term.missing"),": ",...n)))}function Mn(e){let t=e[0].score,r=[...e],o=xe(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),a=r.findIndex(l=>l.scoreQr(l,1)),...p.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,p.length>0&&p.length===1?Ee("search.result.more.one"):Ee("search.result.more.other",p.length))),...p.map(l=>Qr(l,1)))]:[]];return x("li",{class:"md-search-result__item"},c)}function Ln(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?sr(r):r)))}function Kr(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function _n(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function $a(e){var o;let t=xe(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function An(e,t){var o;let r=xe();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Ee("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map($a)))}var Pa=0;function Ra(e){let t=z([et(e),$t(e)]).pipe(m(([o,n])=>o||n),K()),r=C(()=>Xo(e)).pipe(ne(Ne),pt(1),He(t),m(()=>Zo(e)));return t.pipe(Ae(o=>o),v(()=>z([t,r])),m(([o,n])=>({active:o,offset:n})),pe())}function Ia(e,t){let{content$:r,viewport$:o}=t,n=`__tooltip2_${Pa++}`;return C(()=>{let i=new g,a=new _r(!1);i.pipe(Z(),ie(!1)).subscribe(a);let s=a.pipe(Ht(c=>Me(+!c*250,kr)),K(),v(c=>c?r:S),w(c=>c.id=n),pe());z([i.pipe(m(({active:c})=>c)),s.pipe(v(c=>$t(c,250)),Q(!1))]).pipe(m(c=>c.some(l=>l))).subscribe(a);let p=a.pipe(b(c=>c),re(s,o),m(([c,l,{size:f}])=>{let u=e.getBoundingClientRect(),d=u.width/2;if(l.role==="tooltip")return{x:d,y:8+u.height};if(u.y>=f.height/2){let{height:y}=ce(l);return{x:d,y:-16-y}}else return{x:d,y:16+u.height}}));return z([s,i,p]).subscribe(([c,{offset:l},f])=>{c.style.setProperty("--md-tooltip-host-x",`${l.x}px`),c.style.setProperty("--md-tooltip-host-y",`${l.y}px`),c.style.setProperty("--md-tooltip-x",`${f.x}px`),c.style.setProperty("--md-tooltip-y",`${f.y}px`),c.classList.toggle("md-tooltip2--top",f.y<0),c.classList.toggle("md-tooltip2--bottom",f.y>=0)}),a.pipe(b(c=>c),re(s,(c,l)=>l),b(c=>c.role==="tooltip")).subscribe(c=>{let l=ce(R(":scope > *",c));c.style.setProperty("--md-tooltip-width",`${l.width}px`),c.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(K(),ve(me),re(s)).subscribe(([c,l])=>{l.classList.toggle("md-tooltip2--active",c)}),z([a.pipe(b(c=>c)),s]).subscribe(([c,l])=>{l.role==="dialog"?(e.setAttribute("aria-controls",n),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",n)}),a.pipe(b(c=>!c)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ra(e).pipe(w(c=>i.next(c)),_(()=>i.complete()),m(c=>$({ref:e},c)))})}function mt(e,{viewport$:t},r=document.body){return Ia(e,{content$:new j(o=>{let n=e.title,i=En(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t})}function ja(e,t){let r=C(()=>z([en(e),Ne(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:a,height:s}=ce(e);return{x:o-i.x+a/2,y:n-i.y+s/2}}));return et(e).pipe(v(o=>r.pipe(m(n=>({active:o,offset:n})),Te(+!o||1/0))))}function Cn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return C(()=>{let i=new g,a=i.pipe(Z(),ie(!0));return i.subscribe({next({offset:s}){e.style.setProperty("--md-tooltip-x",`${s.x}px`),e.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),tt(e).pipe(U(a)).subscribe(s=>{e.toggleAttribute("data-md-visible",s)}),O(i.pipe(b(({active:s})=>s)),i.pipe(_e(250),b(({active:s})=>!s))).subscribe({next({active:s}){s?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Le(16,me)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(pt(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?e.style.setProperty("--md-tooltip-0",`${-s}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(U(a),b(s=>!(s.metaKey||s.ctrlKey))).subscribe(s=>{s.stopPropagation(),s.preventDefault()}),h(n,"mousedown").pipe(U(a),re(i)).subscribe(([s,{active:p}])=>{var c;if(s.button!==0||s.metaKey||s.ctrlKey)s.preventDefault();else if(p){s.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(c=Ie())==null||c.blur()}}),r.pipe(U(a),b(s=>s===o),Ge(125)).subscribe(()=>e.focus()),ja(e,t).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))})}function Fa(e){return e.tagName==="CODE"?P(".c, .c1, .cm",e):[e]}function Wa(e){let t=[];for(let r of Fa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let a;for(;a=/(\(\d+\))(!)?/.exec(i.textContent);){let[,s,p]=a;if(typeof p=="undefined"){let c=i.splitText(a.index);i=c.splitText(s.length),t.push(c)}else{i.textContent=s,t.push(i);break}}}}return t}function kn(e,t){t.append(...Array.from(e.childNodes))}function fr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,a=new Map;for(let s of Wa(t)){let[,p]=s.textContent.match(/\((\d+)\)/);fe(`:scope > li:nth-child(${p})`,e)&&(a.set(p,wn(p,i)),s.replaceWith(a.get(p)))}return a.size===0?S:C(()=>{let s=new g,p=s.pipe(Z(),ie(!0)),c=[];for(let[l,f]of a)c.push([R(".md-typeset",f),R(`:scope > li:nth-child(${l})`,e)]);return o.pipe(U(p)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of c)l?kn(f,u):kn(u,f)}),O(...[...a].map(([,l])=>Cn(l,t,{target$:r}))).pipe(_(()=>s.complete()),pe())})}function Hn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Hn(t)}}function $n(e,t){return C(()=>{let r=Hn(e);return typeof r!="undefined"?fr(r,e,t):S})}var Pn=Lt(Br());var Ua=0;function Rn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Rn(t)}}function Da(e){return ge(e).pipe(m(({width:t})=>({scrollable:St(e).width>t})),ee("scrollable"))}function In(e,t){let{matches:r}=matchMedia("(hover)"),o=C(()=>{let n=new g,i=n.pipe(jr(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let a=[];if(Pn.default.isSupported()&&(e.closest(".copy")||B("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${Ua++}`;let l=Tn(c.id);c.insertBefore(l,e),B("content.tooltips")&&a.push(mt(l,{viewport$}))}let s=e.closest(".highlight");if(s instanceof HTMLElement){let c=Rn(s);if(typeof c!="undefined"&&(s.classList.contains("annotate")||B("content.code.annotate"))){let l=fr(c,e,t);a.push(ge(s).pipe(U(i),m(({width:f,height:u})=>f&&u),K(),v(f=>f?l:S)))}}return P(":scope > span[id]",e).length&&e.classList.add("md-code__content"),Da(e).pipe(w(c=>n.next(c)),_(()=>n.complete()),m(c=>$({ref:e},c)),Re(...a))});return B("content.lazy")?tt(e).pipe(b(n=>n),Te(1),v(()=>o)):o}function Va(e,{target$:t,print$:r}){let o=!0;return O(t.pipe(m(n=>n.closest("details:not([open])")),b(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(b(n=>n||!o),w(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function jn(e,t){return C(()=>{let r=new g;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Va(e,t).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}var Fn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var Gr,za=0;function qa(){return typeof mermaid=="undefined"||mermaid instanceof Element?Tt("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):I(void 0)}function Wn(e){return e.classList.remove("mermaid"),Gr||(Gr=qa().pipe(w(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Fn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),G(1))),Gr.subscribe(()=>so(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${za++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),a=r.attachShadow({mode:"closed"});a.innerHTML=n,e.replaceWith(r),i==null||i(a)})),Gr.pipe(m(()=>({ref:e})))}var Un=x("table");function Dn(e){return e.replaceWith(Un),Un.replaceWith(_n(e)),I({ref:e})}function Qa(e){let t=e.find(r=>r.checked)||e[0];return O(...e.map(r=>h(r,"change").pipe(m(()=>R(`label[for="${r.id}"]`))))).pipe(Q(R(`label[for="${t.id}"]`)),m(r=>({active:r})))}function Vn(e,{viewport$:t,target$:r}){let o=R(".tabbed-labels",e),n=P(":scope > input",e),i=Kr("prev");e.append(i);let a=Kr("next");return e.append(a),C(()=>{let s=new g,p=s.pipe(Z(),ie(!0));z([s,ge(e),tt(e)]).pipe(U(p),Le(1,me)).subscribe({next([{active:c},l]){let f=Ve(c),{width:u}=ce(c);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=pr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ne(o),ge(o)]).pipe(U(p)).subscribe(([c,l])=>{let f=St(o);i.hidden=c.x<16,a.hidden=c.x>f.width-l.width-16}),O(h(i,"click").pipe(m(()=>-1)),h(a,"click").pipe(m(()=>1))).pipe(U(p)).subscribe(c=>{let{width:l}=ce(o);o.scrollBy({left:l*c,behavior:"smooth"})}),r.pipe(U(p),b(c=>n.includes(c))).subscribe(c=>c.click()),o.classList.add("tabbed-labels--linked");for(let c of n){let l=R(`label[for="${c.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(U(p),b(f=>!(f.metaKey||f.ctrlKey)),w(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return B("content.tabs.link")&&s.pipe(Ce(1),re(t)).subscribe(([{active:c},{offset:l}])=>{let f=c.innerText.trim();if(c.hasAttribute("data-md-switching"))c.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let y of P("[data-tabs]"))for(let M of P(":scope > input",y)){let X=R(`label[for="${M.id}"]`);if(X!==c&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),M.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),s.pipe(U(p)).subscribe(()=>{for(let c of P("audio, video",e))c.pause()}),Qa(n).pipe(w(c=>s.next(c)),_(()=>s.complete()),m(c=>$({ref:e},c)))}).pipe(Ke(se))}function Nn(e,{viewport$:t,target$:r,print$:o}){return O(...P(".annotate:not(.highlight)",e).map(n=>$n(n,{target$:r,print$:o})),...P("pre:not(.mermaid) > code",e).map(n=>In(n,{target$:r,print$:o})),...P("pre.mermaid",e).map(n=>Wn(n)),...P("table:not([class])",e).map(n=>Dn(n)),...P("details",e).map(n=>jn(n,{target$:r,print$:o})),...P("[data-tabs]",e).map(n=>Vn(n,{viewport$:t,target$:r})),...P("[title]",e).filter(()=>B("content.tooltips")).map(n=>mt(n,{viewport$:t})))}function Ka(e,{alert$:t}){return t.pipe(v(r=>O(I(!0),I(!1).pipe(Ge(2e3))).pipe(m(o=>({message:r,active:o})))))}function zn(e,t){let r=R(".md-typeset",e);return C(()=>{let o=new g;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ka(e,t).pipe(w(n=>o.next(n)),_(()=>o.complete()),m(n=>$({ref:e},n)))})}var Ya=0;function Ba(e,t){document.body.append(e);let{width:r}=ce(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=cr(t),n=typeof o!="undefined"?Ne(o):I({x:0,y:0}),i=O(et(t),$t(t)).pipe(K());return z([i,n]).pipe(m(([a,s])=>{let{x:p,y:c}=Ve(t),l=ce(t),f=t.closest("table");return f&&t.parentElement&&(p+=f.offsetLeft+t.parentElement.offsetLeft,c+=f.offsetTop+t.parentElement.offsetTop),{active:a,offset:{x:p-s.x+l.width/2-r/2,y:c-s.y+l.height+8}}}))}function qn(e){let t=e.title;if(!t.length)return S;let r=`__tooltip_${Ya++}`,o=Rt(r,"inline"),n=R(".md-typeset",o);return n.innerHTML=t,C(()=>{let i=new g;return i.subscribe({next({offset:a}){o.style.setProperty("--md-tooltip-x",`${a.x}px`),o.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),O(i.pipe(b(({active:a})=>a)),i.pipe(_e(250),b(({active:a})=>!a))).subscribe({next({active:a}){a?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Le(16,me)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(pt(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?o.style.setProperty("--md-tooltip-0",`${-a}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Ba(o,e).pipe(w(a=>i.next(a)),_(()=>i.complete()),m(a=>$({ref:e},a)))}).pipe(Ke(se))}function Ga({viewport$:e}){if(!B("header.autohide"))return I(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Be(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),K()),o=ze("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),K(),v(n=>n?r:I(!1)),Q(!1))}function Qn(e,t){return C(()=>z([ge(e),Ga(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),K((r,o)=>r.height===o.height&&r.hidden===o.hidden),G(1))}function Kn(e,{header$:t,main$:r}){return C(()=>{let o=new g,n=o.pipe(Z(),ie(!0));o.pipe(ee("active"),He(t)).subscribe(([{active:a},{hidden:s}])=>{e.classList.toggle("md-header--shadow",a&&!s),e.hidden=s});let i=ue(P("[title]",e)).pipe(b(()=>B("content.tooltips")),ne(a=>qn(a)));return r.subscribe(o),t.pipe(U(n),m(a=>$({ref:e},a)),Re(i.pipe(U(n))))})}function Ja(e,{viewport$:t,header$:r}){return mr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=ce(e);return{active:o>=n}}),ee("active"))}function Yn(e,t){return C(()=>{let r=new g;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=fe(".md-content h1");return typeof o=="undefined"?S:Ja(o,t).pipe(w(n=>r.next(n)),_(()=>r.complete()),m(n=>$({ref:e},n)))})}function Bn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),K()),n=o.pipe(v(()=>ge(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ee("bottom"))));return z([o,n,t]).pipe(m(([i,{top:a,bottom:s},{offset:{y:p},size:{height:c}}])=>(c=Math.max(0,c-Math.max(0,a-p,i)-Math.max(0,c+p-s)),{offset:a-i,height:c,active:a-i<=p})),K((i,a)=>i.offset===a.offset&&i.height===a.height&&i.active===a.active))}function Xa(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return I(...e).pipe(ne(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),G(1))}function Gn(e){let t=P("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Pt("(prefers-color-scheme: light)");return C(()=>{let i=new g;return i.subscribe(a=>{if(document.body.setAttribute("data-md-color-switching",""),a.color.media==="(prefers-color-scheme)"){let s=matchMedia("(prefers-color-scheme: light)"),p=document.querySelector(s.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");a.color.scheme=p.getAttribute("data-md-color-scheme"),a.color.primary=p.getAttribute("data-md-color-primary"),a.color.accent=p.getAttribute("data-md-color-accent")}for(let[s,p]of Object.entries(a.color))document.body.setAttribute(`data-md-color-${s}`,p);for(let s=0;sa.key==="Enter"),re(i,(a,s)=>s)).subscribe(({index:a})=>{a=(a+1)%t.length,t[a].click(),t[a].focus()}),i.pipe(m(()=>{let a=Se("header"),s=window.getComputedStyle(a);return o.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(p=>(+p).toString(16).padStart(2,"0")).join("")})).subscribe(a=>r.content=`#${a}`),i.pipe(ve(se)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),Xa(t).pipe(U(n.pipe(Ce(1))),ct(),w(a=>i.next(a)),_(()=>i.complete()),m(a=>$({ref:e},a)))})}function Jn(e,{progress$:t}){return C(()=>{let r=new g;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(w(o=>r.next({value:o})),_(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Jr=Lt(Br());function Za(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Xn({alert$:e}){Jr.default.isSupported()&&new j(t=>{new Jr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||Za(R(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(w(t=>{t.trigger.focus()}),m(()=>Ee("clipboard.copied"))).subscribe(e)}function Zn(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function es(e,t){let r=new Map;for(let o of P("url",e)){let n=R("loc",o),i=[Zn(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let a of P("[rel=alternate]",o)){let s=a.getAttribute("href");s!=null&&i.push(Zn(new URL(s),t))}}return r}function ur(e){return fn(new URL("sitemap.xml",e)).pipe(m(t=>es(t,new URL(e))),de(()=>I(new Map)))}function ts(e,t){if(!(e.target instanceof Element))return S;let r=e.target.closest("a");if(r===null)return S;if(r.target||e.metaKey||e.ctrlKey)return S;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),I(new URL(r.href))):S}function ei(e){let t=new Map;for(let r of P(":scope > *",e.head))t.set(r.outerHTML,r);return t}function ti(e){for(let t of P("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return I(e)}function rs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...B("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=fe(o),i=fe(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=ei(document);for(let[o,n]of ei(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Se("container");return Ue(P("script",r)).pipe(v(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new j(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),S}),Z(),ie(document))}function ri({location$:e,viewport$:t,progress$:r}){let o=xe();if(location.protocol==="file:")return S;let n=ur(o.base);I(document).subscribe(ti);let i=h(document.body,"click").pipe(He(n),v(([p,c])=>ts(p,c)),pe()),a=h(window,"popstate").pipe(m(ye),pe());i.pipe(re(t)).subscribe(([p,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",p)}),O(i,a).subscribe(e);let s=e.pipe(ee("pathname"),v(p=>mn(p,{progress$:r}).pipe(de(()=>(lt(p,!0),S)))),v(ti),v(rs),pe());return O(s.pipe(re(e,(p,c)=>c)),s.pipe(v(()=>e),ee("pathname"),v(()=>e),ee("hash")),e.pipe(K((p,c)=>p.pathname===c.pathname&&p.hash===c.hash),v(()=>i),w(()=>history.back()))).subscribe(p=>{var c,l;history.state!==null||!p.hash?window.scrollTo(0,(l=(c=history.state)==null?void 0:c.y)!=null?l:0):(history.scrollRestoration="auto",cn(p.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(ee("offset"),_e(100)).subscribe(({offset:p})=>{history.replaceState(p,"")}),s}var oi=Lt(qr());function ni(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,a)=>`${i}${a}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return a=>(0,oi.default)(a).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function jt(e){return e.type===1}function dr(e){return e.type===3}function ii(e,t){let r=gn(e);return O(I(location.protocol!=="file:"),ze("search")).pipe(Ae(o=>o),v(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:B("search.suggest")}}})),r}function ai({document$:e}){let t=xe(),r=je(new URL("../versions.json",t.base)).pipe(de(()=>S)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:a,aliases:s})=>a===i||s.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),v(n=>h(document.body,"click").pipe(b(i=>!i.metaKey&&!i.ctrlKey),re(o),v(([i,a])=>{if(i.target instanceof Element){let s=i.target.closest("a");if(s&&!s.target&&n.has(s.href)){let p=s.href;return!i.target.closest(".md-version")&&n.get(p)===a?S:(i.preventDefault(),I(p))}}return S}),v(i=>ur(new URL(i)).pipe(m(a=>{let p=ye().href.replace(t.base,i);return a.has(p.split("#")[0])?new URL(p):new URL(i)})))))).subscribe(n=>lt(n,!0)),z([r,o]).subscribe(([n,i])=>{R(".md-header__topic").appendChild(An(n,i))}),e.pipe(v(()=>o)).subscribe(n=>{var a;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let s=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(s)||(s=[s]);e:for(let p of s)for(let c of n.aliases.concat(n.version))if(new RegExp(p,"i").test(c)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let s of ae("outdated"))s.hidden=!1})}function is(e,{worker$:t}){let{searchParams:r}=ye();r.has("q")&&(Je("search",!0),e.value=r.get("q"),e.focus(),ze("search").pipe(Ae(i=>!i)).subscribe(()=>{let i=ye();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=et(e),n=O(t.pipe(Ae(jt)),h(e,"keyup"),o).pipe(m(()=>e.value),K());return z([n,o]).pipe(m(([i,a])=>({value:i,focus:a})),G(1))}function si(e,{worker$:t}){let r=new g,o=r.pipe(Z(),ie(!0));z([t.pipe(Ae(jt)),r],(i,a)=>a).pipe(ee("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ee("focus")).subscribe(({focus:i})=>{i&&Je("search",i)}),h(e.form,"reset").pipe(U(o)).subscribe(()=>e.focus());let n=R("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),is(e,{worker$:t}).pipe(w(i=>r.next(i)),_(()=>r.complete()),m(i=>$({ref:e},i)),G(1))}function ci(e,{worker$:t,query$:r}){let o=new g,n=rn(e.parentElement).pipe(b(Boolean)),i=e.parentElement,a=R(":scope > :first-child",e),s=R(":scope > :last-child",e);ze("search").subscribe(l=>s.setAttribute("role",l?"list":"presentation")),o.pipe(re(r),Ur(t.pipe(Ae(jt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:a.textContent=f.length?Ee("search.result.none"):Ee("search.result.placeholder");break;case 1:a.textContent=Ee("search.result.one");break;default:let u=sr(l.length);a.textContent=Ee("search.result.other",u)}});let p=o.pipe(w(()=>s.innerHTML=""),v(({items:l})=>O(I(...l.slice(0,10)),I(...l.slice(10)).pipe(Be(4),Vr(n),v(([f])=>f)))),m(Mn),pe());return p.subscribe(l=>s.appendChild(l)),p.pipe(ne(l=>{let f=fe("details",l);return typeof f=="undefined"?S:h(f,"toggle").pipe(U(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(b(dr),m(({data:l})=>l)).pipe(w(l=>o.next(l)),_(()=>o.complete()),m(l=>$({ref:e},l)))}function as(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=ye();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function pi(e,t){let r=new g,o=r.pipe(Z(),ie(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(U(o)).subscribe(n=>n.preventDefault()),as(e,t).pipe(w(n=>r.next(n)),_(()=>r.complete()),m(n=>$({ref:e},n)))}function li(e,{worker$:t,keyboard$:r}){let o=new g,n=Se("search-query"),i=O(h(n,"keydown"),h(n,"focus")).pipe(ve(se),m(()=>n.value),K());return o.pipe(He(i),m(([{suggest:s},p])=>{let c=p.split(/([\s-]+)/);if(s!=null&&s.length&&c[c.length-1]){let l=s[s.length-1];l.startsWith(c[c.length-1])&&(c[c.length-1]=l)}else c.length=0;return c})).subscribe(s=>e.innerHTML=s.join("").replace(/\s/g," ")),r.pipe(b(({mode:s})=>s==="search")).subscribe(s=>{switch(s.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(b(dr),m(({data:s})=>s)).pipe(w(s=>o.next(s)),_(()=>o.complete()),m(()=>({ref:e})))}function mi(e,{index$:t,keyboard$:r}){let o=xe();try{let n=ii(o.search,t),i=Se("search-query",e),a=Se("search-result",e);h(e,"click").pipe(b(({target:p})=>p instanceof Element&&!!p.closest("a"))).subscribe(()=>Je("search",!1)),r.pipe(b(({mode:p})=>p==="search")).subscribe(p=>{let c=Ie();switch(p.type){case"Enter":if(c===i){let l=new Map;for(let f of P(":first-child [href]",a)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}p.claim()}break;case"Escape":case"Tab":Je("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof c=="undefined")i.focus();else{let l=[i,...P(":not(details) > [href], summary, details[open] [href]",a)],f=Math.max(0,(Math.max(0,l.indexOf(c))+l.length+(p.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}p.claim();break;default:i!==Ie()&&i.focus()}}),r.pipe(b(({mode:p})=>p==="global")).subscribe(p=>{switch(p.type){case"f":case"s":case"/":i.focus(),i.select(),p.claim();break}});let s=si(i,{worker$:n});return O(s,ci(a,{worker$:n,query$:s})).pipe(Re(...ae("search-share",e).map(p=>pi(p,{query$:s})),...ae("search-suggest",e).map(p=>li(p,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,Ye}}function fi(e,{index$:t,location$:r}){return z([t,r.pipe(Q(ye()),b(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>ni(o.config)(n.searchParams.get("h"))),m(o=>{var a;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let s=i.nextNode();s;s=i.nextNode())if((a=s.parentElement)!=null&&a.offsetHeight){let p=s.textContent,c=o(p);c.length>p.length&&n.set(s,c)}for(let[s,p]of n){let{childNodes:c}=x("span",null,p);s.replaceWith(...Array.from(c))}return{ref:e,nodes:n}}))}function ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:a},{offset:{y:s}}])=>(a=a+Math.min(n,Math.max(0,s-i))-n,{height:a,locked:s>=i+n})),K((i,a)=>i.height===a.height&&i.locked===a.locked))}function Xr(e,o){var n=o,{header$:t}=n,r=ao(n,["header$"]);let i=R(".md-sidebar__scrollwrap",e),{y:a}=Ve(i);return C(()=>{let s=new g,p=s.pipe(Z(),ie(!0)),c=s.pipe(Le(0,me));return c.pipe(re(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*a}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),c.pipe(Ae()).subscribe(()=>{for(let l of P(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=ce(f);f.scrollTo({top:u-d/2})}}}),ue(P("label[tabindex]",e)).pipe(ne(l=>h(l,"click").pipe(ve(se),m(()=>l),U(p)))).subscribe(l=>{let f=R(`[id="${l.htmlFor}"]`);R(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),ss(e,r).pipe(w(l=>s.next(l)),_(()=>s.complete()),m(l=>$({ref:e},l)))})}function ui(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return st(je(`${r}/releases/latest`).pipe(de(()=>S),m(o=>({version:o.tag_name})),De({})),je(r).pipe(de(()=>S),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),De({}))).pipe(m(([o,n])=>$($({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return je(r).pipe(m(o=>({repositories:o.public_repos})),De({}))}}function di(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return st(je(`${r}/releases/permalink/latest`).pipe(de(()=>S),m(({tag_name:o})=>({version:o})),De({})),je(r).pipe(de(()=>S),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),De({}))).pipe(m(([o,n])=>$($({},o),n)))}function hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return ui(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return di(r,o)}return S}var cs;function ps(e){return cs||(cs=C(()=>{let t=__md_get("__source",sessionStorage);if(t)return I(t);if(ae("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return S}return hi(e.href).pipe(w(o=>__md_set("__source",o,sessionStorage)))}).pipe(de(()=>S),b(t=>Object.keys(t).length>0),m(t=>({facts:t})),G(1)))}function bi(e){let t=R(":scope > :last-child",e);return C(()=>{let r=new g;return r.subscribe(({facts:o})=>{t.appendChild(Ln(o)),t.classList.add("md-source__repository--active")}),ps(e).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}function ls(e,{viewport$:t,header$:r}){return ge(document.body).pipe(v(()=>mr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ee("hidden"))}function vi(e,t){return C(()=>{let r=new g;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(B("navigation.tabs.sticky")?I({hidden:!1}):ls(e,t)).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}function ms(e,{viewport$:t,header$:r}){let o=new Map,n=P(".md-nav__link",e);for(let s of n){let p=decodeURIComponent(s.hash.substring(1)),c=fe(`[id="${p}"]`);typeof c!="undefined"&&o.set(s,c)}let i=r.pipe(ee("height"),m(({height:s})=>{let p=Se("main"),c=R(":scope > :first-child",p);return s+.8*(c.offsetTop-p.offsetTop)}),pe());return ge(document.body).pipe(ee("height"),v(s=>C(()=>{let p=[];return I([...o].reduce((c,[l,f])=>{for(;p.length&&o.get(p[p.length-1]).tagName>=f.tagName;)p.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return c.set([...p=[...p,l]].reverse(),u)},new Map))}).pipe(m(p=>new Map([...p].sort(([,c],[,l])=>c-l))),He(i),v(([p,c])=>t.pipe(Fr(([l,f],{offset:{y:u},size:d})=>{let y=u+d.height>=Math.floor(s.height);for(;f.length;){let[,M]=f[0];if(M-c=u&&!y)f=[l.pop(),...f];else break}return[l,f]},[[],[...p]]),K((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([s,p])=>({prev:s.map(([c])=>c),next:p.map(([c])=>c)})),Q({prev:[],next:[]}),Be(2,1),m(([s,p])=>s.prev.length{let i=new g,a=i.pipe(Z(),ie(!0));if(i.subscribe(({prev:s,next:p})=>{for(let[c]of p)c.classList.remove("md-nav__link--passed"),c.classList.remove("md-nav__link--active");for(let[c,[l]]of s.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",c===s.length-1)}),B("toc.follow")){let s=O(t.pipe(_e(1),m(()=>{})),t.pipe(_e(250),m(()=>"smooth")));i.pipe(b(({prev:p})=>p.length>0),He(o.pipe(ve(se))),re(s)).subscribe(([[{prev:p}],c])=>{let[l]=p[p.length-1];if(l.offsetHeight){let f=cr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=ce(f);f.scrollTo({top:u-d/2,behavior:c})}}})}return B("navigation.tracking")&&t.pipe(U(a),ee("offset"),_e(250),Ce(1),U(n.pipe(Ce(1))),ct({delay:250}),re(i)).subscribe(([,{prev:s}])=>{let p=ye(),c=s[s.length-1];if(c&&c.length){let[l]=c,{hash:f}=new URL(l.href);p.hash!==f&&(p.hash=f,history.replaceState({},"",`${p}`))}else p.hash="",history.replaceState({},"",`${p}`)}),ms(e,{viewport$:t,header$:r}).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))})}function fs(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:a}})=>a),Be(2,1),m(([a,s])=>a>s&&s>0),K()),i=r.pipe(m(({active:a})=>a));return z([i,n]).pipe(m(([a,s])=>!(a&&s)),K(),U(o.pipe(Ce(1))),ie(!0),ct({delay:250}),m(a=>({hidden:a})))}function yi(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new g,a=i.pipe(Z(),ie(!0));return i.subscribe({next({hidden:s}){e.hidden=s,s?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(U(a),ee("height")).subscribe(({height:s})=>{e.style.top=`${s+16}px`}),h(e,"click").subscribe(s=>{s.preventDefault(),window.scrollTo({top:0})}),fs(e,{viewport$:t,main$:o,target$:n}).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))}function xi({document$:e,viewport$:t}){e.pipe(v(()=>P(".md-ellipsis")),ne(r=>tt(r).pipe(U(e.pipe(Ce(1))),b(o=>o),m(()=>r),Te(1))),b(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,B("content.tooltips")?mt(n,{viewport$:t}).pipe(U(e.pipe(Ce(1))),_(()=>n.removeAttribute("title"))):S})).subscribe(),B("content.tooltips")&&e.pipe(v(()=>P(".md-status")),ne(r=>mt(r,{viewport$:t}))).subscribe()}function Ei({document$:e,tablet$:t}){e.pipe(v(()=>P(".md-toggle--indeterminate")),w(r=>{r.indeterminate=!0,r.checked=!1}),ne(r=>h(r,"change").pipe(Dr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),re(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function us(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function wi({document$:e}){e.pipe(v(()=>P("[data-md-scrollfix]")),w(t=>t.removeAttribute("data-md-scrollfix")),b(us),ne(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Ti({viewport$:e,tablet$:t}){z([ze("search"),t]).pipe(m(([r,o])=>r&&!o),v(r=>I(r).pipe(Ge(r?400:100))),re(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ds(){return location.protocol==="file:"?Tt(`${new URL("search/search_index.js",Zr.base)}`).pipe(m(()=>__index),G(1)):je(new URL("search/search_index.json",Zr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ot=Bo(),Wt=an(),Mt=pn(Wt),eo=nn(),Oe=vn(),hr=Pt("(min-width: 960px)"),Oi=Pt("(min-width: 1220px)"),Mi=ln(),Zr=xe(),Li=document.forms.namedItem("search")?ds():Ye,to=new g;Xn({alert$:to});var ro=new g;B("navigation.instant")&&ri({location$:Wt,viewport$:Oe,progress$:ro}).subscribe(ot);var Si;((Si=Zr.version)==null?void 0:Si.provider)==="mike"&&ai({document$:ot});O(Wt,Mt).pipe(Ge(125)).subscribe(()=>{Je("drawer",!1),Je("search",!1)});eo.pipe(b(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=fe("link[rel=prev]");typeof t!="undefined"&<(t);break;case"n":case".":let r=fe("link[rel=next]");typeof r!="undefined"&<(r);break;case"Enter":let o=Ie();o instanceof HTMLLabelElement&&o.click()}});xi({viewport$:Oe,document$:ot});Ei({document$:ot,tablet$:hr});wi({document$:ot});Ti({viewport$:Oe,tablet$:hr});var rt=Qn(Se("header"),{viewport$:Oe}),Ft=ot.pipe(m(()=>Se("main")),v(e=>Bn(e,{viewport$:Oe,header$:rt})),G(1)),hs=O(...ae("consent").map(e=>xn(e,{target$:Mt})),...ae("dialog").map(e=>zn(e,{alert$:to})),...ae("header").map(e=>Kn(e,{viewport$:Oe,header$:rt,main$:Ft})),...ae("palette").map(e=>Gn(e)),...ae("progress").map(e=>Jn(e,{progress$:ro})),...ae("search").map(e=>mi(e,{index$:Li,keyboard$:eo})),...ae("source").map(e=>bi(e))),bs=C(()=>O(...ae("announce").map(e=>yn(e)),...ae("content").map(e=>Nn(e,{viewport$:Oe,target$:Mt,print$:Mi})),...ae("content").map(e=>B("search.highlight")?fi(e,{index$:Li,location$:Wt}):S),...ae("header-title").map(e=>Yn(e,{viewport$:Oe,header$:rt})),...ae("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Nr(Oi,()=>Xr(e,{viewport$:Oe,header$:rt,main$:Ft})):Nr(hr,()=>Xr(e,{viewport$:Oe,header$:rt,main$:Ft}))),...ae("tabs").map(e=>vi(e,{viewport$:Oe,header$:rt})),...ae("toc").map(e=>gi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Mt})),...ae("top").map(e=>yi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Mt})))),_i=ot.pipe(v(()=>bs),Re(hs),G(1));_i.subscribe();window.document$=ot;window.location$=Wt;window.target$=Mt;window.keyboard$=eo;window.viewport$=Oe;window.tablet$=hr;window.screen$=Oi;window.print$=Mi;window.alert$=to;window.progress$=ro;window.component$=_i;})(); +//# sourceMappingURL=bundle.56dfad97.min.js.map + diff --git a/assets/javascripts/bundle.56dfad97.min.js.map b/assets/javascripts/bundle.56dfad97.min.js.map new file mode 100644 index 000000000..eb83bdb3d --- /dev/null +++ b/assets/javascripts/bundle.56dfad97.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n *\n * @class BehaviorSubject\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an