diff --git a/README.md b/README.md index 6c463f6f5..fdb7c705a 100644 --- a/README.md +++ b/README.md @@ -72,4 +72,11 @@ To download all new posts and their updates from dev.to need to run ```bash bin/from_devto -f -``` \ No newline at end of file +``` + +## How to set custom urls for posts + +1. Open `devto_urls_mapping.csv` +2. Change the URLs that you want to update (you should not change the first number in the line, this is the foreign key for the article on dev.to ) +3. Save `devto_urls_mapping.csv` +4. Run `bin/from_devto -f` diff --git a/bin/from_devto b/bin/from_devto index 6fcf5025d..4ede62137 100755 --- a/bin/from_devto +++ b/bin/from_devto @@ -5,9 +5,12 @@ class SaveArticleToMarkdown require 'time' require 'net/http' require 'json' + require 'csv' + require 'yaml' DEV_TO_API_HOST = 'https://dev.to/api/articles/jetthoughts/'.freeze JT_BLOG_HOST = 'https://jetthoughts.github.io/blog/'.freeze + USELESS_WORDS = ['and', 'the', 'a', 'but', 'to', 'is', 'so'].freeze def initialize(article_slug) @article_slug = article_slug @@ -19,11 +22,20 @@ class SaveArticleToMarkdown def call data = fetch_article_data + set_custom_filename(data) save_to_md_file(data) update_canonical_url_on_dev_to(data) fill_seo_attributes(data) end + def set_custom_filename(data) + file = find_file_with_id(data['id']) + + return if file.nil? + + File.rename(file, 'content/blog/' + slug(data) + File.extname(file)) + end + def fetch_article_data uri = URI(DEV_TO_API_HOST + @article_slug) response = Net::HTTP.get(uri) @@ -31,16 +43,31 @@ class SaveArticleToMarkdown end def save_to_md_file(data) - File.open(stored_post_path_for(data), 'w') { |file| file.write(data['body_markdown']) } + local_article = find_file_with_id(data['id']) + File.open(local_article || stored_post_path_for(data), 'w') { |file| file.write(data['body_markdown']) } puts "File #{slug(data)}.md successfully created." end def stored_post_path_for(data) - __dir__ + "/../content/blog/#{slug(data)}.md" + __dir__ + "/../content/blog/" + slug(data) + ".md" end def slug(data) - Time.parse(data['created_at']).utc.strftime('%y%m%d') + '-' + data['slug'].split('-')[0..-2].join('-') + csv_data = read_csv + row = csv_data.find { |r| r[0] == data['id'].to_s } + + if row + row[1] + else + generated_slug = [data['slug'].split('-')[0..-2], data['tags'][0], data['tags'][1]] + .flatten + .uniq + .filter { |segment| !USELESS_WORDS.include?(segment) } + .compact + .join('-') + write_csv(data['id'], generated_slug) + generated_slug + end end def update_canonical_url_on_dev_to(data) @@ -62,20 +89,39 @@ class SaveArticleToMarkdown markdown = File.read(stored_post_path_for(data)) File.open(stored_post_path_for(data), 'w') do |file| - file.write("+++\n") - file.write("title = #{data['title'].to_json}\n") - file.write("description = #{data['description'].to_json}\n") - file.write("created_at = \"#{data['created_at']}\"\n") - file.write("edited_at = \"#{data['edited_at']}\"\n") - file.write("sync_date = \"#{Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')}\"\n") - file.write("draft = false\n") - file.write("tags = #{data['tags']}\n") - file.write("canonical_url = \"#{data['canonical_url']}\"\n") - file.write("slug = \"#{slug(data)}\"\n") - file.write("+++\n") + file.write("---\n") + file.write("id: #{data['id'].to_json}\n") + file.write("title: #{data['title'].to_json}\n") + file.write("description: #{data['description'].to_json}\n") + file.write("created_at: \"#{data['created_at']}\"\n") + file.write("edited_at: \"#{data['edited_at']}\"\n") + file.write("sync_date: \"#{Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')}\"\n") + file.write("draft: false\n") + file.write("tags: #{data['tags']}\n") + file.write("canonical_url: \"#{data['canonical_url']}\"\n") + file.write("slug: \"#{slug(data)}\"\n") + file.write("---\n") file.write(markdown) end end + + def read_csv + CSV.read(File.expand_path('../devto_urls_mapping.csv', __dir__), headers: false) + end + + def write_csv(id, slug) + CSV.open(File.expand_path('../devto_urls_mapping.csv', __dir__), 'a+') do |csv| + csv << [id, slug] + end + end + + def find_file_with_id(id) + Dir.glob('content/blog/*.md').each do |filename| + data = YAML.load_file(filename) + return filename if data['id'] == id + end + nil + end end page = 1 @@ -106,4 +152,4 @@ loop do page += 1 end -pp "TODO: Need to uncomment canonical url update when blog will be live." +puts 'TODO: Need to uncomment canonical url update when blog will be live.' diff --git a/devto_urls_mapping.csv b/devto_urls_mapping.csv new file mode 100644 index 000000000..113a85506 --- /dev/null +++ b/devto_urls_mapping.csv @@ -0,0 +1,119 @@ +1853650,what-are-next-steps-when-your-project-failing-management +1853649,cheap-tests-with-ghost-inspector-webdev-legacy +1853643,effective-project-onboarding-checklist-management-productivity +1853641,who-blame-when-project-fails-management +1853640,tools-provide-effective-feedback-for-distributed-development-teams-productivity-startup +1853635,running-tests-in-containers-with-docker-compose-coding-tutorial +1853624,cross-platform-development-using-reactxp-react-javascript +1853621,how-have-clean-css-structure-with-rscssitcss-rscss +1853617,things-that-remote-teams-expect-from-product-owner-startup +1853611,cons-of-private-chats-for-team-collaboration-communication-process +1853610,migrate-from-sidekiq-sidekiqcr-in-rails-application-tdd-testing +1853605,git-minimum-for-effective-project-development +1853596,typical-day-at-jetthoughts-agile-remote +1853575,how-know-what-your-team-doing-remote-startup +1853571,feature-branches-where-find-them-development-coding +1853556,simplest-step-by-guide-creating-post-blogging-writing +1853553,load-web-page-in-less-than-one-second-optimization-html +1853550,how-write-right-content-for-your-article-blogging-posting +1853549,tips-for-writing-readable-system-tests-in-rails-capybara-ruby +1853545,how-handle-remote-services-in-tests-rails-tutorial +1853539,why-communication-important-when-you-work-remotely-remote +1853537,tips-hire-great-people-startup-hiring +1853531,8-step-sales-process-in-5-min-productivity +1853524,how-does-onboarding-look-like-in-jetthoughts-productivity-startup +1853478,why-it-hard-find-great-developer-on-upwork-other-freelance-platforms-senior +1853475,react-native-testing-options-overview +1853472,services-tools-automatize-development-for-remote-teams-workflow-automation +1853469,jetthoughts-receives-first-review-on-clutchco-thank-you-startup +1853466,speed-up-github-prs-review-of-your-react-applications-development-tutorial +1853461,deploying-jekyll-github-pages-with-circleci-20 +1852952,what-are-steps-of-an-automation-test-plan-qualitycontrol-testing +1852950,enum-validation-in-ruby-on-rails-71 +1852940,pitfalls-of-using-metaprogramming-in-ruby-on-rails-application +1852752,regular-automatic-dependencies-update-with-circleci-github-circle +1852749,design-rails-json-api-with-performance-in-mind-cache +1852747,practical-guide-writing-introductions-introduction +1852745,trial-period-for-staff-augmentation-in-jetthoughts-startup-engagement +1852741,how-avoid-n1-query-using-sql-views-materialized-in-rails-application-ruby +1852739,rscss-styling-css-without-losing-your-sanity +1852738,rails-virtual-attributes-use-cases-ruby +1852733,4-tricks-write-catchy-headlines-contentwriting +1852719,5-free-tools-make-sales-process-easier-leadgeneration +1852712,what-are-most-common-misconceptions-about-remote-work-misconception +1852642,deploying-subdirectory-projects-heroku-git +1852632,simple-lead-generation-tactics-startup-sails +1852630,cleaning-routines-keep-your-project-without-bugs-agile +1852624,which-platforms-are-better-use-for-non-tech-founders-look-contractors-freelance-development +1852622,how-create-technical-post-in-short-time-writing-blogging +1852621,ultimate-guide-sales-onboarding-in-it-companies-sails-leadgeneration +1852618,how-start-an-open-source-project-building-reso-api-js-client-javascript-opensource +1852617,prepare-pull-request-before-asking-review-git-pullrequest +1852612,where-read-env-variables-in-ruby-on-rails-application +1852611,jetthoughts-recognized-by-techreviewer-as-top-web-development-company-in-2020-webdev +1852603,delivery-flow-for-distributed-remote-teams-agile-kanban +1852581,what-do-for-developer-when-wip-limit-reached-agile-kanban +1852579,we-got-3-main-principles-for-writing-technical-blog-posts-heres-what-learned +1852577,benefits-of-working-remotely-remote +1852568,collecting-javascript-code-coverage-with-capybara-in-ruby-on-rails-application-testing +1852564,automated-delivery-react-vue-app-for-each-pull-request-ci +1852562,tips-attract-readers-read-your-post-blogging +1852561,what-activities-are-expected-from-remote-developer-for-effective-collaboration-development-process +1851499,new-asserts-for-testing-stopped-streams-after-ruby-on-rails-71-changelog +1846033,migration-from-medium-devto-hugo-blog-webdev +1783325,setting-up-docker-for-ruby-on-rails-7-beginners +1774708,more-control-over-enum-in-rails-71-webdev +1760994,upgrading-postgresql-on-heroku-step-by-guide-database +1740002,install-official-firefox-deb-in-dockerfile-docker-devops +1739418,anonymous-block-argument-in-ruby-tutorial +1698810,stimulus-keyboard-event-filter +1689481,integrating-bun-with-vite-ruby-for-lightning-fast-frontend-builds-startup-productivity +1677293,custom-ordering-without-sql-with-ruby-on-rails-7 +1673284,how-we-configure-simplecov-for-our-ruby-on-rails-projects +1667161,integrating-bun-with-vite-ruby-for-lightning-fast-frontend-builds-rails-javascript +1561382,improving-ruby-on-rails-test-suite-performance-by-disabling-animations-programming-tutorial +1560068,optimize-your-chrome-options-for-testing-get-x125-impact-performance +1559624,ruby-on-rails-views-resources-for-frontend-developer-programming-javascript +1556678,align-remote-teams-with-okrs-impact-mapping-management-devops +1454204,how-large-transaction-can-be-source-of-db-deadlocks-this-fixed-ruby-database +1375276,how-keep-clean-ruby-on-rails-views-with-null-object-pattern +1297134,how-wip-limits-improves-effectiveness-productivity-management +1283284,when-use-microservices-devops-distributedsystems +1277349,what-ruby-on-rails-middleware +1222970,how-create-triangles-in-tailwindcss-html-css +1215921,troubleshooting-ruby-build-tutorial +1212480,custom-templates-for-rails-scaffolding-ruby +1205578,our-mvp-team-structure-startup-management +1205643,tldr-move-cicd-scripts-into-automation-devops-productivity +1188292,how-make-vertically-scrollable-in-css-html +1200329,auto-install-system-dependencies-for-ruby-on-rails-programming +1198201,how-avoid-callbacks-using-services-rails-refactoring +1189342,incremental-lint-fixes-by-github-actions-devops +1182433,data-migrations-with-rails-ruby +1170570,how-use-ruby-on-rails-concerns-webdev +1168677,how-create-circles-in-css-html +1156418,how-make-truncate-text-in-css-html +1148536,how-create-triangles-in-css-html +1144505,how-setup-default-values-for-attributes-in-ruby-on-rails-programming +1140235,what-difference-between-joins-includes-in-rails-activerecord-ruby +1125937,how-use-linear-gradient-in-css-html +1116924,how-use-background-size-in-css-html +1115250,pitfalls-of-using-metaprogramming-in-ruby-on-rails-application-programming +1113061,how-use-nth-child-in-css-html +1108673,preview-ui-changes-with-ruby-on-rails-variants +1109046,how-style-checkbox-using-css-html +1103639,generating-random-strings-with-ruby-webdev +1103884,how-vertically-center-an-element-without-flex-css-html +1101579,how-horizontally-center-an-element-without-flex-css-html +1099690,change-inputs-placeholder-color-with-css-html +1093446,vertical-align-with-full-screen-across-tailwind-css-jetthoughts +1091289,manage-bundler-indirect-dependencies-versions-ruby-beginners +1089599,how-get-build-full-urls-in-rails-ruby +539418,why-how-use-tdd-main-tips-testing +470562,how-make-small-valuable-async-standups-productivity-development +462572,how-create-technical-post-in-short-time-writing-blogging +462569,trial-period-for-staff-augmentation-in-jetthoughts-startup-engagement +462554,who-blame-when-project-fails-blamegame +462546,what-are-next-steps-when-your-project-failing-management +399009,how-use-transaction-script-aka-service-objects-in-ruby-on-rails-simple-example +462581,communication-agreement-in-remote-environment-agile