├── .Rprofile ├── .github ├── .gitignore ├── CODEOWNERS ├── ISSUE_TEMPLATE │ └── new-blog.yaml └── workflows │ ├── check-images.yaml │ ├── hello.yaml │ ├── knit_readme.yaml │ ├── slack-feed.yaml │ ├── trigger-website.yaml │ └── validate_jsons.yaml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── LICENSE.md ├── README.Rmd ├── README.md ├── awesome-rladies-blogs.Rproj ├── blogs ├── amanda.rbind.io.json ├── aosmith.rbind.io.json ├── apreshill.com.json ├── beatrizmilz.com.json ├── blog.djnavarro.net.json ├── cararthompson.com.json ├── cghlewis.com.json ├── citizen-statistician.org.json ├── cosimameyer.com.json ├── daryavanichkina.com.json ├── datapedagogy.com.json ├── drmowinckels.io.json ├── elenadudukina.com.json ├── ellakaye.co.uk.json ├── emilyriederer.com.json ├── fgazzelloni.quarto.pub.json ├── florenciadandrea.com.json ├── hypebright.nl.json ├── isabelizimm.github.io.json ├── ivelasq.rbind.io.json ├── jadeyryan.com.json ├── jhylin.github.io.json ├── juliasilge.com.json ├── karbartolome-blog.netlify.app.json ├── lgibson7.quarto.pub-once-upon-a-time-series.json ├── loreabad6.github.io.json ├── macarenaquiroga.netlify.app.json ├── masalmon.eu.json ├── nrennie.rbind.io.json ├── pipinghotdata.com.json ├── r-ladiesgaborone2021.quarto.pub.json ├── r-ladiesmelbourne.github.io.json ├── rladies-sp.org.json ├── sabrinaschalz.wordpress.com.json ├── sarahgillespie.github.io.json ├── sctyner.me.json ├── shelkariuki.netlify.app.json ├── silviacanelon.com.json ├── soyandrea.netlify.app.json ├── sporella.xyz.json ├── steffilazerte.ca.json ├── surroundedbydata.netlify.app.json ├── thetidytrekker.com.json ├── yabellini.netlify.app.json └── youtube.com.c.ggnot2.json ├── images ├── contrib_newfile.png └── contrib_patch.png ├── renv.lock ├── renv ├── .gitignore └── activate.R └── scripts ├── .entry_schema.json ├── check-images.R └── validate_jsons.R /.Rprofile: -------------------------------------------------------------------------------- 1 | source("renv/activate.R") 2 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | renv/ 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @rladies/website 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new-blog.yaml: -------------------------------------------------------------------------------- 1 | name: New Blog 2 | description: Submit an R-Ladies Blog 3 | title: "[New Blog]: " 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to submit a new R-Ladies blog and congrats if it is yours! :tada: 9 | - type: input 10 | id: title 11 | attributes: 12 | label: Blog Name 13 | description: Name of the blog 14 | placeholder: ex. Awesome R Blog 15 | validations: 16 | required: true 17 | - type: input 18 | id: url 19 | attributes: 20 | label: Blog URL 21 | description: URL to the blog 22 | placeholder: ex. https://rladies.org/blog 23 | validations: 24 | required: true 25 | - type: input 26 | id: pic 27 | attributes: 28 | label: Picture URL 29 | description: URL to a picture illustrating the blog 30 | placeholder: ex. https://rladies.org/wp-content/uploads/2016/12/R-LadiesGlobal.png 31 | validations: 32 | required: false 33 | - type: input 34 | id: author1name 35 | attributes: 36 | label: Author Name 37 | description: Name of an author 38 | placeholder: ex. Firstname Lastname 39 | validations: 40 | required: true 41 | - type: input 42 | id: author1twitter 43 | attributes: 44 | label: Author Twitter Username 45 | description: Username on Twitter without the Twitter URL 46 | placeholder: ex. username 47 | validations: 48 | required: false 49 | - type: input 50 | id: author1github 51 | attributes: 52 | label: Author GitHub Username 53 | description: Username on GitHub without the GitHub URL 54 | placeholder: ex. username 55 | validations: 56 | required: false 57 | - type: input 58 | id: author1orcid 59 | attributes: 60 | label: Author ORCID ID 61 | description: ID on ORCID without the ORCID URL 62 | placeholder: ex. 1234-1234-1234 63 | validations: 64 | required: false 65 | - type: markdown 66 | attributes: 67 | value: | 68 | If the blog has several authors please update the issue after opening it, adding info for other authors. 69 | - type: checkboxes 70 | id: terms 71 | attributes: 72 | label: Code of Conduct 73 | description: By submitting this issue, you agree to follow our [Code of Conduct](http://rladies.org/code-of-conduct/) 74 | options: 75 | - label: I agree to follow R-Ladies' Code of Conduct 76 | required: true 77 | -------------------------------------------------------------------------------- /.github/workflows/check-images.yaml: -------------------------------------------------------------------------------- 1 | name: Check repo image urls 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [main] 7 | schedule: 8 | - cron: "0 0 * * SAT" 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | knit: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout repo 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 1 22 | 23 | - name: Install cURL Headers 24 | run: | 25 | sudo apt-get update 26 | sudo apt-get install libcurl4-openssl-dev 27 | 28 | - name: Setup R 29 | uses: r-lib/actions/setup-r@v2 30 | with: 31 | r-version: 'renv' 32 | 33 | - name: Setup renv 34 | uses: r-lib/actions/setup-renv@v2 35 | 36 | - name: Check repo image urls 37 | run: | 38 | Rscript --verbose scripts/check-images.R 39 | 40 | -------------------------------------------------------------------------------- /.github/workflows/hello.yaml: -------------------------------------------------------------------------------- 1 | name: "Auto message for PR's and Issues" 2 | on: [pull_request, issues] 3 | 4 | jobs: 5 | send-message: 6 | runs-on: ubuntu-latest 7 | if: github.event.pull_request.merged 8 | steps: 9 | - uses: actions/github-script@v6 10 | name: send-message 11 | with: 12 | github-token: ${{ secrets.GLOBAL_GHA_PAT }} 13 | script: | 14 | await github.rest.issues.createComment({ 15 | issue_number: context.issue.number, 16 | owner: context.repo.owner, 17 | repo: context.repo.repo, 18 | body: `Hey, thank you for opening your Pull Request ! 🙂 We will get to this as soon as we can!` 19 | }) 20 | 21 | -------------------------------------------------------------------------------- /.github/workflows/knit_readme.yaml: -------------------------------------------------------------------------------- 1 | name: Knit readme 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | knit: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repo 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 1 19 | 20 | - name: Install cURL Headers 21 | run: | 22 | sudo apt-get update 23 | sudo apt-get install libcurl4-openssl-dev 24 | 25 | - name: Setup R 26 | uses: r-lib/actions/setup-r@v2 27 | with: 28 | r-version: '4.3.0' 29 | 30 | - name: Serup renv 31 | uses: r-lib/actions/setup-renv@v2 32 | 33 | - uses: r-lib/actions/setup-pandoc@v2 34 | 35 | - name: Render readme files 36 | run: rmarkdown::render("README.Rmd", output_format = "github_document") 37 | shell: Rscript {0} 38 | 39 | - name: Commit data 40 | env: 41 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 42 | run: | 43 | git config --local user.name "$GITHUB_ACTOR" 44 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 45 | git commit README.md -m 'Update README list' || echo "No changes to commit" 46 | git push origin || echo "Nothing to push" 47 | 48 | -------------------------------------------------------------------------------- /.github/workflows/slack-feed.yaml: -------------------------------------------------------------------------------- 1 | name: Slack RSS Subscription 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | rss_feed_url: 6 | description: 'RSS Feed URL' 7 | required: true 8 | 9 | jobs: 10 | send_url_to_slack: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out code 14 | uses: actions/checkout@v3 15 | 16 | - name: Set up Node.js 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: 14 20 | 21 | - name: Subscribe to RSS feed 22 | env: 23 | SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} 24 | SLACK_CHANNEL: rladiesblogs 25 | RSS_FEED_URL: ${{ github.event.inputs.rss_feed_url }} 26 | run: | 27 | npm install @slack/web-api 28 | node -e ' 29 | const { WebClient } = require("@slack/web-api"); 30 | 31 | const slackToken = process.env.SLACK_TOKEN; 32 | const channel = process.env.SLACK_CHANNEL; 33 | const url = process.env.RSS_FEED_URL; 34 | 35 | (async () => { 36 | const web = new WebClient(slackToken); 37 | const response = await web.chat.postMessage({ 38 | channel, 39 | text: `/feed subscribe ${url}` 40 | }); 41 | 42 | console.log("Response:", response); 43 | })().catch(error => { 44 | console.error("Error:", error); 45 | process.exit(1); 46 | }); 47 | ' 48 | -------------------------------------------------------------------------------- /.github/workflows/trigger-website.yaml: -------------------------------------------------------------------------------- 1 | name: Trigger Website 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | trigger: 11 | name: Trigger 12 | runs-on: ubuntu-latest 13 | steps: 14 | 15 | - name: Trigger test build 16 | uses: actions/github-script@v7 17 | with: 18 | github-token: ${{ secrets.GLOBAL_GHA_PAT }} 19 | script: | 20 | await github.rest.actions.createWorkflowDispatch({ 21 | owner: 'rladies', 22 | repo: 'rladies.github.io', 23 | workflow_id: 'build-preview.yaml', 24 | ref: 'main', 25 | inputs: { 26 | blogs: '${{ github.run_id }}', 27 | triggering_issue: '${{ github.event.pull_request.number }}', 28 | triggering_repo: '${{ github.event.repository.name }}' 29 | } 30 | }) 31 | 32 | - name: Notify about build start 33 | uses: actions/github-script@v7 34 | with: 35 | github-token: ${{ secrets.GITHUB_TOKEN }} 36 | script: | 37 | const path = require('path'); 38 | await github.rest.issues.createComment({ 39 | owner: 'rladies', 40 | repo: 'awesome-rladies-blogs', 41 | issue_number: '${{ github.event.pull_request.number }}', 42 | body: 'Building test-site now! The build will take a few minutes, but look at its progress in the [website repo](https://github.com/rladies/rladies.github.io/actions/workflows/build-preview.yaml).' 43 | }) 44 | 45 | 46 | -------------------------------------------------------------------------------- /.github/workflows/validate_jsons.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | pull_request: 4 | branches: 5 | - main 6 | paths: 7 | - 'blogs/**' 8 | 9 | name: Validate and clean jsons 10 | 11 | jobs: 12 | validate: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repo 16 | uses: actions/checkout@v4 17 | 18 | - name: Get changed files 19 | id: changed-files 20 | uses: tj-actions/changed-files@v12 21 | with: 22 | files: blogs/ 23 | 24 | - name: Cleanup json template comments 25 | if: ${{ steps.changed-files.outputs.any_changed }} 26 | run: | 27 | for f in ${{ steps.changed-files.outputs.all_changed_files }}; do 28 | echo Cleaning $f 29 | sed -i 's.//required..g' $f 30 | done 31 | 32 | - name: Install cURL Headers 33 | run: | 34 | sudo apt-get update 35 | sudo apt-get install libcurl4-openssl-dev 36 | 37 | - name: Setup R 38 | uses: r-lib/actions/setup-r@v2 39 | with: 40 | version: 'renv' 41 | 42 | - name: Setup renv 43 | uses: r-lib/actions/setup-renv@v2 44 | 45 | - name: Validate jsons 46 | run: Rscript 'scripts/validate_jsons.R' 47 | 48 | - name: Commit data 49 | env: 50 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 51 | run: | 52 | git config --local user.name "$GITHUB_ACTOR" 53 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 54 | git commit blogs/ -m 'Commit cleaned jsons' || echo "No changes to commit" 55 | git push origin || echo "Nothing to push" 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .DS_Store 6 | .httr-oauth 7 | tmp 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This repository collects R-Ladies blogs, this is inclusive of those who identify as a minority gender (including but not limited to cis/trans women, trans men, non-binary, genderqueer, & agender). We'd love to have contributions to this list! If you identify with R-Ladies and have a blog, please add yourself. 4 | 5 | # Contributing checklist 6 | 7 | - [ ] The entry is added to [blogs/](blogs/) folder 8 | - [ ] The entry filename ends with `.json` 9 | - [ ] The json contains at minimum: 10 | - [ ] title (blog title) 11 | - [ ] type ("blog") 12 | - [ ] url (blog url) 13 | - [ ] rss_feed (RSS feed for R-related posts. Ideally, you use "tags" or "categories" to identify your R-related posts. A title-based RSS feed is OK. If you need more input on how to get your RSS, have a look [here](https://zapier.com/blog/how-to-find-rss-feed-url/)) 14 | - [ ] photo_url (logo or profile) 15 | - [ ] language (one of [ISO 639-1 Language Codes](https://www.w3schools.com/tags/ref_language_codes.asp)) 16 | - [ ] authors (list of authors) 17 | 18 | # Contributing details 19 | 20 | All the blogs are listed in the [blogs](blogs/) folder, where each blog is in its own json-file. These files are used to render a table on the upcoming revamped R-Ladies website. Follow the below instructions to add to the list. If you have any trouble, please create an issue for us to help. 21 | 22 | Depending on how you are most comfortable working, there are several ways of adding new entries. 23 | 24 | If you are not familiar with JSON you can [open an issue](https://github.com/rladies/awesome-rladies-blogs/issues/new/choose) with your blog info, which uses a GitHub Form, and we'd create the JSON for you! 25 | 26 | Now we will focus on adding new entries directly through GitHub, but you could also work on a local copy (branch) or fork and add new entries that way too. 27 | 28 | 29 | ## Create a new file 30 | 31 | Create a new file in the [blogs/](blogs/) folder by [using this link](https://github.com/rladies/awesome-rladies-blogs/new/main/?filename=blogs/your-blog-url.com.json&value=%7B%0A%20%20%22title%22%3A%20%22Your%20title%22%2C%20%2F%2Frequired%0A%20%20%22subtitle%22%3A%20%22subtitle%20or%20tagline%22%2C%20%2F%2Foptional%0A%20%20%22type%22%3A%20%22blog%22%2C%20%2F%2Frequired%0A%20%20%22url%22%3A%20%22https%3A%2F%2Fyour_blog.com%22%2C%20%2F%2Frequired%0A%20%20%22photo_url%22%3A%20%22https%3A%2F%2Fyour_blog.com%2Fyour_photo.png%22%2C%20%2F%2Frequired%0A%20%20%22description%22%3A%20%22Short%20description%20of%20what%20you%20blog%20about%22%2C%0A%20%20%22language%22%3A%20%22en%22%2C%20%2F%2Frequired%0A%20%20%22rss_feed%22%3A%20%22%5Burl%5D%2Ffile.xml%22%2C%20%2F%2Frequired%0A%20%20%22authors%22%3A%20%5B%20%2F%2Frequired%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%22name%22%3A%20%22Your%20Name%22%2C%20%2F%2Frequired%0A%20%20%20%20%20%20%22social_media%22%3A%20%5B%7B%0A%20%20%20%20%20%20%20%20%20%22twitter%22%3A%20%22username%22%2C%0A%20%20%20%20%20%20%20%20%20%22mastodon%22%3A%20%22%40username%40server.org%22%2C%0A%20%20%20%20%20%20%20%20%20%22bluesky%22%3A%20%22username.domain%22%2C%0A%20%20%20%20%20%20%20%20%20%22github%22%3A%20%22username%22%2C%0A%20%20%20%20%20%20%20%20%20%22instagram%22%3A%20%22username%22%2C%0A%20%20%20%20%20%20%20%20%20%22youtube%22%3A%20%22username%2Fend-url%22%2C%0A%20%20%20%20%20%20%20%20%20%22tiktok%22%3A%20%22username%22%2C%0A%20%20%20%20%20%20%20%20%20%22periscope%22%3A%20%22username%22%2C%0A%20%20%20%20%20%20%20%20%20%22researchgate%22%3A%20%22username%22%2C%0A%20%20%20%20%20%20%20%20%20%22website%22%3A%20%22url%22%2C%0A%20%20%20%20%20%20%20%20%20%22linkedin%22%3A%20%22username%22%2C%0A%20%20%20%20%20%20%20%20%20%22facebook%22%3A%20%22username%22%2C%0A%20%20%20%20%20%20%20%20%20%22orcid%22%3A%20%22member%20number%22%2C%0A%20%20%20%20%20%20%20%20%20%22meetup%22%3A%20%22end-url%22%0A%20%20%20%20%20%20%7D%5D%0A%20%20%20%20%7D%0A%20%20%5D%0A%7D). 32 | 33 | This link will fork the repository to your user account, and initiate a new file with some template content in it. After filling the file, please [create a PR to the main branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). 34 | 35 | ### File name 36 | 37 | The name of the file should be the site url (without `www` or `http(s)://` . This way we can ensure each file has a unique name and that duplication does not happen. 38 | 39 | ### File content 40 | 41 | Using the link above will create a template for you to start with. 42 | Fill inn all the information that is relevant for your blog. 43 | There are several adaptations to an entry you can make that are not highlighted in every entry. 44 | Remove all mentions of `\\required`, these are just for making it clear which information you _must_ provide for the file to be valid. 45 | Any optional field you don't want to add, you may delete entirely. 46 | For instance, if you don't have a subtitle or tagline for your blog, remove the entire line of `"subtite": "subtitle or tagline"` rather than leaving it empty with `"subtite": ""` 47 | 48 | #### Photo 49 | 50 | The photo url you provide will be displayed as your blogs thumbnail. 51 | This may be a picture of you, or if you have a logo for your blog/website, it may be best to use this in stead. 52 | 53 | #### RSS Feed 54 | 55 | Please add a content-specific feed in `rss_feed`. Ideally, you have a specific RSS feed for R-related posts. Depending on your website theme, the implementation may differ. We collected a few of the most common approaches below: 56 | 57 |
Quarto 58 | - Change the code in `index.qmd` as (under listing, also described [here](https://quarto.org/docs/websites/website-blog.html#rss-feed)): 59 | 60 | ``` 61 | feed: 62 | categories: [R] 63 | 64 | ``` 65 | 66 | - Note to new users that the category names will be the names of your category tags used in the blogs (not `posts`, which are the posts folder for Quarto blogs) 67 | - Then provide the RSS feed links as, `[url]/blog/index-r.xml` for R category posts (`[url]/blog/index.xml` will be the RSS feed link for main posts only) 68 | 69 |
70 | 71 |
Distill 72 | There is currently a [workaround](https://github.com/rladies/awesome-rladies-blogs/pull/54#issuecomment-1501263818) for adding RSS feeds in distill that works as follows: 73 | 74 | - In distill, there is a categories folder generated when a post is rendered which gets deleted when the blog is rendered 75 | - Store the folder and add it later because we need a categories folder, containing each specified category with an `index.xml` for each category 76 |
77 | 78 |
Hugo 79 | ###### Hugo Academic 80 | 81 | - Apparently the RSS feed is enabled by default and you can access it by using the field `category` in the YAML of your posts 82 | - Further readings for [Hugo Academic](https://cosimameyer.com/post/adding-your-hugo-academic-blog-to-r-bloggers-and-python-bloggers/) 83 | 84 | ###### Hugo Portio 85 | 86 | - Copy and paste the content of [this file](https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml) (it’s Hugo’s default RSS settings) 87 | - Store it under `layouts/_default/rss.xml` (if there is no file, you need to create this one). 88 | - Exchange one line. Instead of `{{ .Summary | html }}`, we want `{{ .Content | html }}` (it’s at the very bottom of the file). This way, you RSS feed doesn’t show an excerpt but the full text. 89 | - More about [Hugo Portio](https://cosimameyer.com/post/adding-your-hugo-academic-blog-to-r-bloggers-and-python-bloggers/) 90 |
91 | 92 | If you want to check how your RSS looks like, you can use [simple pie](https://simplepie.org/demo/). 93 | 94 | 95 | #### Authors 96 | 97 | The entry may have several authors. This is for blogs where maybe there are several blogging together. If it is a blog that mainly has guest bloggers, its better to list the editors/maintainers of the blog and add "guest bloggers" as authors also. 98 | 99 | Adding several authors means duplicating the content between the curlies `{}` in the author section, and adding a comma between each one. 100 | 101 | ```json 102 | "authors": [ 103 | { 104 | "name": "Athanasia Mo Mowinckel", 105 | "social_media": [{ 106 | "twitter": "DrMowinckels", 107 | "github": "Athanasiamo" 108 | }] 109 | }, 110 | { 111 | "name": "Mary Johnson", 112 | "social_media": [{ 113 | "linkedin": "maryj", 114 | "youtube": "maryj" 115 | }] 116 | }, 117 | { 118 | "name": "Guest bloggers" 119 | } 120 | ] 121 | ``` 122 | 123 | #### Icons 124 | 125 | The `social_media` section supports many different key-value pairs. 126 | For rendering on the website, only the three first social media items for each author will be rendered. 127 | 128 | ```json 129 | "twitter": "username" 130 | "mastodon": "@username@instance" 131 | "bluesky": "username.domain" 132 | "github": "username" 133 | "instagram": "username" 134 | "youtube": "username/end-url" 135 | "tiktok": "username" 136 | "periscope": "username" 137 | "researchgate": "username" 138 | "website": "url" 139 | "linkedin": "username" 140 | "facebook": "username" 141 | "orcid": "member number" 142 | "meetup": "end-url" 143 | ``` 144 | 145 | #### Language 146 | The language field should be populated with the [ISO 639-1 Language Codes](https://www.w3schools.com/tags/ref_language_codes.asp) of the site content. 147 | Please be thorough when entering this information. 148 | 149 | ## Commit and PR the file 150 | 151 | At the bottom of the page on GitHub, add a commit message in the box. 152 | 153 | ![Propose changes](images/contrib_patch.png) 154 | 155 | You will immediately be sent to the 'Pull requests' page, to create a PR to the main branch. 156 | Click the `Create pull request` button. 157 | Once this is done, a new page will open and some automated checks of your submitted entries start. 158 | In the comment section, make sure to @drmowinckels so she can take a look. 159 | 160 | If anything needs fixing you will be notified and given instructions on how to do that. 161 | 162 | Once all checks pass and the entries have been reviewed, they will be merged to the main branch. 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## creative commons 2 | 3 | # CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. 6 | 7 | ### Statement of Purpose 8 | 9 | The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). 10 | 11 | Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. 12 | 13 | For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 14 | 15 | 1. __Copyright and Related Rights.__ A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: 16 | 17 | i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; 18 | 19 | ii. moral rights retained by the original author(s) and/or performer(s); 20 | 21 | iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; 22 | 23 | iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; 24 | 25 | v. rights protecting the extraction, dissemination, use and reuse of data in a Work; 26 | 27 | vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and 28 | 29 | vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 30 | 31 | 2. __Waiver.__ To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 32 | 33 | 3. __Public License Fallback.__ Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 34 | 35 | 4. __Limitations and Disclaimers.__ 36 | 37 | a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. 38 | 39 | b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. 40 | 41 | c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. 42 | 43 | d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. 44 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>" 11 | ) 12 | ``` 13 | 14 | # Awesome R-Ladies' Blogs 15 | 16 | [![Awesome](https://awesome.re/badge.svg)](https://awesome.re) 17 | 18 | 19 | Curated list of awesome blogs by R-Ladies, inspired by this [Tweet](https://twitter.com/WeAreRLadies/status/1362021673239785473). 20 | The content of this list i rendered on the [R-Ladies website](https://www.rladies.org/activities/rladies-blogs/). 21 | The list also is the source for the [R-Ladies mastodon bot](https://fosstodon.org/@rladies_bot@botsin.space), that occasionally toots links to posts from these blogs. 22 | 23 | To contribute to the list, please see the [contributing](CONTRIBUTING.md) instructions. 24 | 25 | 26 | ## List of blogs 27 | 28 | Created by accessing the json files in [blogs/](blogs/) 29 | 30 | ```{r, echo = FALSE, results='asis'} 31 | json_list <- list.files(here::here("blogs"), "json$", full.names = TRUE) 32 | dt <- lapply(json_list, jsonlite::read_json) 33 | 34 | invisible( 35 | lapply(dt, function(x){ 36 | cat(sprintf(" - [%s](%s) by %s", 37 | x$title, x$url, 38 | paste(sapply(x$authors, function(x) x$name), collapse=", ")), 39 | sep = "\n") 40 | }) 41 | ) 42 | ``` 43 | 44 | 45 | ## License 46 | 47 | [![CC0](https://upload.wikimedia.org/wikipedia/commons/6/69/CC0_button.svg)](https://creativecommons.org/publicdomain/zero/1.0/) 48 | 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Awesome R-Ladies’ Blogs 5 | 6 | 7 | 8 | [![Awesome](https://awesome.re/badge.svg)](https://awesome.re) 9 | 10 | 11 | Curated list of awesome blogs by R-Ladies, inspired by this 12 | [Tweet](https://twitter.com/WeAreRLadies/status/1362021673239785473). 13 | The content of this list i rendered on the [R-Ladies 14 | website](https://www.rladies.org/activities/rladies-blogs/). The list 15 | also is the source for the [R-Ladies mastodon 16 | bot](https://fosstodon.org/@rladies_bot@botsin.space), that occasionally 17 | toots links to posts from these blogs. 18 | 19 | To contribute to the list, please see the 20 | [contributing](CONTRIBUTING.md) instructions. 21 | 22 | ## List of blogs 23 | 24 | Created by accessing the json files in [blogs/](blogs/) 25 | 26 | - [Amanda’s Data Blog](amanda.rbind.io) by Amanda Peterson 27 | - [Very Statisticious](https://aosmith.rbind.io) by Ariel Muldoon 28 | - [Alison Hill](https://www.apreshill.com) by Alison Hill 29 | - [Beatriz Milz’s blog](https://beatrizmilz.com/) by Beatriz Milz 30 | - [Notes from a data witch](https://blog.djnavarro.net/) by Danielle 31 | Navarro 32 | - [Building Stories with Data](https://cararthompson.com/blog) by Cara 33 | Thompson 34 | - [Crystal Lewis](https://www.cghlewis.com) by Crystal Lewis 35 | - [Citizen Statistician](citizen-statistician.org) by Mine 36 | Çetinkaya-Rundel, Rob Gould, Andrew Zieffler 37 | - [Cosima Meyer](https://cosimameyer.com/) by Cosima Meyer 38 | - [Darya Vanichkina’s blog](https://www.daryavanichkina.com/posts.html) 39 | by Darya Vanichkina 40 | - [Data Pedagogy](https://www.datapedagogy.com/) by Mine Dogucu 41 | - [Dr. Mowinckel’s blog](https://drmowinckels.io) by Athanasia Monika 42 | Mowinckel 43 | - [Elena Dudukina’s blog](https://elenadudukina.com) by Elena Dudukina 44 | - [Ella Kaye](https://ellakaye.co.uk) by Ella Kaye 45 | - [Emily Riederer](https://emilyriederer.com) by Emily Riederer 46 | - [Federica Gazzelloni](https://fgazzelloni.quarto.pub) by Federica 47 | Gazzelloni 48 | - [Florencia D’Andrea](https://florenciadandrea.com) by Florencia 49 | D’Andrea 50 | - [Hypebright](https://hypebright.nl/index.php/en/home-en/blog/) by 51 | Veerle van Leemput 52 | - [Isabel Zimmerman](https://isabelizimm.github.io/) by Isabel Zimmerman 53 | - [%\>% Dreams](https://ivelasq.rbind.io/) by Isabella Velásquez 54 | - [data whiskeRs](https://jadeyryan.com/blog) by Jadey Ryan 55 | - [Data in life](https://jhylin.github.io/Data_in_life_blog/) by 56 | Jennifer HY Lin 57 | - [Julia Silge](https://juliasilge.com/) by Julia Silge 58 | - [Karina Bartolome](https://karbartolome-blog.netlify.app) by Karina 59 | Bartolome 60 | - [Once Upon a Time 61 | Series](https://lgibson7.quarto.pub/once-upon-a-time-series/) by Lydia 62 | Gibson 63 | - [Lore Abad](https://loreabad6.github.io/) by Lorena Abad 64 | - [Macarena Quiroga](https://macarenaquiroga.netlify.app) by Macarena 65 | Quiroga 66 | - [Maëlle’s R blog](https://masalmon.eu/) by Maëlle Salmon 67 | - [Nicola Rennie](https://nrennie.rbind.io) by Nicola Rennie 68 | - [Piping Hot Data](https://www.pipinghotdata.com) by Shannon Pileggi 69 | - [R-Ladies Gaborone](https://r-ladiesgaborone2021.quarto.pub) by 70 | R-Ladies Gabarone 71 | - [R-Ladies Melbourne Blog](https://r-ladiesmelbourne.github.io/) by 72 | R-Ladies Melbourde 73 | - [R-Ladies São Paulo Blog](https://rladies-sp.org/) by R-Ladies São 74 | Paulo 75 | - [Data Science 76 | Blog](https://sabrinaschalz.wordpress.com/data-science-blog/) by 77 | Sabrina Schalz 78 | - [Sarah Gillespie’s blog](https://sarahgillespie.github.io/SG/) by 79 | Sarah Gillespie 80 | - [Sam Tyner-Monroe](https://sctyner.me) by Sam Tyner-Monroe 81 | - [Shel Kariuki’s blog](https://shelkariuki.netlify.app/) by Shel 82 | Kariuki 83 | - [Meeting People Where They R](https://silviacanelon.com) by Silvia 84 | Canelón 85 | - [Soy Andrea blog](https://soyandrea.netlify.app/) by Andrea Gómez 86 | Vargas 87 | - [Ciencia de Datos en Español](https://sporella.xyz) by Steph Orellana 88 | Bello 89 | - [Steffi LaZerte](https://steffilazerte.ca/tips_and_tricks.html) by 90 | Steffi LaZerte 91 | - [Surrounded by Data](https://surroundedbydata.netlify.app/) by Veerle 92 | van Son 93 | - [Exploration Corner](https://thetidytrekker.com/blog.html) by Meghan 94 | Harris 95 | - [Yanina Bellini Saibene](https://yabellini.netlify.app/blog/) by 96 | Yanina Bellini Saibene 97 | - [Melissa Van Bussel (ggnot2)’s YouTube channel about 98 | R](https://www.youtube.com/c/ggnot2) by Melissa Van Bussel 99 | 100 | ## License 101 | 102 | [![CC0](https://upload.wikimedia.org/wikipedia/commons/6/69/CC0_button.svg)](https://creativecommons.org/publicdomain/zero/1.0/) 103 | -------------------------------------------------------------------------------- /awesome-rladies-blogs.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | -------------------------------------------------------------------------------- /blogs/amanda.rbind.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Amanda's Data Blog", 3 | "type": "blog", 4 | "url": "amanda.rbind.io", 5 | "photo_url": "http://amanda.rbind.io/profile.jpg", 6 | "description": "Amanda's musings about data analysis and R programming", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Amanda Peterson", 11 | "social_media": [{ 12 | "twitter": "DrAmandaRP", 13 | "github": "AmandaRP" 14 | }] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /blogs/aosmith.rbind.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Very Statisticious", 3 | "type": "blog", 4 | "url": "https://aosmith.rbind.io", 5 | "rss_feed": "https://aosmith.rbind.io/index.xml", 6 | "photo_url": "https://aosmith.rbind.io/img/main/ao_ppg2.jpg", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Ariel Muldoon", 11 | "social_media": [{ 12 | "twitter": "aosmith16", 13 | "mastodon": "@aosmith16@fosstodon.org", 14 | "github": "aosmith16", 15 | "website": "https://ariel.rbind.io", 16 | "linkedin": "arielmuldoon" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/apreshill.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Alison Hill", 3 | "type": "blog", 4 | "url": "https://www.apreshill.com", 5 | "rss_feed": "https://www.apreshill.com/index.xml", 6 | "photo_url": "https://www.apreshill.com/about/sidebar/avatar.jpg", 7 | "description": "Personal website, blog, and professional portfolio.", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Alison Hill", 12 | "social_media": [{ 13 | "twitter": "apreshill", 14 | "mastodon": "@apreshill@fosstodon.org", 15 | "github": "apreshill", 16 | "orcid": "0000-0002-8082-1890" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/beatrizmilz.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Beatriz Milz's blog", 3 | "type": "blog", 4 | "url": "https://beatrizmilz.com/", 5 | "photo_url": "https://beatrizmilz.com/about/sidebar/avatar.jpeg", 6 | "description": "Blog in Brazilian Portuguese, in which I share what I have been learning related to R and Data science.", 7 | "language": "pt", 8 | "authors": [ 9 | { 10 | "name": "Beatriz Milz", 11 | "social_media": [{ 12 | "twitter": "BeaMilz", 13 | "bluesky": "beatrizmilz.bsky.social", 14 | "github": "beatrizmilz", 15 | "orcid": "0000-0002-3064-4486", 16 | "linkedin": "beatrizmilz", 17 | "instagram": "beatrizmilz", 18 | "youtube": "UCJ3RjWuqWNfKiAthnNgcv-w", 19 | "orcid": "0000-0001-7648-6578", 20 | "website": "https://beatrizmilz.com/" 21 | }] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /blogs/blog.djnavarro.net.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Notes from a data witch", 3 | "type": "blog", 4 | "url": "https://blog.djnavarro.net/", 5 | "photo_url": "https://djnavarro.net/img/danielle-navarro_hexes_square.jpg", 6 | "description": "Data science and generative art", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Danielle Navarro", 11 | "social_media": [{ 12 | "twitter": "djnavarro", 13 | "bluesky": "djnavarro.bsky.social", 14 | "github": "djnavarro", 15 | "linkedin": "djnavarro", 16 | "instagram": "daniellenavarro77", 17 | "youtube": "daniellenavarro77", 18 | "orcid": "0000-0001-7648-6578", 19 | "website": "https://djnavarro.net" 20 | }] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /blogs/cararthompson.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Building Stories with Data", 3 | "subtitle": "Pars pas les mains vides", 4 | "type": "blog", 5 | "url": "https://cararthompson.com/blog", 6 | "rss_feed": "https://www.cararthompson.com/posts.xml", 7 | "photo_url": "https://www.cararthompson.com/images/home/profile-pic.png", 8 | "description": "My website and portfolio, which includes talks and a blog about the things I've picked up along the way in the weird and wonderful worlds of #rstats, datascience and dataviz.", 9 | "language": "en", 10 | "authors": [ 11 | { 12 | "name": "Cara Thompson", 13 | "social_media": [{ 14 | "twitter": "cararthompson", 15 | "mastodon": "@cararthompson@fosstodon.org", 16 | "bluesky": "cararthompson.bsky.social", 17 | "github": "cararthompson", 18 | "website": "cararthompson.com", 19 | "linkedin": "cararthompson" 20 | }] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /blogs/cghlewis.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Crystal Lewis", 3 | "type": "blog", 4 | "url": "https://www.cghlewis.com", 5 | "photo_url": "https://github.com/Cghlewis/crystal_site/blob/main/static/img/me2022v02.JPG?raw=true", 6 | "language": "en", 7 | "authors": [ 8 | { 9 | "name": "Crystal Lewis", 10 | "social_media": [{ 11 | "twitter": "Cghlewis", 12 | "bluesky": "cghlewis.bsky.social", 13 | "website": "https://www.cghlewis.com", 14 | "linkedin": "crystal-lewis-922b4193/"}] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /blogs/citizen-statistician.org.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Citizen Statistician", 3 | "type": "blog", 4 | "url": "citizen-statistician.org", 5 | "rss_feed": "http://www.citizen-statistician.org/categories/rstats/index.xml", 6 | "photo_url": "http://www.citizen-statistician.org/img/logo.png", 7 | "description": "A blog, meant for anyone wishing to become a citizen statistician, but in particular for statistics teachers – those who help empower citizens to become citizen statisticians.", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Mine Çetinkaya-Rundel", 12 | "social_media": [{ 13 | "twitter": "minebocek", 14 | "mastodon": "@minecr@fosstodon.org", 15 | "bluesky": "minecr.bsky.social", 16 | "github": "mine-cetinkaya-rundel", 17 | "orcid": "0000-0001-6452-2420", 18 | "website": "http://mine-cr.com/" 19 | }] 20 | }, 21 | { 22 | "name": "Rob Gould", 23 | "social_media": [{ 24 | "github": "rgmobilize", 25 | "orcid": "0000-0001-5392-5945", 26 | "website": "http://www.stat.ucla.edu/~rgould/Home/About_Me.html" 27 | }] 28 | }, 29 | { 30 | "name": "Andrew Zieffler", 31 | "social_media": [{ 32 | "twitter": "Chili14", 33 | "github": "zief0002", 34 | "orcid": "0000-0002-1035-0341", 35 | "website": "https://www.datadreaming.org/" 36 | }] 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /blogs/cosimameyer.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Cosima Meyer", 3 | "type": "blog", 4 | "url": "https://cosimameyer.com/", 5 | "photo_url": "https://cosimameyer.com/images/hero/avatar.jpg", 6 | "rss_feed": "https://cosimameyer.com/category/r-post/index.xml", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Cosima Meyer", 11 | "social_media": [{ 12 | "twitter": "cosima_meyer", 13 | "mastodon": "@cosima_meyer@mas.to", 14 | "bluesky": "cosima.bsky.social", 15 | "github": "cosimameyer", 16 | "website": "https://cosimameyer.com/", 17 | "linkedin": "cosimameyer/" 18 | }] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /blogs/daryavanichkina.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Darya Vanichkina's blog", 3 | "type": "blog", 4 | "url": "https://www.daryavanichkina.com/posts.html", 5 | "photo_url": "https://daryavanichkina.com/images/daryaVhead1.jpg", 6 | "description": "Darya Vanichkina's blog", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Darya Vanichkina", 11 | "social_media": [{ 12 | "twitter": "dvanichkina", 13 | "github": "dvanic", 14 | "linkedin": "daryavanichkina", 15 | "orcid": "0000-0002-0406-164X", 16 | "website": "https://daryavanichkina.com" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/datapedagogy.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Data Pedagogy", 3 | "subtitle": "Thoughts longer than a tweet, shorter than a manuscript on data, pedagogy, and data pedagogy", 4 | "type": "blog", 5 | "url": "https://www.datapedagogy.com/", 6 | "photo_url": "https://www.datapedagogy.com/images/dogucu-icon.png", 7 | "description": "Teaching and learning data science", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Mine Dogucu", 12 | "social_media": [{ 13 | "twitter": "MineDogucu", 14 | "bluesky": "minedogucu.bsky.social", 15 | "github": "mdogucu", 16 | "linkedin": "minedogucu" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/drmowinckels.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Dr. Mowinckel's blog", 3 | "subtitle": "All things R and Neuroimaging", 4 | "type": "blog", 5 | "url": "https://drmowinckels.io", 6 | "rss_feed": "https://drmowinckels.io/tags/r/index.xml", 7 | "photo_url": "https://drmowinckels.io/about/profile.png", 8 | "description": "Personal website and blog about R and neuromaging in R", 9 | "language": "en", 10 | "authors": [ 11 | { 12 | "name": "Athanasia Monika Mowinckel", 13 | "social_media": [{ 14 | "mastodon": "@Drmowinckels@fosstodon.org", 15 | "bluesky": "drmowinckels.io", 16 | "github": "drmowinckels" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/elenadudukina.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Elena Dudukina's blog", 3 | "type": "blog", 4 | "url": "https://elenadudukina.com", 5 | "rss_feed": "https://www.elenadudukina.com/category/r/index.xml", 6 | "photo_url": "https://www.elenadudukina.com/author/elena-dudukina/avatar_hub94ed909de68ef2f7bd91a471236bb47_211159_270x270_fill_q90_lanczos_center.jpg", 7 | "description": "I blog about Rstats, pharmacoepidemiology, women's health, causal inference, and open source Nordic data", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Elena Dudukina", 12 | "social_media": [{ 13 | "twitter": "evpatora", 14 | "mastodon": "@evpatora@mstdn.social", 15 | "orcid": "0000-0002-4238-049X" 16 | }] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /blogs/ellakaye.co.uk.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Ella Kaye", 3 | "type": "blog", 4 | "url": "https://ellakaye.co.uk", 5 | "rss_feed": "https://ellakaye.co.uk/posts-r.xml", 6 | "photo_url": "https://ellakaye.co.uk/images/ellakaye-profile.jpeg", 7 | "description": "Personal website, portfolio and blog", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Ella Kaye", 12 | "social_media": [{ 13 | "github": "EllaKaye", 14 | "mastodon": "@ellakaye@fosstodon.org", 15 | "bluesky": "ellakaye.co.uk", 16 | "linkedin": "ellakaye/", 17 | "website": "https://ellakaye.co.uk", 18 | "orcid": "0000-0002-7300-3718" 19 | }] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /blogs/emilyriederer.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Emily Riederer", 3 | "subtitle": "", 4 | "type": "blog", 5 | "url": "https://emilyriederer.com", 6 | "photo_url": "https://avatars.githubusercontent.com/u/19798371", 7 | "description": "Thoughts on advancing the data analysis lifecycle -- from data quality and management, reproducible tooling, and statistical methods", 8 | "language": "en", 9 | "rss_feed": "https://www.emilyriederer.com/tags/rstats/index.xml", 10 | "authors": [ 11 | { 12 | "name": "Emily Riederer", 13 | "social_media": [{ 14 | "twitter": "emilyriederer", 15 | "mastodon": "@emilyriederer@mastodon.social", 16 | "github": "emilyriederer", 17 | "website": "emilyriederer.com", 18 | "linkedin": "linkedin.com/in/emilyriederer", 19 | "facebook": "emilyriederer", 20 | "orcid": "0000-0002-1788-7934" 21 | }] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /blogs/fgazzelloni.quarto.pub.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Federica Gazzelloni", 3 | "type": "blog", 4 | "url": "https://fgazzelloni.quarto.pub", 5 | "rss_feed": "https://fgazzelloni.quarto.pub/index.xml", 6 | "photo_url": "https://avatars.githubusercontent.com/u/61802414", 7 | "description": "Data analytics, visualization and modeling with R", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Federica Gazzelloni", 12 | "social_media": [{ 13 | "twitter": "fgazzelloni", 14 | "mastodon": "@Fgazzelloni@fosstodon.org", 15 | "github": "fgazzelloni", 16 | "youtube": "Federica Gazzelloni", 17 | "website": "https://fgazzelloni.quarto.pub", 18 | "linkedin": "federicagazzelloni", 19 | "orcid": "0000-0002-4285-611X" 20 | }] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /blogs/florenciadandrea.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Florencia D'Andrea", 3 | "type": "website", 4 | "url": "https://florenciadandrea.com", 5 | "photo_url": "https://florencia.netlify.app/about/sidebar/avatar.jpg", 6 | "language": "en", 7 | "authors": [ 8 | { 9 | "name": "Florencia D'Andrea", 10 | "social_media": [{ 11 | "twitter": "cantoflor_87", 12 | "mastodon": "@florencia@techhub.social", 13 | "github": "flor14", 14 | "orcid": "0000-0002-0041-097X", 15 | "linkedin": "florenciadandrea" 16 | }] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /blogs/hypebright.nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Hypebright", 3 | "subtitle": "Programming | R and Shiny", 4 | "type": "blog", 5 | "url": "https://hypebright.nl/index.php/en/home-en/blog/", 6 | "photo_url": "https://hypebright.nl/wp-content/uploads/2023/01/mvb2021-08-10-1058-scaled.jpg", 7 | "description": "R and Shiny tutorials for both beginners and advanced R developers", 8 | "language": "en", 9 | "rss_feed": "https://hypebright.nl/index.php/en/category/r-en/feed/", 10 | "authors": [ 11 | { 12 | "name": "Veerle van Leemput", 13 | "social_media": [{ 14 | "bluesky": "veerle.hypebright.nl", 15 | "youtube": "HypebrightBV", 16 | "website": "https://hypebright.nl", 17 | "linkedin": "veerlevanleemput", 18 | "github": "hypebright" 19 | }] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /blogs/isabelizimm.github.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Isabel Zimmerman", 3 | "type": "blog", 4 | "url": "https://isabelizimm.github.io/", 5 | "rss_feed": "https://isabelizimm.github.io/blog-r.xml", 6 | "photo_url": "https://isabelizimm.github.io/profile.jpg", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Isabel Zimmerman", 11 | "social_media": [{ 12 | "twitter": "@isabelizimm", 13 | "bluesky": "isabelizimm.bsky.social", 14 | "mastodon": "@isabelizimm@fosstodon.org", 15 | "github": "isabelizimm", 16 | "linkedin": "isabel-zimmerman" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/ivelasq.rbind.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "%>% Dreams", 3 | "type": "blog", 4 | "url": "https://ivelasq.rbind.io/", 5 | "rss_feed": "https://ivelasq.rbind.io/index.xml", 6 | "photo_url": "https://ivelasq.rbind.io/images/avatar.jpg", 7 | "description": "Isabella Velásquez Personal Site on R and Analytics", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Isabella Velásquez", 12 | "social_media": [{ 13 | "bluesky": "ivelasq3.bsky.social", 14 | "mastodon": "@ivelasq3@fosstodon.org", 15 | "github": "ivelasq", 16 | "linkedin": "ivelasq", 17 | "orcid": "0000-0002-7180-4095", 18 | "instagram": "ivelasq", 19 | "website": "https://ivelasq.rbind.io/" 20 | }] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /blogs/jadeyryan.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "data whiskeRs", 3 | "type": "blog", 4 | "url": "https://jadeyryan.com/blog", 5 | "photo_url": "https://raw.githubusercontent.com/jadeynryan/jadey_website/main/assets/img/JR-logo.png", 6 | "description": "R code, environmental data science, and cats", 7 | "language": "en", 8 | "rss_feed": "https://jadeyryan.com/blog-r.xml", 9 | "authors": [ 10 | { 11 | "name": "Jadey Ryan", 12 | "social_media": [{ 13 | "twitter": "jadeynryan", 14 | "mastodon": "@jadeynryan@fosstodon.org", 15 | "bluesky": "jadeynryan.bsky.social", 16 | "github": "jadeynryan", 17 | "orcid": "0009-0004-5998-4697", 18 | "youtube": "jadeynryan", 19 | "website": "https://jadeyryan.com", 20 | "linkedin": "jadeynryan" 21 | }] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /blogs/jhylin.github.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Data in life", 3 | "type": "blog", 4 | "url": "https://jhylin.github.io/Data_in_life_blog/", 5 | "description": "A blog using data science on pharmaceutical and healthcare data", 6 | "language": "en", 7 | "rss_feed": "https://jhylin.github.io/Data_in_life_blog/index-r.xml", 8 | "authors": [ 9 | { 10 | "name": "Jennifer HY Lin", 11 | "social_media": [{ 12 | "twitter": "@jenhylin", 13 | "mastodon": "@jhylin@fosstodon.org", 14 | "bluesky": "jhylin.bsky.social", 15 | "github": "jhylin" 16 | }] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /blogs/juliasilge.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Julia Silge", 3 | "type": "blog", 4 | "url": "https://juliasilge.com/", 5 | "rss_feed": "https://juliasilge.com/categories/rstats/index.xml", 6 | "photo_url": "https://github.com/juliasilge.png", 7 | "description": "A data science blog by Julia Silge", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Julia Silge", 12 | "social_media": [{ 13 | "twitter": "juliasilge", 14 | "mastodon": "@juliasilge@fosstodon.org", 15 | "bluesky": "juliasilge.com", 16 | "github": "juliasilge", 17 | "youtube": "juliasilge", 18 | "website": "https://juliasilge.com/", 19 | "linkedin": "juliasilge", 20 | "orcid": "0000-0002-3671-836X" 21 | }] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /blogs/karbartolome-blog.netlify.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Karina Bartolome", 3 | "subtitle": "Personal blog", 4 | "type": "blog", 5 | "url": "https://karbartolome-blog.netlify.app", 6 | "rss_feed": "https://karbartolome-blog.netlify.app/categories/r/index.xml", 7 | "photo_url": "https://raw.githubusercontent.com/RLadies-BA/RLadies-BA/main/content/authors/kari/avatar.jpg", 8 | "description": "Data science, economics and more", 9 | "language": "es", 10 | "authors": [ 11 | { 12 | "name": "Karina Bartolome", 13 | "social_media": [{ 14 | "twitter": "karbartolome", 15 | "mastodon": "@karbartolome@mastodon.social", 16 | "github": "karbartolome", 17 | "instagram": "karbartolome", 18 | "website": "https://karbartolome-blog.netlify.app", 19 | "linkedin": "karinabartolome", 20 | "meetup": "276190207" 21 | }] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /blogs/lgibson7.quarto.pub-once-upon-a-time-series.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Once Upon a Time Series", 3 | "type": "blog", 4 | "url": "https://lgibson7.quarto.pub/once-upon-a-time-series/", 5 | "photo_url": "https://lgibson7.quarto.pub/once-upon-a-time-series/profile_pic.jpeg", 6 | "language": "en", 7 | "rss_feed": "https://lgibson7.quarto.pub/once-upon-a-time-series/index.xml", 8 | "authors": [ 9 | { 10 | "name": "Lydia Gibson", 11 | "social_media": [{ 12 | "twitter": "Lydz_is_Poppin", 13 | "bluesky": "lydz-gibby.bsky.social", 14 | "github": "lgibson7" 15 | }] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /blogs/loreabad6.github.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Lore Abad", 3 | "type": "blog", 4 | "url": "https://loreabad6.github.io/", 5 | "rss_feed": "https://loreabad6.github.io/index.xml", 6 | "photo_url": "https://loreabad6.github.io/AbadL.jpg", 7 | "description": "My adventures with RSpatial", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Lorena Abad", 12 | "social_media": [{ 13 | "twitter": "@loreabad6", 14 | "mastodon": "@loreabad6@fosstodon.org", 15 | "github": "loreabad6", 16 | "researchgate": "Lorena-Abad", 17 | "linkedin": "lorena-abad", 18 | "orcid": "0000-0003-0554-734X" 19 | }] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /blogs/macarenaquiroga.netlify.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Macarena Quiroga", 3 | "type": "blog", 4 | "url": "https://macarenaquiroga.netlify.app", 5 | "rss_feed": "https://macarenaquiroga.netlify.app/tag/rstats/index.xml", 6 | "photo_url": "https://macarenaquiroga.netlify.app/author/macarena-quiroga/avatar_hu2dcaef447beb41a9ecf37ab13d81ec8c_130063_270x270_fill_q75_lanczos_center.jpg", 7 | "description": "Linguistics, education, and data science with R", 8 | "language": "es", 9 | "authors": [ 10 | { 11 | "name": "Macarena Quiroga", 12 | "social_media": [{ 13 | "twitter": "_msquiroga", 14 | "mastodon": "@macarenaquiroga@datasci.social", 15 | "github": "msquiroga89", 16 | "linkedin": "macarena-quiroga" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/masalmon.eu.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Maëlle's R blog", 3 | "type": "blog", 4 | "url": "https://masalmon.eu/", 5 | "rss_feed": "https://masalmon.eu/post/index.xml", 6 | "photo_url": "https://masalmon.eu/img/barbie_office.jpg", 7 | "description": "R(esearch) Software Engineer & Blogger based in Nancy, France", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Maëlle Salmon", 12 | "social_media": [{ 13 | "twitter": "ma_salmon", 14 | "mastodon": "@maelle@mastodon.social", 15 | "bluesky": "@maellesalmon.bsky.social", 16 | "github": "maelle", 17 | "orcid": "0000-0002-2815-0399" 18 | }] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /blogs/nrennie.rbind.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Nicola Rennie", 3 | "type": "blog", 4 | "url": "https://nrennie.rbind.io", 5 | "rss_feed": "https://nrennie.rbind.io/categories/r/index.xml", 6 | "photo_url": "https://nrennie.rbind.io/img/nicola_blue.jpg", 7 | "description": "Personal website, blog, and portfolio.", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Nicola Rennie", 12 | "social_media": [{ 13 | "twitter": "nrennie35", 14 | "mastodon": "@nrennie@fosstodon.org", 15 | "bluesky": "@nrennie.bsky.social", 16 | "github": "nrennie", 17 | "linkedin": "nicola-rennie-076511b3" 18 | }] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /blogs/pipinghotdata.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Piping Hot Data", 3 | "type": "blog", 4 | "url": "https://www.pipinghotdata.com", 5 | "rss_feed": "https://www.pipinghotdata.com/blog.xml", 6 | "photo_url": "https://www.pipinghotdata.com/cirprofile.png", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Shannon Pileggi", 11 | "social_media": [{ 12 | "twitter": "PipingHotData", 13 | "mastodon": "@PipingHotData@fosstodon.org", 14 | "bluesky": "@pipinghotdata.com", 15 | "website": "https://twitter.com/PipingHotData", 16 | "linkedin": "shannon-m-pileggi/"}] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /blogs/r-ladiesgaborone2021.quarto.pub.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "R-Ladies Gaborone", 3 | "type": "blog", 4 | "url": "https://r-ladiesgaborone2021.quarto.pub", 5 | "photo_url": "https://r-ladiesgaborone2021.quarto.pub/images/bo_5sEb-_400x400.jpg", 6 | "description": "Blog by R-Ladies Gabarone", 7 | "language": "en", 8 | 9 | "authors": [ 10 | { 11 | "name": "R-Ladies Gabarone", 12 | "social_media": [{ 13 | "twitter": "RLadiesGaborone", 14 | "mastodon": "@RLadiesGaborone@fosstodon.org", 15 | "youtube": "r-ladiesgaborone8407", 16 | "linkedin": "r-ladies-gaborone", 17 | "facebook": "RLadiesGaborone" 18 | }] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /blogs/r-ladiesmelbourne.github.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "R-Ladies Melbourne Blog", 3 | "type": "blog", 4 | "url": "https://r-ladiesmelbourne.github.io/", 5 | "photo_url": "https://r-ladiesmelbourne.github.io/images/rladiesmelblogo.png", 6 | "description": "Blog by R-Ladies Melbourne", 7 | "language": "en", 8 | "rss_feed": "https://r-ladiesmelbourne.github.io/blog_index.html", 9 | "authors": [ 10 | { 11 | "name": "R-Ladies Melbourde", 12 | "social_media": [{ 13 | "twitter": "rladiesmelb", 14 | "bluesky": "@rladiesmelb.bsky.social", 15 | "github": "r-ladiesmelbourne", 16 | "linkedin": "https://www.linkedin.com/company/r-ladies-melbourne/", 17 | "meetup": "r-ladies-melbourne", 18 | "website": "https://r-ladiesmelbourne.github.io/" 19 | }] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /blogs/rladies-sp.org.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "R-Ladies São Paulo Blog", 3 | "type": "blog", 4 | "url": "https://rladies-sp.org/", 5 | "photo_url": "https://rladies-sp.org/img/logo.jpg", 6 | "description": "Blog in Brazilian Portuguese, in which people from the R-Ladies community in São Paulo and in Brazil share about the events, R tutorials, etc.", 7 | "language": "pt", 8 | "authors": [ 9 | { 10 | "name": "R-Ladies São Paulo", 11 | "social_media": [{ 12 | "twitter": "rladiessaopaulo", 13 | "github": "R-Ladies-Sao-Paulo", 14 | "instagram": "rladiessaopaulo", 15 | "youtube": "RLadiesS%C3%A3oPaulo", 16 | "website": "https://rladies-sp.org/" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/sabrinaschalz.wordpress.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Data Science Blog", 3 | "type": "blog", 4 | "url": "https://sabrinaschalz.wordpress.com/data-science-blog/", 5 | "photo_url": "https://sabrinaschalz.files.wordpress.com/2022/07/screenshot_20220720-132450_instagram.jpg", 6 | "language": "en", 7 | "rss_feed": "https://sabrinaschalz.wordpress.com/feed/", 8 | "authors": [ 9 | { 10 | "name": "Sabrina Schalz", 11 | "social_media": [{ 12 | "mastodon": "@sabrinaschalz@fediscience.org", 13 | "bluesky": "sabrinaschalz.bsky.social", 14 | "github": "SabrinaSchalz", 15 | "website": "https://sabrinaschalz.wordpress.com", 16 | "orcid": "0000-0003-0237-4367" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/sarahgillespie.github.io.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Sarah Gillespie's blog", 3 | "subtitle": "United States student majoring in (1) statistical and data sciences and (2) quantitative economics", 4 | "type": "blog", 5 | "url": "https://sarahgillespie.github.io/SG/", 6 | "photo_url": "https://raw.githubusercontent.com/SarahGillespie/SG/main/docs/img_about_harbor_photo.jpg", 7 | "description": "Strategies to reduce machine learning bias through planning, data curation, and specific algorithm techniques.", 8 | "language": "en", 9 | "authors": [ 10 | { 11 | "name": "Sarah Gillespie", 12 | "social_media": [{ 13 | "twitter": "SarahG4567", 14 | "github": "SarahGillespie" 15 | }] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /blogs/sctyner.me.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Sam Tyner-Monroe", 3 | "type": "blog", 4 | "url": "https://sctyner.me", 5 | "photo_url": "https://sctyner.me/author/sam-tyner/avatar_hufb4a28a2230dd21f7cfbe0269e6196dd_54857_270x270_fill_q90_lanczos_center.jpg", 6 | "rss_feed": "https://sctyner.me/category/r/index.xml", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Sam Tyner-Monroe", 11 | "social_media": [{ 12 | "twitter": "sctyner", 13 | "github": "sctyner", 14 | "website": "https://sctyner.me", 15 | "linkedin": "sctyner/", 16 | "orcid": "0000-0001-5484-2957" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/shelkariuki.netlify.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Shel Kariuki's blog", 3 | "type": "blog", 4 | "url": "https://shelkariuki.netlify.app/", 5 | "photo_url": "https://shelkariuki.netlify.app/author/avatar_hubc1f6cfd80a3e5aa5176d464d1c32c28_52181_270x270_fill_q90_lanczos_center.jpeg", 6 | "description": "Data Analytics Consultant based in Nairobi, Kenya", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Shel Kariuki", 11 | "social_media": [{ 12 | "twitter": "Shel_Kariuki", 13 | "github": "Shelmith-Kariuki" 14 | }] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /blogs/silviacanelon.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Meeting People Where They R", 3 | "type": "blog", 4 | "url": "https://silviacanelon.com", 5 | "rss_feed": "https://silviacanelon.com/blog/index-r.xml", 6 | "language": "en", 7 | "authors": [ 8 | { 9 | "name": "Silvia Canelón", 10 | "social_media": [{ 11 | "twitter": "spcanelon", 12 | "mastodon": "@spcanelon@hachyderm.io", 13 | "bluesky": "silviacanelon.com", 14 | "github": "spcanelon", 15 | "linkedin": "spcanelon/" 16 | }] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /blogs/soyandrea.netlify.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Soy Andrea blog", 3 | "type": "blog", 4 | "url": "https://soyandrea.netlify.app/", 5 | "rss_feed": "https://soyandrea.netlify.app/tag/r/index.xml", 6 | "photo_url": "https://soyandrea.netlify.app/author/andrea/avatar.jpg", 7 | "description": "Socióloga, web profesional & portafolio", 8 | "language": "es", 9 | "authors": [ 10 | { 11 | "name": "Andrea Gómez Vargas", 12 | "social_media": [{ 13 | "twitter": "me_andre", 14 | "mastodon": "@me_andre@mastodon.social", 15 | "github": "SoyAndrea", 16 | "linkedin": "andreasociologa" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/sporella.xyz.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Ciencia de Datos en Español", 3 | "type": "blog", 4 | "url": "https://sporella.xyz", 5 | "rss_feed": "https://sporella.xyz/tags/r/index.xml", 6 | "photo_url": "https://sporella.xyz/img/me.png", 7 | "description": "Blog in Spanish about Data Science and personal projects.", 8 | "language": "es", 9 | "authors": [ 10 | { 11 | "name": "Steph Orellana Bello", 12 | "social_media": [{ 13 | "twitter": "sporella", 14 | "mastodon": "@sporella@fosstodon.org", 15 | "bluesky": "sporella.bsky.social", 16 | "github": "sporella", 17 | "website": "https://sporella.xyz" 18 | }] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /blogs/steffilazerte.ca.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Steffi LaZerte", 3 | "subtitle": "Tips & Tricks", 4 | "type": "blog", 5 | "url": "https://steffilazerte.ca/tips_and_tricks.html", 6 | "photo_url": "https://github.com/steffilazerte.png", 7 | "description": "Collection of statistical notes, general tips and tricks, and useful citations which serve as a reference when helping others with R, especially for statistics in the natural sciences.", 8 | "language": "en", 9 | "rss_feed": "https://steffilazerte.ca/tips_and_tricks-r.xml", 10 | "authors": [ 11 | { 12 | "name": "Steffi LaZerte", 13 | "social_media": [{ 14 | "github": "steffilazerte", 15 | "mastodon": "@steffilazerte@fosstodon.org", 16 | "website": "https://steffilazerte.ca", 17 | "orcid": "0000-0002-7690-8360", 18 | "researchgate": "Steffi-Lazerte", 19 | "linkedin": "steffilazerte", 20 | "twitter": "steffilazerte" 21 | }] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /blogs/surroundedbydata.netlify.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Surrounded by Data", 3 | "type": "blog", 4 | "url": "https://surroundedbydata.netlify.app/", 5 | "photo_url": "https://surroundedbydata.netlify.app/about_files/Veerle_klein.jpg", 6 | "description": "Stories about the data that can be found all around us, in Dutch and English", 7 | "language": "en (required)", 8 | "authors": [ 9 | { 10 | "name": "Veerle van Son", 11 | "social_media": [{ 12 | "twitter": "veerlevanson", 13 | "github": "veerlevanson", 14 | "instagram": "veerle.van.son", 15 | "linkedin": "veerlevanson", 16 | "meetup": "rladies-den-bosch" 17 | }] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /blogs/thetidytrekker.com.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Exploration Corner", 3 | "subtitle": "The Tidy Trekker", 4 | "type": "blog", 5 | "url": "https://thetidytrekker.com/blog.html", 6 | "rss_feed": "https://thetidytrekker.com/blog.xml", 7 | "photo_url": "https://thetidytrekker.com/assets/EC-Header.gif", 8 | "description": "A mix of R Stats, general coding, and personal content", 9 | "language": "en", 10 | "authors": [ 11 | { 12 | "name": "Meghan Harris", 13 | "social_media": [{ 14 | "twitter": "meghansharris", 15 | "mastodon": "@meghansharris@fosstodon.org", 16 | "github": "Meghansaha", 17 | "website": "https://thetidytrekker.com", 18 | "linkedin": "meghan-harris", 19 | "orcid": "0000-0003-3922-8101" 20 | }] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /blogs/yabellini.netlify.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Yanina Bellini Saibene", 3 | "type": "blog", 4 | "url": "https://yabellini.netlify.app/blog/", 5 | "rss_feed": "https://yabellini.netlify.app/categories/rstats/index.xml", 6 | "description": "Writings about my interests and my work: R, education, communities of practice, research, data science, experiences, talks and some more personal stuff.", 7 | "language": "en", 8 | "authors": [ 9 | { 10 | "name": "Yanina Bellini Saibene", 11 | "social_media": [{ 12 | "mastodon": "@yabellini@fosstodon.org", 13 | "bluesky": "yabellini.bsky.social", 14 | "github": "yabellini", 15 | "instagram": "yanina.bellini", 16 | "youtube": "yabellini", 17 | "website": "https://yabellini.netlify.app/", 18 | "linkedin": "/in/yabellini/", 19 | "orcid": "0000-0002-4522-7466" 20 | }] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /blogs/youtube.com.c.ggnot2.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Melissa Van Bussel (ggnot2)'s YouTube channel about R", 3 | "type": "youtube", 4 | "url": "https://www.youtube.com/c/ggnot2", 5 | "photo_url": "https://images.squarespace-cdn.com/content/v1/615e5d6733744f26a45a0916/dbb29443-45e7-4f7c-b8d8-ab721631d225/selectedEdit_2.png", 6 | "description": "Melissa Van Bussel (ggnot2)'s YouTube channel that contains tutorials about R programming and Data Science", 7 | "language": "en", 8 | "rss_feed": "https://www.youtube.com/feeds/videos.xml?channel_id=UCWPCd6tPtoLJYzQQ681pe5Q", 9 | "authors": [ 10 | { 11 | "name": "Melissa Van Bussel", 12 | "social_media": [{ 13 | "youtube": "ggnot2", 14 | "twitter": "melvanbussel", 15 | "linkedin": "melissavanbussel", 16 | "github": "melissavanbussel", 17 | "website": "https://www.melissavanbussel.com" 18 | }] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /images/contrib_newfile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rladies/awesome-rladies-blogs/c2df9cf9f6fc9c24090a9d4c8330f4aa42a6505e/images/contrib_newfile.png -------------------------------------------------------------------------------- /images/contrib_patch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rladies/awesome-rladies-blogs/c2df9cf9f6fc9c24090a9d4c8330f4aa42a6505e/images/contrib_patch.png -------------------------------------------------------------------------------- /renv.lock: -------------------------------------------------------------------------------- 1 | { 2 | "R": { 3 | "Version": "4.3.3", 4 | "Repositories": [ 5 | { 6 | "Name": "CRAN", 7 | "URL": "https://cran.rstudio.com" 8 | } 9 | ] 10 | }, 11 | "Packages": { 12 | "R6": { 13 | "Package": "R6", 14 | "Version": "2.5.1", 15 | "Source": "Repository", 16 | "Repository": "RSPM", 17 | "Requirements": [ 18 | "R" 19 | ], 20 | "Hash": "470851b6d5d0ac559e9d01bb352b4021" 21 | }, 22 | "Rcpp": { 23 | "Package": "Rcpp", 24 | "Version": "1.0.13-1", 25 | "Source": "Repository", 26 | "Repository": "CRAN", 27 | "Requirements": [ 28 | "methods", 29 | "utils" 30 | ], 31 | "Hash": "6b868847b365672d6c1677b1608da9ed" 32 | }, 33 | "V8": { 34 | "Package": "V8", 35 | "Version": "6.0.0", 36 | "Source": "Repository", 37 | "Repository": "CRAN", 38 | "Requirements": [ 39 | "Rcpp", 40 | "curl", 41 | "jsonlite", 42 | "utils" 43 | ], 44 | "Hash": "6603bfcbc7883a5fed41fb13042a3899" 45 | }, 46 | "base64enc": { 47 | "Package": "base64enc", 48 | "Version": "0.1-3", 49 | "Source": "Repository", 50 | "Repository": "RSPM", 51 | "Requirements": [ 52 | "R" 53 | ], 54 | "Hash": "543776ae6848fde2f48ff3816d0628bc" 55 | }, 56 | "bslib": { 57 | "Package": "bslib", 58 | "Version": "0.8.0", 59 | "Source": "Repository", 60 | "Repository": "CRAN", 61 | "Requirements": [ 62 | "R", 63 | "base64enc", 64 | "cachem", 65 | "fastmap", 66 | "grDevices", 67 | "htmltools", 68 | "jquerylib", 69 | "jsonlite", 70 | "lifecycle", 71 | "memoise", 72 | "mime", 73 | "rlang", 74 | "sass" 75 | ], 76 | "Hash": "b299c6741ca9746fb227debcb0f9fb6c" 77 | }, 78 | "cachem": { 79 | "Package": "cachem", 80 | "Version": "1.1.0", 81 | "Source": "Repository", 82 | "Repository": "RSPM", 83 | "Requirements": [ 84 | "fastmap", 85 | "rlang" 86 | ], 87 | "Hash": "cd9a672193789068eb5a2aad65a0dedf" 88 | }, 89 | "cli": { 90 | "Package": "cli", 91 | "Version": "3.6.3", 92 | "Source": "Repository", 93 | "Repository": "CRAN", 94 | "Requirements": [ 95 | "R", 96 | "utils" 97 | ], 98 | "Hash": "b21916dd77a27642b447374a5d30ecf3" 99 | }, 100 | "curl": { 101 | "Package": "curl", 102 | "Version": "6.0.1", 103 | "Source": "Repository", 104 | "Repository": "CRAN", 105 | "Requirements": [ 106 | "R" 107 | ], 108 | "Hash": "e8ba62486230951fcd2b881c5be23f96" 109 | }, 110 | "digest": { 111 | "Package": "digest", 112 | "Version": "0.6.37", 113 | "Source": "Repository", 114 | "Repository": "CRAN", 115 | "Requirements": [ 116 | "R", 117 | "utils" 118 | ], 119 | "Hash": "33698c4b3127fc9f506654607fb73676" 120 | }, 121 | "evaluate": { 122 | "Package": "evaluate", 123 | "Version": "1.0.1", 124 | "Source": "Repository", 125 | "Repository": "CRAN", 126 | "Requirements": [ 127 | "R" 128 | ], 129 | "Hash": "3fd29944b231036ad67c3edb32e02201" 130 | }, 131 | "fastmap": { 132 | "Package": "fastmap", 133 | "Version": "1.2.0", 134 | "Source": "Repository", 135 | "Repository": "RSPM", 136 | "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" 137 | }, 138 | "fontawesome": { 139 | "Package": "fontawesome", 140 | "Version": "0.5.3", 141 | "Source": "Repository", 142 | "Repository": "CRAN", 143 | "Requirements": [ 144 | "R", 145 | "htmltools", 146 | "rlang" 147 | ], 148 | "Hash": "bd1297f9b5b1fc1372d19e2c4cd82215" 149 | }, 150 | "fs": { 151 | "Package": "fs", 152 | "Version": "1.6.5", 153 | "Source": "Repository", 154 | "Repository": "CRAN", 155 | "Requirements": [ 156 | "R", 157 | "methods" 158 | ], 159 | "Hash": "7f48af39fa27711ea5fbd183b399920d" 160 | }, 161 | "glue": { 162 | "Package": "glue", 163 | "Version": "1.8.0", 164 | "Source": "Repository", 165 | "Repository": "CRAN", 166 | "Requirements": [ 167 | "R", 168 | "methods" 169 | ], 170 | "Hash": "5899f1eaa825580172bb56c08266f37c" 171 | }, 172 | "here": { 173 | "Package": "here", 174 | "Version": "1.0.1", 175 | "Source": "Repository", 176 | "Repository": "RSPM", 177 | "Requirements": [ 178 | "rprojroot" 179 | ], 180 | "Hash": "24b224366f9c2e7534d2344d10d59211" 181 | }, 182 | "highr": { 183 | "Package": "highr", 184 | "Version": "0.11", 185 | "Source": "Repository", 186 | "Repository": "RSPM", 187 | "Requirements": [ 188 | "R", 189 | "xfun" 190 | ], 191 | "Hash": "d65ba49117ca223614f71b60d85b8ab7" 192 | }, 193 | "htmltools": { 194 | "Package": "htmltools", 195 | "Version": "0.5.8.1", 196 | "Source": "Repository", 197 | "Repository": "RSPM", 198 | "Requirements": [ 199 | "R", 200 | "base64enc", 201 | "digest", 202 | "fastmap", 203 | "grDevices", 204 | "rlang", 205 | "utils" 206 | ], 207 | "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" 208 | }, 209 | "jquerylib": { 210 | "Package": "jquerylib", 211 | "Version": "0.1.4", 212 | "Source": "Repository", 213 | "Repository": "RSPM", 214 | "Requirements": [ 215 | "htmltools" 216 | ], 217 | "Hash": "5aab57a3bd297eee1c1d862735972182" 218 | }, 219 | "jsonlite": { 220 | "Package": "jsonlite", 221 | "Version": "1.8.9", 222 | "Source": "Repository", 223 | "Repository": "CRAN", 224 | "Requirements": [ 225 | "methods" 226 | ], 227 | "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" 228 | }, 229 | "jsonvalidate": { 230 | "Package": "jsonvalidate", 231 | "Version": "1.3.2", 232 | "Source": "Repository", 233 | "Repository": "CRAN", 234 | "Requirements": [ 235 | "V8" 236 | ], 237 | "Hash": "cdc2843ef7f44f157198bb99aea7552d" 238 | }, 239 | "knitr": { 240 | "Package": "knitr", 241 | "Version": "1.49", 242 | "Source": "Repository", 243 | "Repository": "CRAN", 244 | "Requirements": [ 245 | "R", 246 | "evaluate", 247 | "highr", 248 | "methods", 249 | "tools", 250 | "xfun", 251 | "yaml" 252 | ], 253 | "Hash": "9fcb189926d93c636dea94fbe4f44480" 254 | }, 255 | "lifecycle": { 256 | "Package": "lifecycle", 257 | "Version": "1.0.4", 258 | "Source": "Repository", 259 | "Repository": "CRAN", 260 | "Requirements": [ 261 | "R", 262 | "cli", 263 | "glue", 264 | "rlang" 265 | ], 266 | "Hash": "b8552d117e1b808b09a832f589b79035" 267 | }, 268 | "memoise": { 269 | "Package": "memoise", 270 | "Version": "2.0.1", 271 | "Source": "Repository", 272 | "Repository": "RSPM", 273 | "Requirements": [ 274 | "cachem", 275 | "rlang" 276 | ], 277 | "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" 278 | }, 279 | "mime": { 280 | "Package": "mime", 281 | "Version": "0.12", 282 | "Source": "Repository", 283 | "Repository": "RSPM", 284 | "Requirements": [ 285 | "tools" 286 | ], 287 | "Hash": "18e9c28c1d3ca1560ce30658b22ce104" 288 | }, 289 | "rappdirs": { 290 | "Package": "rappdirs", 291 | "Version": "0.3.3", 292 | "Source": "Repository", 293 | "Repository": "RSPM", 294 | "Requirements": [ 295 | "R" 296 | ], 297 | "Hash": "5e3c5dc0b071b21fa128676560dbe94d" 298 | }, 299 | "renv": { 300 | "Package": "renv", 301 | "Version": "1.0.2", 302 | "Source": "Repository", 303 | "Repository": "CRAN", 304 | "Requirements": [ 305 | "utils" 306 | ], 307 | "Hash": "4b22ac016fe54028b88d0c68badbd061" 308 | }, 309 | "rlang": { 310 | "Package": "rlang", 311 | "Version": "1.1.4", 312 | "Source": "Repository", 313 | "Repository": "RSPM", 314 | "Requirements": [ 315 | "R", 316 | "utils" 317 | ], 318 | "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" 319 | }, 320 | "rmarkdown": { 321 | "Package": "rmarkdown", 322 | "Version": "2.29", 323 | "Source": "Repository", 324 | "Repository": "CRAN", 325 | "Requirements": [ 326 | "R", 327 | "bslib", 328 | "evaluate", 329 | "fontawesome", 330 | "htmltools", 331 | "jquerylib", 332 | "jsonlite", 333 | "knitr", 334 | "methods", 335 | "tinytex", 336 | "tools", 337 | "utils", 338 | "xfun", 339 | "yaml" 340 | ], 341 | "Hash": "df99277f63d01c34e95e3d2f06a79736" 342 | }, 343 | "rprojroot": { 344 | "Package": "rprojroot", 345 | "Version": "2.0.4", 346 | "Source": "Repository", 347 | "Repository": "CRAN", 348 | "Requirements": [ 349 | "R" 350 | ], 351 | "Hash": "4c8415e0ec1e29f3f4f6fc108bef0144" 352 | }, 353 | "sass": { 354 | "Package": "sass", 355 | "Version": "0.4.9", 356 | "Source": "Repository", 357 | "Repository": "RSPM", 358 | "Requirements": [ 359 | "R6", 360 | "fs", 361 | "htmltools", 362 | "rappdirs", 363 | "rlang" 364 | ], 365 | "Hash": "d53dbfddf695303ea4ad66f86e99b95d" 366 | }, 367 | "tinytex": { 368 | "Package": "tinytex", 369 | "Version": "0.54", 370 | "Source": "Repository", 371 | "Repository": "CRAN", 372 | "Requirements": [ 373 | "xfun" 374 | ], 375 | "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" 376 | }, 377 | "xfun": { 378 | "Package": "xfun", 379 | "Version": "0.49", 380 | "Source": "Repository", 381 | "Repository": "CRAN", 382 | "Requirements": [ 383 | "R", 384 | "grDevices", 385 | "stats", 386 | "tools" 387 | ], 388 | "Hash": "8687398773806cfff9401a2feca96298" 389 | }, 390 | "yaml": { 391 | "Package": "yaml", 392 | "Version": "2.3.10", 393 | "Source": "Repository", 394 | "Repository": "CRAN", 395 | "Hash": "51dab85c6c98e50a18d7551e9d49f76c" 396 | } 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /renv/.gitignore: -------------------------------------------------------------------------------- 1 | library/ 2 | local/ 3 | cellar/ 4 | lock/ 5 | python/ 6 | sandbox/ 7 | staging/ 8 | -------------------------------------------------------------------------------- /renv/activate.R: -------------------------------------------------------------------------------- 1 | 2 | local({ 3 | 4 | # the requested version of renv 5 | version <- "1.0.2" 6 | attr(version, "sha") <- NULL 7 | 8 | # the project directory 9 | project <- getwd() 10 | 11 | # use start-up diagnostics if enabled 12 | diagnostics <- Sys.getenv("RENV_STARTUP_DIAGNOSTICS", unset = "FALSE") 13 | if (diagnostics) { 14 | start <- Sys.time() 15 | profile <- tempfile("renv-startup-", fileext = ".Rprof") 16 | utils::Rprof(profile) 17 | on.exit({ 18 | utils::Rprof(NULL) 19 | elapsed <- signif(difftime(Sys.time(), start, units = "auto"), digits = 2L) 20 | writeLines(sprintf("- renv took %s to run the autoloader.", format(elapsed))) 21 | writeLines(sprintf("- Profile: %s", profile)) 22 | print(utils::summaryRprof(profile)) 23 | }, add = TRUE) 24 | } 25 | 26 | # figure out whether the autoloader is enabled 27 | enabled <- local({ 28 | 29 | # first, check config option 30 | override <- getOption("renv.config.autoloader.enabled") 31 | if (!is.null(override)) 32 | return(override) 33 | 34 | # next, check environment variables 35 | # TODO: prefer using the configuration one in the future 36 | envvars <- c( 37 | "RENV_CONFIG_AUTOLOADER_ENABLED", 38 | "RENV_AUTOLOADER_ENABLED", 39 | "RENV_ACTIVATE_PROJECT" 40 | ) 41 | 42 | for (envvar in envvars) { 43 | envval <- Sys.getenv(envvar, unset = NA) 44 | if (!is.na(envval)) 45 | return(tolower(envval) %in% c("true", "t", "1")) 46 | } 47 | 48 | # enable by default 49 | TRUE 50 | 51 | }) 52 | 53 | if (!enabled) 54 | return(FALSE) 55 | 56 | # avoid recursion 57 | if (identical(getOption("renv.autoloader.running"), TRUE)) { 58 | warning("ignoring recursive attempt to run renv autoloader") 59 | return(invisible(TRUE)) 60 | } 61 | 62 | # signal that we're loading renv during R startup 63 | options(renv.autoloader.running = TRUE) 64 | on.exit(options(renv.autoloader.running = NULL), add = TRUE) 65 | 66 | # signal that we've consented to use renv 67 | options(renv.consent = TRUE) 68 | 69 | # load the 'utils' package eagerly -- this ensures that renv shims, which 70 | # mask 'utils' packages, will come first on the search path 71 | library(utils, lib.loc = .Library) 72 | 73 | # unload renv if it's already been loaded 74 | if ("renv" %in% loadedNamespaces()) 75 | unloadNamespace("renv") 76 | 77 | # load bootstrap tools 78 | `%||%` <- function(x, y) { 79 | if (is.null(x)) y else x 80 | } 81 | 82 | catf <- function(fmt, ..., appendLF = TRUE) { 83 | 84 | quiet <- getOption("renv.bootstrap.quiet", default = FALSE) 85 | if (quiet) 86 | return(invisible()) 87 | 88 | msg <- sprintf(fmt, ...) 89 | cat(msg, file = stdout(), sep = if (appendLF) "\n" else "") 90 | 91 | invisible(msg) 92 | 93 | } 94 | 95 | header <- function(label, 96 | ..., 97 | prefix = "#", 98 | suffix = "-", 99 | n = min(getOption("width"), 78)) 100 | { 101 | label <- sprintf(label, ...) 102 | n <- max(n - nchar(label) - nchar(prefix) - 2L, 8L) 103 | if (n <= 0) 104 | return(paste(prefix, label)) 105 | 106 | tail <- paste(rep.int(suffix, n), collapse = "") 107 | paste0(prefix, " ", label, " ", tail) 108 | 109 | } 110 | 111 | startswith <- function(string, prefix) { 112 | substring(string, 1, nchar(prefix)) == prefix 113 | } 114 | 115 | bootstrap <- function(version, library) { 116 | 117 | friendly <- renv_bootstrap_version_friendly(version) 118 | section <- header(sprintf("Bootstrapping renv %s", friendly)) 119 | catf(section) 120 | 121 | # attempt to download renv 122 | catf("- Downloading renv ... ", appendLF = FALSE) 123 | withCallingHandlers( 124 | tarball <- renv_bootstrap_download(version), 125 | error = function(err) { 126 | catf("FAILED") 127 | stop("failed to download:\n", conditionMessage(err)) 128 | } 129 | ) 130 | catf("OK") 131 | on.exit(unlink(tarball), add = TRUE) 132 | 133 | # now attempt to install 134 | catf("- Installing renv ... ", appendLF = FALSE) 135 | withCallingHandlers( 136 | status <- renv_bootstrap_install(version, tarball, library), 137 | error = function(err) { 138 | catf("FAILED") 139 | stop("failed to install:\n", conditionMessage(err)) 140 | } 141 | ) 142 | catf("OK") 143 | 144 | # add empty line to break up bootstrapping from normal output 145 | catf("") 146 | 147 | return(invisible()) 148 | } 149 | 150 | renv_bootstrap_tests_running <- function() { 151 | getOption("renv.tests.running", default = FALSE) 152 | } 153 | 154 | renv_bootstrap_repos <- function() { 155 | 156 | # get CRAN repository 157 | cran <- getOption("renv.repos.cran", "https://cloud.r-project.org") 158 | 159 | # check for repos override 160 | repos <- Sys.getenv("RENV_CONFIG_REPOS_OVERRIDE", unset = NA) 161 | if (!is.na(repos)) { 162 | 163 | # check for RSPM; if set, use a fallback repository for renv 164 | rspm <- Sys.getenv("RSPM", unset = NA) 165 | if (identical(rspm, repos)) 166 | repos <- c(RSPM = rspm, CRAN = cran) 167 | 168 | return(repos) 169 | 170 | } 171 | 172 | # check for lockfile repositories 173 | repos <- tryCatch(renv_bootstrap_repos_lockfile(), error = identity) 174 | if (!inherits(repos, "error") && length(repos)) 175 | return(repos) 176 | 177 | # retrieve current repos 178 | repos <- getOption("repos") 179 | 180 | # ensure @CRAN@ entries are resolved 181 | repos[repos == "@CRAN@"] <- cran 182 | 183 | # add in renv.bootstrap.repos if set 184 | default <- c(FALLBACK = "https://cloud.r-project.org") 185 | extra <- getOption("renv.bootstrap.repos", default = default) 186 | repos <- c(repos, extra) 187 | 188 | # remove duplicates that might've snuck in 189 | dupes <- duplicated(repos) | duplicated(names(repos)) 190 | repos[!dupes] 191 | 192 | } 193 | 194 | renv_bootstrap_repos_lockfile <- function() { 195 | 196 | lockpath <- Sys.getenv("RENV_PATHS_LOCKFILE", unset = "renv.lock") 197 | if (!file.exists(lockpath)) 198 | return(NULL) 199 | 200 | lockfile <- tryCatch(renv_json_read(lockpath), error = identity) 201 | if (inherits(lockfile, "error")) { 202 | warning(lockfile) 203 | return(NULL) 204 | } 205 | 206 | repos <- lockfile$R$Repositories 207 | if (length(repos) == 0) 208 | return(NULL) 209 | 210 | keys <- vapply(repos, `[[`, "Name", FUN.VALUE = character(1)) 211 | vals <- vapply(repos, `[[`, "URL", FUN.VALUE = character(1)) 212 | names(vals) <- keys 213 | 214 | return(vals) 215 | 216 | } 217 | 218 | renv_bootstrap_download <- function(version) { 219 | 220 | sha <- attr(version, "sha", exact = TRUE) 221 | 222 | methods <- if (!is.null(sha)) { 223 | 224 | # attempting to bootstrap a development version of renv 225 | c( 226 | function() renv_bootstrap_download_tarball(sha), 227 | function() renv_bootstrap_download_github(sha) 228 | ) 229 | 230 | } else { 231 | 232 | # attempting to bootstrap a release version of renv 233 | c( 234 | function() renv_bootstrap_download_tarball(version), 235 | function() renv_bootstrap_download_cran_latest(version), 236 | function() renv_bootstrap_download_cran_archive(version) 237 | ) 238 | 239 | } 240 | 241 | for (method in methods) { 242 | path <- tryCatch(method(), error = identity) 243 | if (is.character(path) && file.exists(path)) 244 | return(path) 245 | } 246 | 247 | stop("All download methods failed") 248 | 249 | } 250 | 251 | renv_bootstrap_download_impl <- function(url, destfile) { 252 | 253 | mode <- "wb" 254 | 255 | # https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17715 256 | fixup <- 257 | Sys.info()[["sysname"]] == "Windows" && 258 | substring(url, 1L, 5L) == "file:" 259 | 260 | if (fixup) 261 | mode <- "w+b" 262 | 263 | args <- list( 264 | url = url, 265 | destfile = destfile, 266 | mode = mode, 267 | quiet = TRUE 268 | ) 269 | 270 | if ("headers" %in% names(formals(utils::download.file))) 271 | args$headers <- renv_bootstrap_download_custom_headers(url) 272 | 273 | do.call(utils::download.file, args) 274 | 275 | } 276 | 277 | renv_bootstrap_download_custom_headers <- function(url) { 278 | 279 | headers <- getOption("renv.download.headers") 280 | if (is.null(headers)) 281 | return(character()) 282 | 283 | if (!is.function(headers)) 284 | stopf("'renv.download.headers' is not a function") 285 | 286 | headers <- headers(url) 287 | if (length(headers) == 0L) 288 | return(character()) 289 | 290 | if (is.list(headers)) 291 | headers <- unlist(headers, recursive = FALSE, use.names = TRUE) 292 | 293 | ok <- 294 | is.character(headers) && 295 | is.character(names(headers)) && 296 | all(nzchar(names(headers))) 297 | 298 | if (!ok) 299 | stop("invocation of 'renv.download.headers' did not return a named character vector") 300 | 301 | headers 302 | 303 | } 304 | 305 | renv_bootstrap_download_cran_latest <- function(version) { 306 | 307 | spec <- renv_bootstrap_download_cran_latest_find(version) 308 | type <- spec$type 309 | repos <- spec$repos 310 | 311 | baseurl <- utils::contrib.url(repos = repos, type = type) 312 | ext <- if (identical(type, "source")) 313 | ".tar.gz" 314 | else if (Sys.info()[["sysname"]] == "Windows") 315 | ".zip" 316 | else 317 | ".tgz" 318 | name <- sprintf("renv_%s%s", version, ext) 319 | url <- paste(baseurl, name, sep = "/") 320 | 321 | destfile <- file.path(tempdir(), name) 322 | status <- tryCatch( 323 | renv_bootstrap_download_impl(url, destfile), 324 | condition = identity 325 | ) 326 | 327 | if (inherits(status, "condition")) 328 | return(FALSE) 329 | 330 | # report success and return 331 | destfile 332 | 333 | } 334 | 335 | renv_bootstrap_download_cran_latest_find <- function(version) { 336 | 337 | # check whether binaries are supported on this system 338 | binary <- 339 | getOption("renv.bootstrap.binary", default = TRUE) && 340 | !identical(.Platform$pkgType, "source") && 341 | !identical(getOption("pkgType"), "source") && 342 | Sys.info()[["sysname"]] %in% c("Darwin", "Windows") 343 | 344 | types <- c(if (binary) "binary", "source") 345 | 346 | # iterate over types + repositories 347 | for (type in types) { 348 | for (repos in renv_bootstrap_repos()) { 349 | 350 | # retrieve package database 351 | db <- tryCatch( 352 | as.data.frame( 353 | utils::available.packages(type = type, repos = repos), 354 | stringsAsFactors = FALSE 355 | ), 356 | error = identity 357 | ) 358 | 359 | if (inherits(db, "error")) 360 | next 361 | 362 | # check for compatible entry 363 | entry <- db[db$Package %in% "renv" & db$Version %in% version, ] 364 | if (nrow(entry) == 0) 365 | next 366 | 367 | # found it; return spec to caller 368 | spec <- list(entry = entry, type = type, repos = repos) 369 | return(spec) 370 | 371 | } 372 | } 373 | 374 | # if we got here, we failed to find renv 375 | fmt <- "renv %s is not available from your declared package repositories" 376 | stop(sprintf(fmt, version)) 377 | 378 | } 379 | 380 | renv_bootstrap_download_cran_archive <- function(version) { 381 | 382 | name <- sprintf("renv_%s.tar.gz", version) 383 | repos <- renv_bootstrap_repos() 384 | urls <- file.path(repos, "src/contrib/Archive/renv", name) 385 | destfile <- file.path(tempdir(), name) 386 | 387 | for (url in urls) { 388 | 389 | status <- tryCatch( 390 | renv_bootstrap_download_impl(url, destfile), 391 | condition = identity 392 | ) 393 | 394 | if (identical(status, 0L)) 395 | return(destfile) 396 | 397 | } 398 | 399 | return(FALSE) 400 | 401 | } 402 | 403 | renv_bootstrap_download_tarball <- function(version) { 404 | 405 | # if the user has provided the path to a tarball via 406 | # an environment variable, then use it 407 | tarball <- Sys.getenv("RENV_BOOTSTRAP_TARBALL", unset = NA) 408 | if (is.na(tarball)) 409 | return() 410 | 411 | # allow directories 412 | if (dir.exists(tarball)) { 413 | name <- sprintf("renv_%s.tar.gz", version) 414 | tarball <- file.path(tarball, name) 415 | } 416 | 417 | # bail if it doesn't exist 418 | if (!file.exists(tarball)) { 419 | 420 | # let the user know we weren't able to honour their request 421 | fmt <- "- RENV_BOOTSTRAP_TARBALL is set (%s) but does not exist." 422 | msg <- sprintf(fmt, tarball) 423 | warning(msg) 424 | 425 | # bail 426 | return() 427 | 428 | } 429 | 430 | catf("- Using local tarball '%s'.", tarball) 431 | tarball 432 | 433 | } 434 | 435 | renv_bootstrap_download_github <- function(version) { 436 | 437 | enabled <- Sys.getenv("RENV_BOOTSTRAP_FROM_GITHUB", unset = "TRUE") 438 | if (!identical(enabled, "TRUE")) 439 | return(FALSE) 440 | 441 | # prepare download options 442 | pat <- Sys.getenv("GITHUB_PAT") 443 | if (nzchar(Sys.which("curl")) && nzchar(pat)) { 444 | fmt <- "--location --fail --header \"Authorization: token %s\"" 445 | extra <- sprintf(fmt, pat) 446 | saved <- options("download.file.method", "download.file.extra") 447 | options(download.file.method = "curl", download.file.extra = extra) 448 | on.exit(do.call(base::options, saved), add = TRUE) 449 | } else if (nzchar(Sys.which("wget")) && nzchar(pat)) { 450 | fmt <- "--header=\"Authorization: token %s\"" 451 | extra <- sprintf(fmt, pat) 452 | saved <- options("download.file.method", "download.file.extra") 453 | options(download.file.method = "wget", download.file.extra = extra) 454 | on.exit(do.call(base::options, saved), add = TRUE) 455 | } 456 | 457 | url <- file.path("https://api.github.com/repos/rstudio/renv/tarball", version) 458 | name <- sprintf("renv_%s.tar.gz", version) 459 | destfile <- file.path(tempdir(), name) 460 | 461 | status <- tryCatch( 462 | renv_bootstrap_download_impl(url, destfile), 463 | condition = identity 464 | ) 465 | 466 | if (!identical(status, 0L)) 467 | return(FALSE) 468 | 469 | renv_bootstrap_download_augment(destfile) 470 | 471 | return(destfile) 472 | 473 | } 474 | 475 | # Add Sha to DESCRIPTION. This is stop gap until #890, after which we 476 | # can use renv::install() to fully capture metadata. 477 | renv_bootstrap_download_augment <- function(destfile) { 478 | sha <- renv_bootstrap_git_extract_sha1_tar(destfile) 479 | if (is.null(sha)) { 480 | return() 481 | } 482 | 483 | # Untar 484 | tempdir <- tempfile("renv-github-") 485 | on.exit(unlink(tempdir, recursive = TRUE), add = TRUE) 486 | untar(destfile, exdir = tempdir) 487 | pkgdir <- dir(tempdir, full.names = TRUE)[[1]] 488 | 489 | # Modify description 490 | desc_path <- file.path(pkgdir, "DESCRIPTION") 491 | desc_lines <- readLines(desc_path) 492 | remotes_fields <- c( 493 | "RemoteType: github", 494 | "RemoteHost: api.github.com", 495 | "RemoteRepo: renv", 496 | "RemoteUsername: rstudio", 497 | "RemotePkgRef: rstudio/renv", 498 | paste("RemoteRef: ", sha), 499 | paste("RemoteSha: ", sha) 500 | ) 501 | writeLines(c(desc_lines[desc_lines != ""], remotes_fields), con = desc_path) 502 | 503 | # Re-tar 504 | local({ 505 | old <- setwd(tempdir) 506 | on.exit(setwd(old), add = TRUE) 507 | 508 | tar(destfile, compression = "gzip") 509 | }) 510 | invisible() 511 | } 512 | 513 | # Extract the commit hash from a git archive. Git archives include the SHA1 514 | # hash as the comment field of the tarball pax extended header 515 | # (see https://www.kernel.org/pub/software/scm/git/docs/git-archive.html) 516 | # For GitHub archives this should be the first header after the default one 517 | # (512 byte) header. 518 | renv_bootstrap_git_extract_sha1_tar <- function(bundle) { 519 | 520 | # open the bundle for reading 521 | # We use gzcon for everything because (from ?gzcon) 522 | # > Reading from a connection which does not supply a 'gzip' magic 523 | # > header is equivalent to reading from the original connection 524 | conn <- gzcon(file(bundle, open = "rb", raw = TRUE)) 525 | on.exit(close(conn)) 526 | 527 | # The default pax header is 512 bytes long and the first pax extended header 528 | # with the comment should be 51 bytes long 529 | # `52 comment=` (11 chars) + 40 byte SHA1 hash 530 | len <- 0x200 + 0x33 531 | res <- rawToChar(readBin(conn, "raw", n = len)[0x201:len]) 532 | 533 | if (grepl("^52 comment=", res)) { 534 | sub("52 comment=", "", res) 535 | } else { 536 | NULL 537 | } 538 | } 539 | 540 | renv_bootstrap_install <- function(version, tarball, library) { 541 | 542 | # attempt to install it into project library 543 | dir.create(library, showWarnings = FALSE, recursive = TRUE) 544 | output <- renv_bootstrap_install_impl(library, tarball) 545 | 546 | # check for successful install 547 | status <- attr(output, "status") 548 | if (is.null(status) || identical(status, 0L)) 549 | return(status) 550 | 551 | # an error occurred; report it 552 | header <- "installation of renv failed" 553 | lines <- paste(rep.int("=", nchar(header)), collapse = "") 554 | text <- paste(c(header, lines, output), collapse = "\n") 555 | stop(text) 556 | 557 | } 558 | 559 | renv_bootstrap_install_impl <- function(library, tarball) { 560 | 561 | # invoke using system2 so we can capture and report output 562 | bin <- R.home("bin") 563 | exe <- if (Sys.info()[["sysname"]] == "Windows") "R.exe" else "R" 564 | R <- file.path(bin, exe) 565 | 566 | args <- c( 567 | "--vanilla", "CMD", "INSTALL", "--no-multiarch", 568 | "-l", shQuote(path.expand(library)), 569 | shQuote(path.expand(tarball)) 570 | ) 571 | 572 | system2(R, args, stdout = TRUE, stderr = TRUE) 573 | 574 | } 575 | 576 | renv_bootstrap_platform_prefix <- function() { 577 | 578 | # construct version prefix 579 | version <- paste(R.version$major, R.version$minor, sep = ".") 580 | prefix <- paste("R", numeric_version(version)[1, 1:2], sep = "-") 581 | 582 | # include SVN revision for development versions of R 583 | # (to avoid sharing platform-specific artefacts with released versions of R) 584 | devel <- 585 | identical(R.version[["status"]], "Under development (unstable)") || 586 | identical(R.version[["nickname"]], "Unsuffered Consequences") 587 | 588 | if (devel) 589 | prefix <- paste(prefix, R.version[["svn rev"]], sep = "-r") 590 | 591 | # build list of path components 592 | components <- c(prefix, R.version$platform) 593 | 594 | # include prefix if provided by user 595 | prefix <- renv_bootstrap_platform_prefix_impl() 596 | if (!is.na(prefix) && nzchar(prefix)) 597 | components <- c(prefix, components) 598 | 599 | # build prefix 600 | paste(components, collapse = "/") 601 | 602 | } 603 | 604 | renv_bootstrap_platform_prefix_impl <- function() { 605 | 606 | # if an explicit prefix has been supplied, use it 607 | prefix <- Sys.getenv("RENV_PATHS_PREFIX", unset = NA) 608 | if (!is.na(prefix)) 609 | return(prefix) 610 | 611 | # if the user has requested an automatic prefix, generate it 612 | auto <- Sys.getenv("RENV_PATHS_PREFIX_AUTO", unset = NA) 613 | if (auto %in% c("TRUE", "True", "true", "1")) 614 | return(renv_bootstrap_platform_prefix_auto()) 615 | 616 | # empty string on failure 617 | "" 618 | 619 | } 620 | 621 | renv_bootstrap_platform_prefix_auto <- function() { 622 | 623 | prefix <- tryCatch(renv_bootstrap_platform_os(), error = identity) 624 | if (inherits(prefix, "error") || prefix %in% "unknown") { 625 | 626 | msg <- paste( 627 | "failed to infer current operating system", 628 | "please file a bug report at https://github.com/rstudio/renv/issues", 629 | sep = "; " 630 | ) 631 | 632 | warning(msg) 633 | 634 | } 635 | 636 | prefix 637 | 638 | } 639 | 640 | renv_bootstrap_platform_os <- function() { 641 | 642 | sysinfo <- Sys.info() 643 | sysname <- sysinfo[["sysname"]] 644 | 645 | # handle Windows + macOS up front 646 | if (sysname == "Windows") 647 | return("windows") 648 | else if (sysname == "Darwin") 649 | return("macos") 650 | 651 | # check for os-release files 652 | for (file in c("/etc/os-release", "/usr/lib/os-release")) 653 | if (file.exists(file)) 654 | return(renv_bootstrap_platform_os_via_os_release(file, sysinfo)) 655 | 656 | # check for redhat-release files 657 | if (file.exists("/etc/redhat-release")) 658 | return(renv_bootstrap_platform_os_via_redhat_release()) 659 | 660 | "unknown" 661 | 662 | } 663 | 664 | renv_bootstrap_platform_os_via_os_release <- function(file, sysinfo) { 665 | 666 | # read /etc/os-release 667 | release <- utils::read.table( 668 | file = file, 669 | sep = "=", 670 | quote = c("\"", "'"), 671 | col.names = c("Key", "Value"), 672 | comment.char = "#", 673 | stringsAsFactors = FALSE 674 | ) 675 | 676 | vars <- as.list(release$Value) 677 | names(vars) <- release$Key 678 | 679 | # get os name 680 | os <- tolower(sysinfo[["sysname"]]) 681 | 682 | # read id 683 | id <- "unknown" 684 | for (field in c("ID", "ID_LIKE")) { 685 | if (field %in% names(vars) && nzchar(vars[[field]])) { 686 | id <- vars[[field]] 687 | break 688 | } 689 | } 690 | 691 | # read version 692 | version <- "unknown" 693 | for (field in c("UBUNTU_CODENAME", "VERSION_CODENAME", "VERSION_ID", "BUILD_ID")) { 694 | if (field %in% names(vars) && nzchar(vars[[field]])) { 695 | version <- vars[[field]] 696 | break 697 | } 698 | } 699 | 700 | # join together 701 | paste(c(os, id, version), collapse = "-") 702 | 703 | } 704 | 705 | renv_bootstrap_platform_os_via_redhat_release <- function() { 706 | 707 | # read /etc/redhat-release 708 | contents <- readLines("/etc/redhat-release", warn = FALSE) 709 | 710 | # infer id 711 | id <- if (grepl("centos", contents, ignore.case = TRUE)) 712 | "centos" 713 | else if (grepl("redhat", contents, ignore.case = TRUE)) 714 | "redhat" 715 | else 716 | "unknown" 717 | 718 | # try to find a version component (very hacky) 719 | version <- "unknown" 720 | 721 | parts <- strsplit(contents, "[[:space:]]")[[1L]] 722 | for (part in parts) { 723 | 724 | nv <- tryCatch(numeric_version(part), error = identity) 725 | if (inherits(nv, "error")) 726 | next 727 | 728 | version <- nv[1, 1] 729 | break 730 | 731 | } 732 | 733 | paste(c("linux", id, version), collapse = "-") 734 | 735 | } 736 | 737 | renv_bootstrap_library_root_name <- function(project) { 738 | 739 | # use project name as-is if requested 740 | asis <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT_ASIS", unset = "FALSE") 741 | if (asis) 742 | return(basename(project)) 743 | 744 | # otherwise, disambiguate based on project's path 745 | id <- substring(renv_bootstrap_hash_text(project), 1L, 8L) 746 | paste(basename(project), id, sep = "-") 747 | 748 | } 749 | 750 | renv_bootstrap_library_root <- function(project) { 751 | 752 | prefix <- renv_bootstrap_profile_prefix() 753 | 754 | path <- Sys.getenv("RENV_PATHS_LIBRARY", unset = NA) 755 | if (!is.na(path)) 756 | return(paste(c(path, prefix), collapse = "/")) 757 | 758 | path <- renv_bootstrap_library_root_impl(project) 759 | if (!is.null(path)) { 760 | name <- renv_bootstrap_library_root_name(project) 761 | return(paste(c(path, prefix, name), collapse = "/")) 762 | } 763 | 764 | renv_bootstrap_paths_renv("library", project = project) 765 | 766 | } 767 | 768 | renv_bootstrap_library_root_impl <- function(project) { 769 | 770 | root <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT", unset = NA) 771 | if (!is.na(root)) 772 | return(root) 773 | 774 | type <- renv_bootstrap_project_type(project) 775 | if (identical(type, "package")) { 776 | userdir <- renv_bootstrap_user_dir() 777 | return(file.path(userdir, "library")) 778 | } 779 | 780 | } 781 | 782 | renv_bootstrap_validate_version <- function(version, description = NULL) { 783 | 784 | # resolve description file 785 | # 786 | # avoid passing lib.loc to `packageDescription()` below, since R will 787 | # use the loaded version of the package by default anyhow. note that 788 | # this function should only be called after 'renv' is loaded 789 | # https://github.com/rstudio/renv/issues/1625 790 | description <- description %||% packageDescription("renv") 791 | 792 | # check whether requested version 'version' matches loaded version of renv 793 | sha <- attr(version, "sha", exact = TRUE) 794 | valid <- if (!is.null(sha)) 795 | renv_bootstrap_validate_version_dev(sha, description) 796 | else 797 | renv_bootstrap_validate_version_release(version, description) 798 | 799 | if (valid) 800 | return(TRUE) 801 | 802 | # the loaded version of renv doesn't match the requested version; 803 | # give the user instructions on how to proceed 804 | remote <- if (!is.null(description[["RemoteSha"]])) { 805 | paste("rstudio/renv", description[["RemoteSha"]], sep = "@") 806 | } else { 807 | paste("renv", description[["Version"]], sep = "@") 808 | } 809 | 810 | # display both loaded version + sha if available 811 | friendly <- renv_bootstrap_version_friendly( 812 | version = description[["Version"]], 813 | sha = description[["RemoteSha"]] 814 | ) 815 | 816 | fmt <- paste( 817 | "renv %1$s was loaded from project library, but this project is configured to use renv %2$s.", 818 | "- Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.", 819 | "- Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.", 820 | sep = "\n" 821 | ) 822 | catf(fmt, friendly, renv_bootstrap_version_friendly(version), remote) 823 | 824 | FALSE 825 | 826 | } 827 | 828 | renv_bootstrap_validate_version_dev <- function(version, description) { 829 | expected <- description[["RemoteSha"]] 830 | is.character(expected) && startswith(expected, version) 831 | } 832 | 833 | renv_bootstrap_validate_version_release <- function(version, description) { 834 | expected <- description[["Version"]] 835 | is.character(expected) && identical(expected, version) 836 | } 837 | 838 | renv_bootstrap_hash_text <- function(text) { 839 | 840 | hashfile <- tempfile("renv-hash-") 841 | on.exit(unlink(hashfile), add = TRUE) 842 | 843 | writeLines(text, con = hashfile) 844 | tools::md5sum(hashfile) 845 | 846 | } 847 | 848 | renv_bootstrap_load <- function(project, libpath, version) { 849 | 850 | # try to load renv from the project library 851 | if (!requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) 852 | return(FALSE) 853 | 854 | # warn if the version of renv loaded does not match 855 | renv_bootstrap_validate_version(version) 856 | 857 | # execute renv load hooks, if any 858 | hooks <- getHook("renv::autoload") 859 | for (hook in hooks) 860 | if (is.function(hook)) 861 | tryCatch(hook(), error = warnify) 862 | 863 | # load the project 864 | renv::load(project) 865 | 866 | TRUE 867 | 868 | } 869 | 870 | renv_bootstrap_profile_load <- function(project) { 871 | 872 | # if RENV_PROFILE is already set, just use that 873 | profile <- Sys.getenv("RENV_PROFILE", unset = NA) 874 | if (!is.na(profile) && nzchar(profile)) 875 | return(profile) 876 | 877 | # check for a profile file (nothing to do if it doesn't exist) 878 | path <- renv_bootstrap_paths_renv("profile", profile = FALSE, project = project) 879 | if (!file.exists(path)) 880 | return(NULL) 881 | 882 | # read the profile, and set it if it exists 883 | contents <- readLines(path, warn = FALSE) 884 | if (length(contents) == 0L) 885 | return(NULL) 886 | 887 | # set RENV_PROFILE 888 | profile <- contents[[1L]] 889 | if (!profile %in% c("", "default")) 890 | Sys.setenv(RENV_PROFILE = profile) 891 | 892 | profile 893 | 894 | } 895 | 896 | renv_bootstrap_profile_prefix <- function() { 897 | profile <- renv_bootstrap_profile_get() 898 | if (!is.null(profile)) 899 | return(file.path("profiles", profile, "renv")) 900 | } 901 | 902 | renv_bootstrap_profile_get <- function() { 903 | profile <- Sys.getenv("RENV_PROFILE", unset = "") 904 | renv_bootstrap_profile_normalize(profile) 905 | } 906 | 907 | renv_bootstrap_profile_set <- function(profile) { 908 | profile <- renv_bootstrap_profile_normalize(profile) 909 | if (is.null(profile)) 910 | Sys.unsetenv("RENV_PROFILE") 911 | else 912 | Sys.setenv(RENV_PROFILE = profile) 913 | } 914 | 915 | renv_bootstrap_profile_normalize <- function(profile) { 916 | 917 | if (is.null(profile) || profile %in% c("", "default")) 918 | return(NULL) 919 | 920 | profile 921 | 922 | } 923 | 924 | renv_bootstrap_path_absolute <- function(path) { 925 | 926 | substr(path, 1L, 1L) %in% c("~", "/", "\\") || ( 927 | substr(path, 1L, 1L) %in% c(letters, LETTERS) && 928 | substr(path, 2L, 3L) %in% c(":/", ":\\") 929 | ) 930 | 931 | } 932 | 933 | renv_bootstrap_paths_renv <- function(..., profile = TRUE, project = NULL) { 934 | renv <- Sys.getenv("RENV_PATHS_RENV", unset = "renv") 935 | root <- if (renv_bootstrap_path_absolute(renv)) NULL else project 936 | prefix <- if (profile) renv_bootstrap_profile_prefix() 937 | components <- c(root, renv, prefix, ...) 938 | paste(components, collapse = "/") 939 | } 940 | 941 | renv_bootstrap_project_type <- function(path) { 942 | 943 | descpath <- file.path(path, "DESCRIPTION") 944 | if (!file.exists(descpath)) 945 | return("unknown") 946 | 947 | desc <- tryCatch( 948 | read.dcf(descpath, all = TRUE), 949 | error = identity 950 | ) 951 | 952 | if (inherits(desc, "error")) 953 | return("unknown") 954 | 955 | type <- desc$Type 956 | if (!is.null(type)) 957 | return(tolower(type)) 958 | 959 | package <- desc$Package 960 | if (!is.null(package)) 961 | return("package") 962 | 963 | "unknown" 964 | 965 | } 966 | 967 | renv_bootstrap_user_dir <- function() { 968 | dir <- renv_bootstrap_user_dir_impl() 969 | path.expand(chartr("\\", "/", dir)) 970 | } 971 | 972 | renv_bootstrap_user_dir_impl <- function() { 973 | 974 | # use local override if set 975 | override <- getOption("renv.userdir.override") 976 | if (!is.null(override)) 977 | return(override) 978 | 979 | # use R_user_dir if available 980 | tools <- asNamespace("tools") 981 | if (is.function(tools$R_user_dir)) 982 | return(tools$R_user_dir("renv", "cache")) 983 | 984 | # try using our own backfill for older versions of R 985 | envvars <- c("R_USER_CACHE_DIR", "XDG_CACHE_HOME") 986 | for (envvar in envvars) { 987 | root <- Sys.getenv(envvar, unset = NA) 988 | if (!is.na(root)) 989 | return(file.path(root, "R/renv")) 990 | } 991 | 992 | # use platform-specific default fallbacks 993 | if (Sys.info()[["sysname"]] == "Windows") 994 | file.path(Sys.getenv("LOCALAPPDATA"), "R/cache/R/renv") 995 | else if (Sys.info()[["sysname"]] == "Darwin") 996 | "~/Library/Caches/org.R-project.R/R/renv" 997 | else 998 | "~/.cache/R/renv" 999 | 1000 | } 1001 | 1002 | renv_bootstrap_version_friendly <- function(version, shafmt = NULL, sha = NULL) { 1003 | sha <- sha %||% attr(version, "sha", exact = TRUE) 1004 | parts <- c(version, sprintf(shafmt %||% " [sha: %s]", substring(sha, 1L, 7L))) 1005 | paste(parts, collapse = "") 1006 | } 1007 | 1008 | renv_bootstrap_exec <- function(project, libpath, version) { 1009 | if (!renv_bootstrap_load(project, libpath, version)) 1010 | renv_bootstrap_run(version, libpath) 1011 | } 1012 | 1013 | renv_bootstrap_run <- function(version, libpath) { 1014 | 1015 | # perform bootstrap 1016 | bootstrap(version, libpath) 1017 | 1018 | # exit early if we're just testing bootstrap 1019 | if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA))) 1020 | return(TRUE) 1021 | 1022 | # try again to load 1023 | if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) { 1024 | return(renv::load(project = getwd())) 1025 | } 1026 | 1027 | # failed to download or load renv; warn the user 1028 | msg <- c( 1029 | "Failed to find an renv installation: the project will not be loaded.", 1030 | "Use `renv::activate()` to re-initialize the project." 1031 | ) 1032 | 1033 | warning(paste(msg, collapse = "\n"), call. = FALSE) 1034 | 1035 | } 1036 | 1037 | 1038 | renv_bootstrap_in_rstudio <- function() { 1039 | commandArgs()[[1]] == "RStudio" 1040 | } 1041 | 1042 | # Used to work around buglet in RStudio if hook uses readline 1043 | renv_bootstrap_flush_console <- function() { 1044 | tryCatch({ 1045 | tools <- as.environment("tools:rstudio") 1046 | tools$.rs.api.sendToConsole("", echo = FALSE, focus = FALSE) 1047 | }, error = function(cnd) {}) 1048 | } 1049 | 1050 | renv_json_read <- function(file = NULL, text = NULL) { 1051 | 1052 | jlerr <- NULL 1053 | 1054 | # if jsonlite is loaded, use that instead 1055 | if ("jsonlite" %in% loadedNamespaces()) { 1056 | 1057 | json <- catch(renv_json_read_jsonlite(file, text)) 1058 | if (!inherits(json, "error")) 1059 | return(json) 1060 | 1061 | jlerr <- json 1062 | 1063 | } 1064 | 1065 | # otherwise, fall back to the default JSON reader 1066 | json <- catch(renv_json_read_default(file, text)) 1067 | if (!inherits(json, "error")) 1068 | return(json) 1069 | 1070 | # report an error 1071 | if (!is.null(jlerr)) 1072 | stop(jlerr) 1073 | else 1074 | stop(json) 1075 | 1076 | } 1077 | 1078 | renv_json_read_jsonlite <- function(file = NULL, text = NULL) { 1079 | text <- paste(text %||% read(file), collapse = "\n") 1080 | jsonlite::fromJSON(txt = text, simplifyVector = FALSE) 1081 | } 1082 | 1083 | renv_json_read_default <- function(file = NULL, text = NULL) { 1084 | 1085 | # find strings in the JSON 1086 | text <- paste(text %||% read(file), collapse = "\n") 1087 | pattern <- '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]' 1088 | locs <- gregexpr(pattern, text, perl = TRUE)[[1]] 1089 | 1090 | # if any are found, replace them with placeholders 1091 | replaced <- text 1092 | strings <- character() 1093 | replacements <- character() 1094 | 1095 | if (!identical(c(locs), -1L)) { 1096 | 1097 | # get the string values 1098 | starts <- locs 1099 | ends <- locs + attr(locs, "match.length") - 1L 1100 | strings <- substring(text, starts, ends) 1101 | 1102 | # only keep those requiring escaping 1103 | strings <- grep("[[\\]{}:]", strings, perl = TRUE, value = TRUE) 1104 | 1105 | # compute replacements 1106 | replacements <- sprintf('"\032%i\032"', seq_along(strings)) 1107 | 1108 | # replace the strings 1109 | mapply(function(string, replacement) { 1110 | replaced <<- sub(string, replacement, replaced, fixed = TRUE) 1111 | }, strings, replacements) 1112 | 1113 | } 1114 | 1115 | # transform the JSON into something the R parser understands 1116 | transformed <- replaced 1117 | transformed <- gsub("{}", "`names<-`(list(), character())", transformed, fixed = TRUE) 1118 | transformed <- gsub("[[{]", "list(", transformed, perl = TRUE) 1119 | transformed <- gsub("[]}]", ")", transformed, perl = TRUE) 1120 | transformed <- gsub(":", "=", transformed, fixed = TRUE) 1121 | text <- paste(transformed, collapse = "\n") 1122 | 1123 | # parse it 1124 | json <- parse(text = text, keep.source = FALSE, srcfile = NULL)[[1L]] 1125 | 1126 | # construct map between source strings, replaced strings 1127 | map <- as.character(parse(text = strings)) 1128 | names(map) <- as.character(parse(text = replacements)) 1129 | 1130 | # convert to list 1131 | map <- as.list(map) 1132 | 1133 | # remap strings in object 1134 | remapped <- renv_json_remap(json, map) 1135 | 1136 | # evaluate 1137 | eval(remapped, envir = baseenv()) 1138 | 1139 | } 1140 | 1141 | renv_json_remap <- function(json, map) { 1142 | 1143 | # fix names 1144 | if (!is.null(names(json))) { 1145 | lhs <- match(names(json), names(map), nomatch = 0L) 1146 | rhs <- match(names(map), names(json), nomatch = 0L) 1147 | names(json)[rhs] <- map[lhs] 1148 | } 1149 | 1150 | # fix values 1151 | if (is.character(json)) 1152 | return(map[[json]] %||% json) 1153 | 1154 | # handle true, false, null 1155 | if (is.name(json)) { 1156 | text <- as.character(json) 1157 | if (text == "true") 1158 | return(TRUE) 1159 | else if (text == "false") 1160 | return(FALSE) 1161 | else if (text == "null") 1162 | return(NULL) 1163 | } 1164 | 1165 | # recurse 1166 | if (is.recursive(json)) { 1167 | for (i in seq_along(json)) { 1168 | json[i] <- list(renv_json_remap(json[[i]], map)) 1169 | } 1170 | } 1171 | 1172 | json 1173 | 1174 | } 1175 | 1176 | # load the renv profile, if any 1177 | renv_bootstrap_profile_load(project) 1178 | 1179 | # construct path to library root 1180 | root <- renv_bootstrap_library_root(project) 1181 | 1182 | # construct library prefix for platform 1183 | prefix <- renv_bootstrap_platform_prefix() 1184 | 1185 | # construct full libpath 1186 | libpath <- file.path(root, prefix) 1187 | 1188 | if (renv_bootstrap_in_rstudio()) { 1189 | # RStudio only updates console once .Rprofile is finished, so 1190 | # instead run code on sessionInit 1191 | setHook("rstudio.sessionInit", function(...) { 1192 | renv_bootstrap_exec(project, libpath, version) 1193 | renv_bootstrap_flush_console() 1194 | }) 1195 | } else { 1196 | renv_bootstrap_exec(project, libpath, version) 1197 | } 1198 | 1199 | invisible() 1200 | 1201 | }) 1202 | -------------------------------------------------------------------------------- /scripts/.entry_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "title": {"type": "string"}, 6 | "url": {"type": "string"}, 7 | "rss_feed": {"type": "string"}, 8 | "type": {"type": "string"}, 9 | "photo_url": {"type": "string"}, 10 | "description": {"type": "string"}, 11 | "language": {"type": "string"}, 12 | "authors": { 13 | "type": "array", 14 | "items": [ 15 | { 16 | "type": "object", 17 | "properties": { 18 | "name": {"type": "string"}, 19 | "social_media": { 20 | "type": "array", 21 | "items": [ 22 | { 23 | "type": "object", 24 | "properties": { 25 | "twitter": {"type": "string"}, 26 | "mastodon": {"type": "string"}, 27 | "bluesky": {"type": "string"}, 28 | "linkedin": {"type": "string"}, 29 | "facebook": {"type": "string"}, 30 | "github": {"type": "string"}, 31 | "instagram": {"type": "string"}, 32 | "youtube": {"type": "string"}, 33 | "tiktok": {"type": "string"}, 34 | "periscope": {"type": "string"}, 35 | "researchgate": {"type": "string"}, 36 | "website": {"type": "string"}, 37 | "orcid": {"type": "string"}, 38 | "meetup": {"type": "string"} 39 | } 40 | } 41 | ] 42 | } 43 | }, 44 | "required": [ 45 | "name" 46 | ] 47 | } 48 | ] 49 | } 50 | }, 51 | "required": [ 52 | "title", 53 | "url", 54 | "type", 55 | "authors", 56 | "language" 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /scripts/check-images.R: -------------------------------------------------------------------------------- 1 | library(httr2) 2 | 3 | jfiles <- list.files(here::here("blogs"), "json$", full.names = TRUE) 4 | jcontent <- lapply(jfiles, jsonlite::read_json) 5 | names(jcontent) <- basename(jfiles) 6 | 7 | 8 | check_image <- function(x){ 9 | if("photo_url" %in% names(x)){ 10 | url_info <- request(x$photo_url) |> 11 | req_error(is_error = function(x){FALSE}) |> 12 | req_perform() 13 | 14 | exists <- resp_status(url_info) == 200 15 | if(!exists){ 16 | message(sprintf("%s: photo url returns http status '%s': %s", 17 | x$url, resp_status(url_info), x$photo_url)) 18 | return(FALSE) 19 | } 20 | 21 | is_image <- grepl("image", resp_content_type(url_info)) 22 | if(!is_image){ 23 | message(sprintf("%s: photo url does not return an image: %s", 24 | x$url, x$photo_url)) 25 | return(FALSE) 26 | } 27 | 28 | return(all(exists, is_image)) 29 | } 30 | } 31 | 32 | images <- sapply(jcontent, check_image, USE.NAMES = TRUE) |> 33 | unlist() 34 | 35 | missing_images <- images[!images] 36 | 37 | if(any(!missing_images)){ 38 | stop("There are images in the data that don't return correctly. See build log for more details.", call. = FALSE) 39 | } 40 | -------------------------------------------------------------------------------- /scripts/validate_jsons.R: -------------------------------------------------------------------------------- 1 | validate_jsons <- function(files, schema){ 2 | validate <- jsonvalidate::json_validator( 3 | schema) 4 | k <- sapply(files, validate, 5 | verbose = TRUE, 6 | error = TRUE, 7 | greedy = TRUE) 8 | } 9 | 10 | files <- list.files( 11 | path = here::here("blogs"), 12 | full.names = TRUE 13 | ) 14 | ext <- grep("json$", files, invert = TRUE) 15 | if(length(ext) > 0) 16 | stop("File has wrong extention. Please rename to end with 'json'\n", 17 | paste0(basename(files[grep("json$", files, invert = TRUE)]), collapse ="\n"), 18 | call. = FALSE) 19 | 20 | # Validate blog json 21 | validate_jsons( 22 | files, 23 | here::here("scripts/.entry_schema.json") 24 | ) 25 | 26 | 27 | --------------------------------------------------------------------------------