{"id":336886,"date":"2022-08-11T09:00:22","date_gmt":"2022-08-11T09:00:22","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=336886"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=336886","title":{"rendered":"<span>\u041c\u043e\u0439 \u043e\u043f\u044b\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 SheetsAPI \u043e\u0442 Google<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0414\u043e\u0431\u0440\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u0443\u0442\u043e\u043a, \u0445\u0430\u0431\u0440\u043e\u043d\u0430\u0432\u0442\u044b. \u041f\u043e\u0434\u0435\u043b\u044e\u0441\u044c \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 Google Drive API, \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0435\u0435, \u0441 SheetsAPI &#8212; API \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446.<\/p>\n<p><strong>\u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0441\u043b\u043e\u0432\u0430<\/strong>: <em>Google Sheets API, Google Drive API, Python, PostgreSQL, apscheduler, googleapiclient, oauth2client, psycopg2<\/em>.<\/p>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043a\u0435\u0439\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f  Google Sheets API \u0438 Google Drive API. \u0417\u0430\u0434\u0430\u0447\u0430 \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0438  \u043a\u043e\u0434\u0430 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043e \u043d\u0435 \u043c\u0430\u043b\u043e (\u0435\u0433\u043e \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 <a href=\"https:\/\/gitlab.com\/Denactive\/test-task-numbers\" rel=\"noopener noreferrer nofollow\">\u0432 \u044d\u0442\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 GitLab<\/a>).<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0412\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 Google \u0438\u043b\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u044f Google-\u0442\u0430\u0431\u043b\u0438\u0446, \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c <a href=\"https:\/\/habr.com\/ru\/post\/483302\/\" rel=\"noopener noreferrer nofollow\">\u043a \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a>;<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0412\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0430 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u043d\u0430 Google-\u0434\u0438\u0441\u043a\u0435, \u043f\u0440\u043e\u043c\u0430\u0442\u044b\u0432\u0430\u0439\u0442\u0435 \u043d\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a &#171;<a href=\"#stage-2-3\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f\u044b 2-4. \u0423\u0437\u043d\u0430\u0435\u043c \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \u0444\u0430\u0439\u043b\u0435.<\/a>&#171;<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0412\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 SheetsAPI \u0432 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0441\u0435, \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0447\u0430\u0441\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e\u0434 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u043c &#171;<a href=\"#stage-2-2\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f\u044b 2-4. \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Sheets API.<\/a>&#171;;<\/p>\n<p>\u0422\u0435\u043c, \u043a\u0442\u043e \u0437\u0430\u0448\u0435\u043b \u0441\u044e\u0434\u0430, \u0436\u0435\u043b\u0430\u044f \u043f\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 PostreSQL \u0447\u0435\u0440\u0435\u0437 Python, \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0440\u0430\u0437\u0434\u0435\u043b &#171;<a href=\"#stage-1\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f 1. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a PostreSQL \u0438\u0437 Python.<\/a>&#171;.<\/p>\n<p>\u041f\u0440\u0438\u044f\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u044f!<\/p>\n<h3>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h3>\n<ul>\n<li>\n<p><a href=\"#about-google-api\" rel=\"noopener noreferrer nofollow\">\u041f\u0430\u0440\u0430 \u0441\u043b\u043e\u0432 \u043f\u0440\u043e Google API<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#task\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#db_creation\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0411\u0414<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#configs\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f. \u041a\u043e\u043d\u0444\u0438\u0433\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-1\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f 1. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a PostreSQL \u0438\u0437 Python<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-2-1\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f 2. \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0443\u0440\u0441\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-2-2\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f\u044b 2-4. \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Sheets API<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-2-3\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f\u044b 2-4. \u0423\u0437\u043d\u0430\u0435\u043c \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \u0444\u0430\u0439\u043b\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-5\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f 5. \u041a\u043e\u043d\u0435\u0446 \u043f\u0443\u0442\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#conclusion\" rel=\"noopener noreferrer nofollow\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u0435\u043c \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e Google Drive &#8212; \u0432\u0441\u0435\u043c\u0438\u0440\u043d\u043e \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442 \u0432\u0441\u0435\u043c\u0438\u0440\u043d\u043e \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e\u0439 Alphabet. \u041e\u043d\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u043e\u043c \u043e\u0444\u0438\u0441\u043d\u044b\u0445 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435, \u043a\u0430\u043a \u044f \u0441\u0447\u0438\u0442\u0430\u044e, \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c MS Office \u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0443\u0434\u043e\u0431\u043d\u044b \u0434\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0434 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0432 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0433\u0440\u0443\u043f\u043f\u0430\u0445. \u041b\u0438\u0447\u043d\u043e \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e Google Drive \u0434\u043b\u044f \u0431\u044d\u043a\u0430\u043f\u0430 \u043b\u0438\u0447\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432. \u0410 \u043a\u0430\u043a \u043d\u0430\u0441\u0447\u0435\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 &#8212; \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0437 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0445 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445? \u042d\u0442\u0430\u043f\u044b \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043c\u044b \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u0435\u043c \u0432\u043c\u0435\u0441\u0442\u0435 \u0432 \u044d\u0442\u043e\u0439 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<p><a class=\"anchor\" name=\"about-google-api\" id=\"about-google-api\"><\/a><\/p>\n<h3>\u041f\u0430\u0440\u0430 \u0441\u043b\u043e\u0432 \u043f\u0440\u043e Google API<\/h3>\n<p>\u041d\u0435 \u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0432 \u0441\u0442\u043e\u043b\u044c \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442, \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f-\u0442\u0430\u043a\u0438 \u043f\u043e\u0434\u0435\u043b\u0438\u043b\u0430\u0441\u044c \u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e API. \u0414\u0430\u0436\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0435 \u044f\u0437\u044b\u043a\u0438 \u0438 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043a\u043e\u0434\u0430 \u043d\u0430 Python, Java, NodeJS, Go. PHP, C#, Ruby. \u041d\u043e \u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0442\u0430\u043c \u043d\u0435 \u043c\u0430\u043b\u043e.<\/p>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435, \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043a\u043e\u0434\u0435 \u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442 \u043d\u0430 Python, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 API \u0434\u043b\u044f Google-\u0442\u0430\u0431\u043b\u0438\u0446 &#8212; <a href=\"https:\/\/developers.google.com\/sheets\/api\/reference\/rest\" rel=\"noopener noreferrer nofollow\">SheetsAPI<\/a> &#8212; \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0439, \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c, \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<p>\u0421\u043a\u0440\u0438\u043f\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 Google Drive \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0445 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u043e\u0432 Google &#8212; <a href=\"https:\/\/cloud.google.com\/iam\/docs\/service-accounts\" rel=\"noopener noreferrer nofollow\">Google Service Accounts<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043e\u0442 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u043d\u0430\u043c \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0415\u0441\u043b\u0438 \u043a\u0440\u0430\u0442\u043a\u043e, \u0442\u043e \u044d\u0442\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 Google Drive. \u0423 \u043d\u0435\u0435 \u0441\u0432\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c credentials (\u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445) &#8212; \u043d\u0430\u0431\u043e\u0440\u0443 \u043a\u043b\u044e\u0447\u0435\u0439 \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0438 \u0441\u0432\u043e\u0435 \u0434\u0438\u0441\u043a\u043e\u0432\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e. \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u043e \u0432\u0441\u0435\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c <a href=\"https:\/\/habr.com\/ru\/company\/vk\/blog\/115163\/\" rel=\"noopener noreferrer nofollow\">OAuth 2.0<\/a>.<\/p>\n<p>\u0421\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0435 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u044b &#8212; \u0443\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438 Google. \u041e\u043d\u0438 \u043d\u0443\u0436\u043d\u044b \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Google Drive \u0438 Google Sheets, \u043d\u043e \u0438 Google BigQuery, Google Cloud \u0438 \u043f\u0440\u043e\u0447\u0438\u043c\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c\u0438 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430. \u041f\u0440\u043e \u0441\u0445\u0435\u043c\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 \u043f\u0440\u043e\u0448\u0443 <a href=\"https:\/\/habr.com\/ru\/post\/483302\/\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u043e\u0447\u0435\u0441\u0442\u044c \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u0438\u043b\u044c\u043d\u043e \u043c\u043d\u0435 \u043f\u043e\u043c\u043e\u0433\u043b\u0430, \u0438 \u0430\u0432\u0442\u043e\u0440\u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u0435\u043d. <\/p>\n<p><a class=\"anchor\" name=\"task\" id=\"task\"><\/a><\/p>\n<h2>\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438<\/h2>\n<p>\u0423\u0442\u043e\u0447\u043d\u044e \u0446\u0435\u043b\u044c: \u0435\u0441\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043e \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0430\u0445:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/76e\/a58\/ebd\/76ea58ebdb2702dfd7d597b70bd0c3ac.png\" alt=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a 1. \u0412\u0438\u0434 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b.\" title=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a 1. \u0412\u0438\u0434 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b.\" width=\"409\" height=\"79\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/76e\/a58\/ebd\/76ea58ebdb2702dfd7d597b70bd0c3ac.png\"\/><figcaption>\u0420\u0438\u0441\u0443\u043d\u043e\u043a 1. \u0412\u0438\u0434 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b.<\/figcaption><\/figure>\n<p>\u0412 \u043d\u0435\u0439: \u043f\u043e\u0440\u044f\u0434\u043a\u043e\u0432\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 &#8212; \u0447\u0438\u0441\u043b\u043e, \u0438\u043d\u0434\u0435\u043a\u0441 \u0437\u0430\u043a\u0430\u0437\u0430 &#8212; \u0447\u0438\u0441\u043b\u043e, \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0432 \u0434\u043e\u043b\u043b\u0430\u0440\u0430\u0445 \u0421\u0428\u0410, \u0434\u0430\u0442\u0430 \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438 &#8212; \u0434\u0430\u0442\u0430 \u0432 <a href=\"https:\/\/tetran.ru\/SiteContent\/Details\/25\" rel=\"noopener noreferrer nofollow\">\u0440\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0434\u0430\u0442\u044b <\/a>(\u0432\u0441\u0435\u0433\u0434\u0430 \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0444\u043e\u0440\u043c\u0430\u0442\u044b \u0434\u0430\u0442\u044b \u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0442. \u043a. \u044d\u0442\u043e \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u0435\u0439 \u0441 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0438 \u0438\u043d\u0442\u0435\u0440\u043f\u0440\u0435\u0442\u0430\u0446\u0438\u0435\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0431\u0430\u0437\u0435). \u041d\u0443\u0436\u043d\u043e \u0432\u0437\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u0443\u0440\u0441\u0430 \u043f\u0430\u0440\u044b RUB-USD \u0441 <a href=\"https:\/\/www.cbr.ru\/development\/SXML\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0430\u0439\u0442\u0430 \u0426\u0411 \u0420\u0424<\/a> \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f, \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0441 \u043e\u0431\u043b\u0430\u043a\u0430 \u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432 \u0411\u0414 PostreSQL, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u043a\u043e\u043b\u043e\u043d\u043a\u0443 &#171;\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u0432  \u0440\u0443\u0431\u043b\u044f\u0445&#187;. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443.<\/p>\n<p>\u041a\u0430\u043a\u0438\u0435 \u0435\u0449\u0435 \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u0441\u0442\u043e\u0438\u0442 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0411\u0414? \u0422.\u043a. \u043d\u043e\u043c\u0435\u0440\u0443 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0434\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043d\u0435 \u0441\u0442\u043e\u0438\u0442, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0433\u043e \u0437\u0430\u043d\u043e\u0441\u0438\u0442 \u0447\u0435\u043b\u043e\u0432\u0435\u043a, \u043a\u043e\u043b\u043e\u043d\u043a\u0443 \u0441 \u043d\u043e\u043c\u0435\u0440\u043e\u043c \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435. \u0421\u0442\u0440\u043e\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u043d\u0434\u0435\u043a\u0441 \u0441\u0442\u0440\u043e\u043a\u0438 \u0442\u043e\u0436\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043c. \u0422.\u043a. \u043c\u044b \u0434\u043b\u044f \u0447\u0435\u0433\u043e-\u0442\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432\u0441\u0451 \u0432 \u0441\u0432\u043e\u044e \u0411\u0414, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u0441\u0432\u043e\u0439 \u043a\u043b\u0438\u0435\u043d\u0442, \u0438 \u0442.\u043a. \u043c\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043b\u043e\u043d\u043a\u0443 updated_at \u0441 \u0434\u0430\u0442\u043e\u0439 \u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c (!) \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f. PostreSQL \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u043e\u043d\u043a\u0443 created_at, \u0442\u0430\u043a \u0447\u0442\u043e \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0445\u043e\u0440\u043e\u0448\u0438\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c. \u041e\u0431\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u0434\u043b\u044f PostgreSQL \u0442\u0438\u043f\u0430 \u0434\u0430\u0442\u044b-\u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0431\u0435\u0437 \u0443\u0447\u0435\u0442\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u044f\u0441\u043e\u0432 <a href=\"https:\/\/www.postgresql.org\/docs\/current\/datatype-datetime.html\" rel=\"noopener noreferrer nofollow\"> timestamp<\/a> (PostreSQL \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/ISO_8601\" rel=\"noopener noreferrer nofollow\">ISO 8601<\/a>).<\/p>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0440\u0443\u044e \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u044d\u0442\u0430\u043f\u044b:<\/p>\n<ul>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0411\u0414 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u043d\u0435\u0439 \u0438\u0437 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 Python.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0443\u0441\u0442\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u043b\u0438\u0441\u0442 \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b-\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043d\u043e\u0432\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443. \u0412\u043f\u0440\u043e\u0447\u0435\u043c, \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0441\u0440\u0430\u0437\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u043e\u043c, \u043d\u043e \u0442\u0430\u043a \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0432\u0435\u0440\u043d\u043e? \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u043c\u0443 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0443 \u0435\u0449\u0435 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0434\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 \/ \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0438 \u043f\u0440\u0435\u0436\u0434\u0435, \u0447\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 &#171;\u0431\u043e\u0435\u0432\u044b\u043c&#187; \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c, \u0440\u0430\u0437\u0443\u043c\u043d\u043e \u043d\u0430\u043b\u0430\u0434\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u043d\u0430 \u043a\u043e\u043f\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0434\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0441\u0432\u043e\u0435\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c\u0443 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0443;<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c Google-\u0434\u0438\u0441\u043a \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0432 \u0444\u0430\u0439\u043b\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0435\u0441\u0442\u044c, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0443 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0432 \u0411\u0414.<\/p>\n<\/li>\n<\/ul>\n<p>\u041e\u0442\u043c\u0435\u0447\u0443, \u0447\u0442\u043e \u0437\u0430\u0434\u0430\u0447\u0443 \u0432\u044b\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c, \u043d\u0435 \u0441\u0442\u0430\u0432\u043b\u044e. \u0418 \u043d\u0435\u0442, SheetAPI \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0445\u043e\u0442\u044f \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 Google Sheets \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u043d\u044b, \u0430 \u0437\u043d\u0430\u0447\u0438\u0442 Google \u0438\u0445 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 \u0438 \u0445\u0440\u0430\u043d\u0438\u0442. \u0422\u0430\u043a\u0438\u0445 \u0434\u0435\u0442\u0430\u043b\u0435\u0439 \u0432 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 Sheets API \u0431\u0443\u0434\u0435\u0442 \u0435\u0449\u0435 \u043c\u043d\u043e\u0433\u043e, \u0438 \u043e \u043a\u0430\u0436\u0434\u043e\u0439 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0434\u0430\u043b\u0435\u0435.<\/p>\n<p>\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u0443\u0434\u0443 \u0447\u0430\u043d\u043a\u0430\u043c\u0438 (chunks) &#8212; \u043f\u043e\u0440\u0446\u0438\u044f\u043c\u0438 \u043f\u043e N \u0441\u0442\u0440\u043e\u043a. \u0422\u0430\u043a \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0431\u043e\u0435\u0432\u044b\u043c\u0438 \u0411\u0414, \u0432\u0435\u0434\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u0432 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435 \u043d\u0435 \u0431\u0435\u0437\u0433\u0440\u0430\u043d\u0438\u0447\u043d\u043e, \u0430 \u043a\u0430\u0436\u0434\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u0432\u0435\u0441\u0438\u0442\u044c \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442\u044b. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441\u043d\u0438\u0437\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u043b\u043b\u0438\u0437\u0438\u0439 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0411\u0414 \u0432 \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435, \u0445\u043e\u0442\u044f \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435, \u0447\u0442\u043e \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u0441\u0442\u043e \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u044d\u0442\u043e \u043d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442. \u041e\u0434\u043d\u0430\u043a\u043e, \u0442\u0435\u043c\u0430 \u044d\u0442\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f. \u0421\u043f\u0440\u0430\u0432\u0435\u0434\u043b\u0438\u0432\u043e\u0439 \u0431\u0443\u0434\u0435\u0442 \u043a\u0440\u0438\u0442\u0438\u043a\u0430: &#171;\u0414\u0430 \u043a\u0442\u043e \u0436\u0435 \u0445\u0440\u0430\u043d\u0438\u0442 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u044b \u0441\u0442\u0440\u043e\u043a \u0432 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u0435!?&#187; &#8212; \u044d\u0442\u043e \u0432\u0435\u0440\u043d\u043e: \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u043d\u043e\u0432\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0442\u043e\u043b\u044c\u043a\u043e 1000 \u0441\u0442\u0440\u043e\u043a, \u043d\u043e \u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c, \u0430 \u0432 \u044f\u0447\u0435\u0439\u043a\u0430\u0445 \u0441\u0442\u0440\u043e\u043a\u0438 \u043c\u043e\u0436\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u0435\u043a\u0441\u0442\u0430. \u0412 \u043e\u0431\u0449\u0435\u043c, \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u0441\u0442\u043e \u0431\u044b\u0442\u044c \u0434\u0430\u043b\u0435\u0435, \u0430 \u0447\u0438\u0441\u043b\u043e \u0441\u0442\u0440\u043e\u043a, \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445 \u0437\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0446\u0435\u043b\u0435\u0441\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u043e \u043f\u043e\u0434\u0431\u0438\u0440\u0430\u0442\u044c \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0434\u0430\u043d\u043d\u044b\u0445, \u0445\u0440\u0430\u043d\u044f\u0449\u0438\u0445\u0441\u044f \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u0438 \u0435\u0441\u043b\u0438 \u0412\u044b \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u043d\u0438\u0436\u0435 \u0432 \u0440\u0430\u0431\u043e\u0442\u0435, \u0442\u043e \u0432\u0441\u0435\u0433\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0441\u0435\u0442\u044c, \u043f\u043e\u0434\u043e\u0431\u0440\u0430\u0442\u044c \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0412\u0430\u0448\u0438\u0445 \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u0435\u0432.<\/p>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043b\u044e\u0447\u0430\u043c\u0438, \u043f\u0430\u0440\u043e\u043b\u044f\u043c\u0438 \u0438 \u0442.\u043f. \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430. \u0411\u0443\u0434\u0443 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0445 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0435 secret\/ \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 .json. \u0420\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0438\u043c\u0438 \u0431\u0443\u0434\u0435\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0434\u0443\u043b\u0435\u0439 os, json.<\/p>\n<p><a class=\"anchor\" name=\"db_creation\" id=\"db_creation\"><\/a><\/p>\n<h3>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0411\u0414<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0411\u0414 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0443. \u042f \u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0441\u044c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c <a href=\"https:\/\/www.jetbrains.com\/ru-ru\/datagrip\/\" rel=\"noopener noreferrer nofollow\">JetBrains Datagrip<\/a>. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0441\u0430\u043c PostgreSQL \u0443\u0436\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e\u0442 \u0438\u043c\u0435\u043d\u0438 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<pre><code class=\"bash\">psql -U postgres<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0412\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u043d\u0430 Windows, \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u043a\u043e\u043d\u0441\u043e\u043b\u044c psql. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u043d\u0430 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044f \u0432 <em>C:\\Program Files\\PostgreSQL\\14\\scripts\\runpsql.bat<\/em>, \u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 \u044f\u0440\u043b\u044b\u043a <em>C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\PostgreSQL 14\\(psql).lnk<\/em>.<\/p>\n<p>\u0412\u0432\u043e\u0434\u0438\u043c \u043b\u043e\u0433\u0438\u043d postres, \u043f\u0430\u0440\u043e\u043b\u044c \u043e\u0442 \u0412\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u043f\u043e\u0440\u0442 \u0438 \u0445\u043e\u0441\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: localhost:5432.<\/p>\n<pre><code class=\"sql\">CREATE USER user_example WITH password '123456'; CREATE DATABASE example_db OWNER user_example; # \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u044b\u0434\u0430\u0442\u044c \u043f\u0440\u0438\u0432\u0435\u043b\u0435\u0433\u0438\u0438: GRANT ALL PRIVILEGES ON DATABASE example_db TO &lt;yourname>;<\/code><\/pre>\n<p>\u0412\u044b\u0445\u043e\u0434\u0438\u043c (<code>exit;<\/code>), \u0437\u0430\u0445\u043e\u0434\u0438\u043c \u0432 DataGrip \u0424\u0430\u0439\u043b -> \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442, (+) -> \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a -> PostgreSQL -> \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043f\u043e\u043b\u044f, \u043a\u0430\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0438\u0436\u0435:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/cab\/8b3\/a2c\/cab8b3a2caa317edf65a7821cc0a8a91.png\" alt=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a 2. \u041f\u0440\u043e\u0435\u043a\u0442 Datagrip\" title=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a 2. \u041f\u0440\u043e\u0435\u043a\u0442 Datagrip\" width=\"798\" height=\"669\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cab\/8b3\/a2c\/cab8b3a2caa317edf65a7821cc0a8a91.png\"\/><figcaption>\u0420\u0438\u0441\u0443\u043d\u043e\u043a 2. \u041f\u0440\u043e\u0435\u043a\u0442 Datagrip<\/figcaption><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432\u0432\u043e\u0434\u0438\u043c \u0432\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c:<\/p>\n<pre><code class=\"sql\">-- \u0422\u0440\u0438\u0433\u0433\u0435\u0440 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435  CREATE     OR REPLACE FUNCTION trigger_set_timestamp()     RETURNS TRIGGER AS $$ BEGIN     NEW.updated_at         = NOW();     RETURN NEW; END; $$     LANGUAGE plpgsql;  -- \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0414\u0430\u0442\u0443 \u0438\u0437 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0445\u0440\u0430\u043d\u044e \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 delivery_date: \u0442\u0430\u043a \u043f\u0440\u043e\u0449\u0435 CREATE TABLE Orders (     id                        SERIAL                    NOT NULL PRIMARY KEY,     table_row_index           INTEGER                   NOT NULL UNIQUE,     -- \u043d\u043e\u043c\u0435\u0440 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 \u0430\u0434\u0440\u0435\u0441\u0435 \u0432\u0438\u0434\u0430 A1:A50     table_row_number          INTEGER                   NOT NULL,     order_number              INTEGER                   NOT NULL UNIQUE,     cost_usd                  NUMERIC(12, 2)            NOT NULL,     cost_rub                  NUMERIC(12, 2)            NOT NULL,     delivery_date             CHAR(10)                  NOT NULL,     created_at                TIMESTAMPTZ               NOT NULL DEFAULT NOW(),     updated_at                TIMESTAMPTZ               NOT NULL DEFAULT NOW() );  -- \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0442\u0440\u0438\u0433\u0433\u0435\u0440 CREATE TRIGGER set_timestamp     BEFORE UPDATE     ON Orders     FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); <\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e delivery_date &#8212; \u0434\u0430\u0442\u0430 \u0438\u0437 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u044b &#8212; \u0438\u043c\u0435\u0435\u0442 \u0442\u0438\u043f \u0441\u0442\u0440\u043e\u043a\u0438, \u0430 \u0434\u0435\u043d\u0435\u0436\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f NUMERIC(12, 2). \u0421\u043e \u0441\u0442\u0440\u043e\u043a\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0449\u0435, \u0438\u043d\u0430\u0447\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u043b\u043e\u0441\u044c \u0431\u044b \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 \u0434\u0430\u0442\u043e\u0439 \u0438\u0437 \u043e\u0431\u043b\u0430\u043a\u0430 \u0432 ISO 8601, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u0438\u0445\u0430\u0442\u044c \u0435\u0435 \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0421 \u0434\u0435\u043d\u0435\u0436\u043d\u044b\u043c\u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u043c\u0438 \u0442\u043e\u0436\u0435 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e, \u043d\u043e \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0441 \u0434\u0432\u0443\u043c\u044f \u0446\u0438\u0444\u0440\u0430\u043c\u0438 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439, \u043e\u0434\u043d\u0430\u043a\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0447\u0438\u0441\u043b\u043e, \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430:<\/p>\n<pre><code class=\"sql\">INSERT INTO Orders(   table_row_index,   table_row_number,   order_number,   cost_rub,   cost_usd,   delivery_date ) VALUES (   57, 1, 1342,   300::NUMERIC(12, 2),   -- \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430   18000::NUMERIC(12, 2), -- \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430   '04.07.2022'::char(10) -- \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 );<\/code><\/pre>\n<p>\u041f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u043e\u0431\u043b\u0430\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043b\u0438 \u0434\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u0443\u0436\u0435 \u0432 \u0411\u0414. \u041e\u0431\u0449\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0445\u043e\u0434, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u044e\u0449\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432\u0441\u0442\u0430\u0432\u043a\u0438 insert \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f update. \u041f\u0440\u0438\u0447\u0435\u043c \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u043f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438\u0437 Python!<\/p>\n<pre><code class=\"sql\">CREATE     OR REPLACE FUNCTION upsert_orders(         arg_table_row_index INTEGER,         arg_table_row_number INTEGER,         arg_order_number INTEGER,         arg_cost_usd DOUBLE PRECISION,         arg_cost_rub DOUBLE PRECISION,         arg_delivery_date CHAR(10)     )     RETURNS VOID AS $$ DECLARE BEGIN     UPDATE Orders as o SET         table_row_number = arg_table_row_number,         order_number = arg_order_number,         cost_usd = arg_cost_usd,         cost_rub = arg_cost_rub,         delivery_date = arg_delivery_date     WHERE table_row_index = arg_table_row_index;      IF NOT FOUND THEN      INSERT INTO Orders(table_row_index, table_row_number, order_number, cost_rub, cost_usd, delivery_date)     VALUES (arg_table_row_index, arg_table_row_number, arg_order_number, arg_cost_rub, arg_cost_usd, arg_delivery_date);      END IF; END; $$     LANGUAGE plpgsql;<\/code><\/pre>\n<p>\u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u0432\u0441\u0435 -> \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c (\u0437\u0435\u043b\u0435\u043d\u044b\u0439 \u0442\u0440\u0435\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 \u0441\u0432\u0435\u0440\u0445\u0443).<\/p>\n<p>\u0411\u0414 \u0441\u043e\u0437\u0434\u0430\u043d\u0430, \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0441\u043a\u0440\u0438\u043f\u0442\u0430.<\/p>\n<p><a class=\"anchor\" name=\"configs\" id=\"configs\"><\/a><\/p>\n<h3>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f. \u041a\u043e\u043d\u0444\u0438\u0433\u0438<\/h3>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<pre><code class=\"bash\">root\/ \u2514\u2500\u2500\u2500 secret\/ \u2502    credentials.json        # \u0444\u0430\u0439\u043b \u0441 \u043a\u043b\u044e\u0447\u0430\u043c\u0438 \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0441\u0438\u043d\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 \u2502    db.json                 # \u0444\u0430\u0439\u043b \u0441 \u043f\u0430\u0440\u043e\u043b\u044f\u043c\u0438 \u043a \u0421\u0423\u0411\u0414 \u2502    gsheets_cached_config.json # \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u2502 \u2514\u2500\u2500\u2500 script\/     \u2502   main.py           # \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0435\u0447\u043a\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430     \u2502     \u2514\u2500\u2500\u2500lib\/     \u2502   config.py         # \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b         \u2502   ConfigLoader.py   # \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0430\u043c\u0438         \u2502   CRB.py            # \u0417\u0430\u0431\u0440\u043e\u0441\u044b \u043a API \u0426\u0411 \u0420\u0424         \u2502   DBConnector.py    # \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0411\u0414         \u2502   GSheets.py        # \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Google Sheets API         <\/code><\/pre>\n<p>\u0412\u0441\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0438 &#8212; \u043e\u0434\u043d\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0435 json&#8217;\u044b. \u0414\u043b\u044f \u0438\u0445 \u0447\u0442\u0435\u043d\u0438\u044f \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 ConfigLoader:<\/p>\n<pre><code class=\"python\"># script\/lib\/ConfigLoader.py  import json import os  class ConfigLoader:   def __init__(self, dir, filenames):     self.config = {}     for filename in filenames:       filename_path = os.path.join(dir, filename)       # \u0415\u0441\u043b\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 \u043d\u0435\u0442, \u043e\u043d \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0438 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435       if not os.path.exists(filename_path):         try:           print(filename, 'does not exist')           with open(filename_path, 'w'): pass           print(filename, 'created in', dir)         except Exception as e:           print('Config error:', e)           continue        with open(filename_path, 'r') as config_file:         try:           # \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c self.config \u0432\u0438\u0434\u0430:           # {           # [\u0438\u043c\u044f \u0444\u0430\u0439\u043b\u0430 \u0431\u0435\u0437 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f]: [JSON \u0432 \u043d\u0451\u043c]           # }           self.config[os.path.splitext(os.path.basename(filename))[0]] = json.load(config_file)         except Exception as e:           print('Config error on loading config:' , e) <\/code><\/pre>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0411\u0414 \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"json\">\/\/ secret\/db.json  {   \"DB_NAME\": \"db_example\",   \"USER\": \"user_example\",   \"PSWD\": \"123456\",   \"HOST\": \"localhost\" }<\/code><\/pre>\n<pre><code class=\"python\"># script\/lib\/config.py  from pprint import pprint from .ConfigLoader import ConfigLoader  # files not to share SECRETS_DIRECTORY = 'secret\/' CREDENTIALS_FILE = 'credentials.json'           # Google service account credentials.                                                 # Relative to SECRETS_DIRECTORY CONFIG_FILENAME = 'gsheets_cached_config.json'  # Script creates new table on launch and writes its                                                 # params here. If the file exists, new table.                                                 # Relative to SECRETS_DIRECTORY  config = ConfigLoader(SECRETS_DIRECTORY, [   'db.json', ]).config  if not config.get('db'):   print('there is no db.json in', SECRETS_DIRECTORY)   exit(1) # keys for DB connecting try:   DB_NAME = config['db']['DB_NAME']   USER = config['db']['USER']   PSWD = config['db']['PSWD']   HOST = config['db']['HOST'] except KeyError as ke:   print('DB: config error. missing field', ke) except Exception as e:   print('DB: config error', e)   exit(1)  print('App started with config:') pprint(config)<\/code><\/pre>\n<p><a class=\"anchor\" name=\"stage-1\" id=\"stage-1\"><\/a><\/p>\n<h3>\u042d\u0442\u0430\u043f 1. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a PostreSQL \u0438\u0437 Python<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0441\u043e\u0431\u0440\u0430\u043d\u0430 \u0431\u0430\u0437\u0430 \u0438 \u0441\u043a\u0440\u0438\u043f\u0442 \u0437\u043d\u0430\u0435\u0442, \u043a\u0430\u043a \u043a \u043d\u0435\u0439 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f, \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438-\u043e\u0431\u0435\u0440\u0442\u043a\u0438 \u0434\u043b\u044f PostgreSQL \u0432 Python &#8212; psycopg2.<\/p>\n<pre><code class=\"python\"># script\/lib\/DBConnector.py  class DB:   def __init__(self):     self.connected = False     self.conn = None     self.cursor = None     pass    def _connect(self):     self.connected = True     self.conn = psycopg2.connect(dbname=DB_NAME, user=USER,                                   password=PSWD, host=HOST)     self.cursor = self.conn.cursor()     self.conn.autocommit = True      self._safely_execute(\"select relname from pg_class where relkind='r' and relname !~ '^(pg_|sql_)';\")     tables = map(lambda x: x[0], self.cursor.fetchall())     if not MAIN_TABLE_NAME.lower() in tables:       print(f'DB: Table {MAIN_TABLE_NAME} is missing in {DB_NAME}!')       self._close()        def _safely_execute(self, q):     # https:\/\/www.psycopg.org\/docs\/errors.html     try:       self.cursor.execute(q)     except Exception as e:       print('DB: Postgre execution error:', e)<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b:<\/p>\n<pre><code class=\"python\"># script\/lib\/DBConnector.py    def _close(self):     self.connected = False      self.cursor.close()     self.conn.close()      self.conn = None     self.cursor = None         def __enter__(self):     self._connect()     return self         def __exit__(self, exc_type, exc_val, exc_tb):     self._close()     def connect(self):     self._connect()     def close(self):     self._close()<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432:<\/p>\n<ul>\n<li>\n<p>\u0447\u0442\u0435\u043d\u0438\u0435 \u0447\u0430\u043d\u043a\u0430:<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\"># script\/lib\/DBConnector.py    def get_chunk(self, from_idx, to_idx):     if not self.connected:       self._connect()      q = f''' SELECT * FROM {MAIN_TABLE_NAME} WHERE table_row_index >= {from_idx + TRACKED_SHEET_HEADER_OFFSET + 1} AND table_row_index &lt;= {to_idx + TRACKED_SHEET_HEADER_OFFSET};};}; '''     self._safely_execute(q)     return self.cursor.fetchall()<\/code><\/pre>\n<ul>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0443\u0431\u043b\u0435\u0432\u044b\u0445 \u0446\u0435\u043d \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043a\u0443\u0440\u0441\u0430 \u0434\u043e\u043b\u043b\u0430\u0440\u0430:<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\"># script\/lib\/DBConnector.py    def update_cost_rub(self, price):     if not self.connected:       self._connect()      q = f''' UPDATE {MAIN_TABLE_NAME} SET cost_rub = cost_usd * {price};     '''      self._safely_execute(q)<\/code><\/pre>\n<ul>\n<li>\n<p>\u0412\u0441\u0442\u0430\u0432\u043a\u0430 \u0438\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u043e\u043a \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043e\u0431\u043b\u0430\u043a\u0430:<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\"># script\/lib\/DBConnector.py    def upsert(self, values):     if not self.connected:       self._connect()      q = ''     for value in values:       if len(value) &lt; FIELDS_NUM:         if len(value) > 0:           print('\\tDB.upsert:\\tmissing values:', value, '\\trequired length is', FIELDS_NUM)         continue       q += f'SELECT upsert_orders{_gen_value_string(value)};\\n'          self._safely_execute(q)<\/code><\/pre>\n<p>\u0412 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0444\u043e\u0440\u043c\u0430\u0442\u0430. \u0414\u043b\u044f \u043f\u043e\u0434\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u044f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438, \u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438\u0437 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 values \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <em>_gen_value_string<\/em> <u>(<\/u>\u043e \u043d\u0435\u0439 \u043d\u0438\u0436\u0435<u>)<\/u>. \u041d\u0443\u0436\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u0432\u0432\u0438\u0434\u0443, \u0447\u0442\u043e \u0432\u043c\u0435\u0441\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u044b \u0441\u0442\u0440\u043e\u043a\u0438 SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u0441\u0443\u0442 \u0443\u0433\u0440\u043e\u0437\u0443 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0443\u0434\u0435\u043b\u044f\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u044d\u0442\u043e\u043c\u0443 \u043d\u0435 \u0431\u0443\u0434\u0435\u043c.<\/p>\n<p>\u0422\u0443\u0442 \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\u0439. \u041e\u043d\u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0432 script\/lib\/config.py<\/p>\n<pre><code class=\"python\"># script\/lib\/config.py  # \u0440\u0430\u0437\u043c\u0435\u0440 \u0447\u0430\u043d\u043a\u0430 CHUNK_SIZE = 1000 MAIN_TABLE_NAME = 'Orders' # \u0418\u043c\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b   # \u0421\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u043c\u0435\u043d\u0438 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 Postgres \u0441 \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u043c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e # \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u0441\u0442\u0440\u043e\u043a\u0430\u0445 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u044b.  MAIN_TABLE_FIELD_NAME__GOOGLE_SPREADSHEET_COLUMN_IDX__MAP = {   \"table_row_index\": 4,   \"table_row_number\": 0,   \"order_number\": 1,   \"cost_usd\": 2,   \"cost_rub\": 5,   \"delivery_date\": 3, }  # \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0442\u0443\u0442, \u043d\u0443\u0436\u043d\u043e \u043b\u0438 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 \u0434\u043b\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f MAIN_TABLE_FIELD_NAME__MAIN_TABLE_FIELD_TYPE = {   \"table_row_index\": '',   \"table_row_number\": '',   \"order_number\": '',   \"cost_usd\": '::NUMERIC(12, 2)',   \"cost_rub\": '::NUMERIC(12, 2)',   \"delivery_date\": '::CHAR(10)', }  # \u041f\u043e\u0440\u044f\u0434\u043e\u043a, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u043b\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 SQL-\u0437\u0430\u043f\u0440\u043e\u0441 DEFAULT_ORDER = (   'table_row_index',   'table_row_number',   'order_number',   'cost_usd',   'cost_rub',   'delivery_date', )  TRACKED_SHEET_HEADER_OFFSET = 1                 # \u0421\u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a \u043e\u0442 \u043d\u0430\u0447\u0430\u043b\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u043f\u0443\u0449\u0435\u043d\u043e. # \u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0442\u0430\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a<\/code><\/pre>\n<p>\u0421\u043b\u043e\u0432\u0430\u0440\u0438 \u043d\u0443\u0436\u043d\u044b \u043f\u043e \u0442\u043e\u0439 \u043f\u0440\u0438\u0447\u0438\u043d\u0435, \u0447\u0442\u043e \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044e <em>_gen_value_string <\/em>\u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f <em>iterable: <\/em>\u043a\u043e\u0440\u0442\u0435\u0436 \u0438\u043b\u0438 \u0441\u043f\u0438\u0441\u043e\u043a<em>., <\/em>\u043f\u043e\u0440\u044f\u0434\u043e\u043a \u043f\u043e\u043b\u0435\u0439 \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043f\u043e\u0440\u044f\u0434\u043a\u043e\u043c \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \u0432 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u0442\u0430\u043a \u0438 \u043d\u0430\u043c\u0438, \u0442.\u043a. \u043f\u043e\u043b\u044f table_row_index \u0438 cost_rub \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0432 \u043e\u0431\u043b\u0430\u043a\u0435. \u0424\u0443\u043d\u043a\u0446\u0438\u044f <em>_gen_value_string <\/em>\u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0438\u043d\u0434\u0435\u043a\u0441 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c \u043f\u043e\u043b\u044f, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0438\u043c\u044f \u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u0438 \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\u0443, \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438  \u0444\u043e\u0440\u043c\u0430\u0442\u044b Google-\u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0432 \u0411\u0414 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044f.<\/p>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u044d\u0442\u043e \u0438\u0437\u043b\u0438\u0448\u043d\u0435\u0435 \u0443\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u0435, \u043d\u043e \u0432\u043e\u0442 \u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u0447\u0435\u043c\u0443 \u044f \u0441\u0447\u0438\u0442\u0430\u044e, \u0447\u0442\u043e \u044d\u0442\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u0443\u0436\u043d\u0430:<\/p>\n<pre><code class=\"sql\">INSERT INTO Orders(   table_row_index, table_row_number, order_number, cost_usd, cost_rub, delivery_date ) VALUES(3, 2, 1338, 250, 15000, '05.07.2022');  INSERT INTO Orders(   table_row_index, order_number, cost_usd, cost_rub, delivery_date, table_row_number ) VALUES(3, 2, 1338, 250, 15000, '05.07.2022');<\/code><\/pre>\n<p>\u0412\u0442\u043e\u0440\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u044b\u0434\u0430\u0441\u0442 \u043e\u0448\u0438\u0431\u043a\u0443, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043f\u043e\u043b\u044f \u043f\u0435\u0440\u0435\u043f\u0443\u0442\u0430\u043d\u044b. \u0412 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u0432\u044b\u0448\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>upsert<\/code> \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u043f\u043e\u043b\u0435\u0439 \u0432\u043e\u043e\u0431\u0449\u0435 \u0436\u0435\u0441\u0442\u043a\u043e \u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d. \u0417\u0430\u0431\u0435\u0433\u0430\u044f \u0432\u043f\u0435\u0440\u0435\u0434 \u0441\u043a\u0430\u0436\u0443, \u0447\u0442\u043e SheetsAPI \u0432\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0441\u0441\u0438\u0432 \u0438\u0437 4-\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 &#8212; \u044d\u0442\u043e \u043f\u043e\u043b\u044f table_row_number, order_number, cost_usd, delivery_date &#8212; \u0438\u043c\u0435\u043d\u043d\u043e \u0432 \u0442\u0430\u043a\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435. \u0421 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u0430 \u0446\u0435\u043b\u0435\u0441\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u043e \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u043d\u0435\u0446 \u043c\u0430\u0441\u0441\u0438\u0432\u0430, \u0447\u0442\u043e \u043f\u0440\u0438 \u0442\u0430\u043a\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043b\u0435\u0433\u043a\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c. \u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u044e <em>_gen_value_string <\/em>\u0442\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 [&lt;table_row_number>, &lt;order_number>, &lt;cost_usd>, &lt;delivery_date>, &lt;table_row_index>, &lt;cost_rub>] &#8212; \u0438\u043d\u0434\u0435\u043a\u0441\u044b \u043f\u043e\u043b\u0435\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0442\u0435\u043c, \u0447\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u0435 <code>MAIN_TABLE_FIELD_NAME__GOOGLE_SPREADSHEET_COLUMN_IDX__MAP<\/code>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044f <em>_gen_value_string<\/em> \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 SQL-\u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u043e\u0439 \u043f\u043e\u043b\u0435\u0439, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0443 <em>order<\/em>:<\/p>\n<pre><code class=\"python\"># script\/lib\/DBConnector.py  def _gen_value_string(row, order = DEFAULT_ORDER):   result = '('   last_field = order[-1]   for field in order:     if field == 'delivery_date':       result += '\\''     result += str(row[MAIN_TABLE_FIELD_NAME__GOOGLE_SPREADSHEET_COLUMN_IDX__MAP[field]])     if field == 'delivery_date':       result += '\\''     result += MAIN_TABLE_FIELD_NAME__MAIN_TABLE_FIELD_TYPE[field]     if field != last_field:       result += ', '   return result + ')'<\/code><\/pre>\n<p><a class=\"anchor\" name=\"stage-2-1\" id=\"stage-2-1\"><\/a><\/p>\n<h3>\u042d\u0442\u0430\u043f 2. \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0443\u0440\u0441\u0430<\/h3>\n<p>\u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0442\u0443\u0442 \u043c\u0430\u043b\u043e. \u0418\u0434\u0435\u043c \u0432 XML-API \u0426\u0411 \u0420\u0424 \u0438 \u0438\u0437\u044b\u043c\u0430\u0435\u043c \u043d\u0443\u0436\u043d\u044b\u0435 \u0446\u0438\u0444\u0440\u044b. \u0422\u044d\u0433\u0438, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0438\u0449\u0435\u043c \u043d\u0443\u0436\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u044b \u0432 \u043a\u043e\u043d\u0444\u0438\u0433. \u0422\u0443\u0442 \u043f\u0430\u0440\u0441\u0438\u043d\u0433 XML \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u043c\u0438 \u0432 python \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u043e \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438. \u0414\u043b\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 XML \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043c\u043e\u0434\u0443\u043b\u044c <a href=\"https:\/\/habr.com\/ru\/post\/544828\/\" rel=\"noopener noreferrer nofollow\">BeautifulSoup<\/a>. API \u043e\u0442\u0434\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0442\u0430\u043a\u043e\u043c \u0432\u0438\u0434\u0435 (<a href=\"http:\/\/www.cbr.ru\/scripts\/XML_daily.asp?date_req=02\/03\/2002\" rel=\"noopener noreferrer nofollow\">\u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c<\/a> \u043f\u0440\u0438\u043c\u0435\u0440):<\/p>\n<pre><code class=\"xml\">&lt;CharCode>USD&lt;\/CharCode> &lt;Name>\u0414\u043e\u043b\u043b\u0430\u0440 \u0421\u0428\u0410&lt;\/Name> &lt;Value>30,9436&lt;\/Value><\/code><\/pre>\n<pre><code class=\"python\"># script\/lib\/CRB.py  import httplib2 from locale import atof from datetime import datetime  from .config import VALUTE_CHAR_CODE, VALUTE_INFO_OPEN_TAG, VALUTE_INFO_VALUE_OPEN_TAG, \\   VALUTE_INFO_VALUE_CLOSE_TAG, VALUTE_INFO_NOMINAL_OPEN_TAG, VALUTE_INFO_NOMINAL_CLOSE_TAG, \\   REQUEST_TIMEOUT  class CRB:   def __init__(self):     self.client = httplib2.Http(timeout=REQUEST_TIMEOUT)     self.currency_rate = 0     self.fetch_currency_rate()    def fetch_currency_rate(self):     try:       cbr_response, cbr_response_s = self.client.request(         f'http:\/\/www.cbr.ru\/scripts\/XML_daily.asp?date_req={datetime.now().strftime(\"%d\/%m\/%Y\")}', 'GET',       )       cbr_response_s = str(cbr_response_s)        if cbr_response.get('status') != '200':         print('Error fetching currency from http:\/\/www.cbr.ru\/ :\\n\\t', cbr_response_s)        try:         start_charcode_idx = cbr_response_s.index(VALUTE_INFO_OPEN_TAG + VALUTE_CHAR_CODE) + len(VALUTE_INFO_OPEN_TAG + VALUTE_CHAR_CODE)         start_value_idx = cbr_response_s.index(VALUTE_INFO_VALUE_OPEN_TAG, start_charcode_idx) +  len(VALUTE_INFO_VALUE_OPEN_TAG)         end_value_idx = cbr_response_s.index(VALUTE_INFO_VALUE_CLOSE_TAG, start_value_idx)         start_nominal_idx = cbr_response_s.index(VALUTE_INFO_NOMINAL_OPEN_TAG) + len(VALUTE_INFO_NOMINAL_OPEN_TAG)         end_nominal_idx = cbr_response_s.index(VALUTE_INFO_NOMINAL_CLOSE_TAG, start_nominal_idx)         currency_rate = cbr_response_s[start_value_idx: end_value_idx]         nominal = cbr_response_s[start_nominal_idx: end_nominal_idx]         self.currency_rate = atof(currency_rate.replace(',','.')) \/ atof(nominal.replace(',','.'))       except ValueError:         print(f'Error: unable to fetch {VALUTE_CHAR_CODE} currency rate. The CBR API has changed')       except Exception as e:       print(f'Error: unable to fetch {VALUTE_CHAR_CODE} currency rate:', e)     finally:       return self.currency_rate     <\/code><\/pre>\n<p>\u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u0436\u0435 \u0441\u043a\u043e\u0440\u0435\u0435 \u043a \u0441\u0430\u043c\u043e\u043c\u0443 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u043c\u0443!<\/p>\n<p><a class=\"anchor\" name=\"stage-2-2\" id=\"stage-2-2\"><\/a><\/p>\n<h3>\u042d\u0442\u0430\u043f\u044b 2-4. \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Sheets API<\/h3>\n<p>\u041f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0431 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430\u0445 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0445:<\/p>\n<pre><code class=\"python\"># script\/lib\/config.py  CREDENTIALS_FILE = 'secret\/credentials.json'    # Google service account credentials CONFIG_FILENAME = 'gsheets_cached_config.json'  # \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043d\u0430 \u0434\u0438\u0441\u043a\u0435                                                 # \u0415\u0441\u043b\u0438 \u044d\u0442\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 \u043d\u0435\u0442, \u0441\u043a\u0440\u0438\u043f\u0442 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u043d\u043e\u0432\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 EMAIL_ADDRS = ['example1@gmail.com', 'example2@gmail.com']                                                 # gmail'\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0442\u0430\u0431\u043b\u0438\u0446\u0435-\u043a\u043e\u043f\u0438\u0438 SOURCE_SPREADSHEET = '...'                      # ID \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b TRACKED_SHEET_NAME = 'SourceSheet'              # \u0418\u043c\u044f \u043b\u0438\u0441\u0442\u0430 \u0441 \u043a\u043e\u043f\u0438\u0435\u0439 TRACKED_SHEET_HEADER_OFFSET = 1                 # \u0421\u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a \u043e\u0442 \u043d\u0430\u0447\u0430\u043b\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u043f\u0443\u0449\u0435\u043d\u043e. # \u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0442\u0430\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a START_COLUMN = 'A'                              # \u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 [\u043e\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u044f]:[\u0434\u043e \u0432\u043a\u043b\u044e\u0447\u0430\u044f] END_COLUMN = 'D'<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0412\u044b \u0440\u0430\u043d\u0435\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043b\u0438 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u043d\u0430 \u0441\u0442\u0430\u0442\u044c\u044e, \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u043d\u043e\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 \u0438 \u0437\u0430\u043c\u0435\u0442\u043a\u0430\u043c \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 SheetsAPI, \u0442\u043e \u0443\u0436\u0435 \u0437\u043d\u0430\u0435\u0442\u0435, \u043e\u0442\u043a\u0443\u0434\u0430 \u0431\u0440\u0430\u0442\u044c SpreadSheetID, \u0435\u0441\u043b\u0438 \u0436\u0435 \u043d\u0435\u0442, \u0442\u043e \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 \u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430: <\/p>\n<pre><code class=\"python\">def gen_ss_link(ssID):   return 'https:\/\/docs.google.com\/spreadsheets\/d\/' + ssID<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0432\u044b\u0437\u043e\u0432\u044b Google Drive API \u0438 Sheets API \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b \u043b\u0438\u0448\u044c \u043f\u0440\u0438 \u0443\u0441\u043b\u043e\u0432\u0438\u0438, \u0447\u0442\u043e \u0412\u044b \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u044b. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b <a href=\"https:\/\/habr.com\/ru\/post\/483302\/\" rel=\"noopener noreferrer nofollow\">\u0440\u0430\u043d\u0435\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0444\u0430\u0439\u043b \u0441 \u043a\u043b\u044e\u0447\u0430\u043c\u0438 &#8212; credentials.json<\/a>.<\/p>\n<p><a href=\"https:\/\/developers.google.com\/drive\/api\/guides\/about-auth\" rel=\"noopener noreferrer nofollow\">\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0447\u0435\u0440\u0435\u0437 credentials<\/a> \u0438 \u0447\u0442\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u043c Google-\u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u0435\u0441\u043b\u0438 \u043e\u043d \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b\u0441\u044f \u0432\u043f\u0435\u0440\u0432\u044b\u0435:<\/p>\n<pre><code class=\"python\"># script\/lib\/GSheets.py  import httplib2  from googleapiclient.discovery import build from oauth2client.service_account import ServiceAccountCredentials import os import json  from .ConfigLoader import ConfigLoader  credentials = ServiceAccountCredentials.from_json_keyfile_name(   os.path.join(SECRETS_DIRECTORY, CREDENTIALS_FILE), [     'https:\/\/www.googleapis.com\/auth\/spreadsheets',     'https:\/\/www.googleapis.com\/auth\/drive', ])  class GSheets:   def __init__(self):     httpAuth = credentials.authorize(httplib2.Http(timeout=REQUEST_TIMEOUT)) # \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0435\u043c\u0441\u044f \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435      self.drive_service = build('drive', 'v3', http = httpAuth)     self.sheets_service = build('sheets', 'v4', http = httpAuth) # \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u043c\u0438 \u0438 4 \u0432\u0435\u0440\u0441\u0438\u044e API          config = ConfigLoader(SECRETS_DIRECTORY, [CONFIG_FILENAME]).config.get(       os.path.splitext(CONFIG_FILENAME)[0]     )     if config == None:       config = {}      self.spreadsheet_id = config.get('spreadsheetId')     self.main_sheet_id = config.get('mainSheetId')     self.start_page_token = config.get('startPageToken')     self.page_token = config.get('startPageToken')     self.last_update = config.get('lastUpdate')      if not self.spreadsheet_id or not self.main_sheet_id or not self.start_page_token:       print(f'GSheets: A config {CONFIG_FILENAME} doesn\\'t match a following format requirement')       pprint({         \"spreadsheetId\": 'spreadsheet id numbers as a string',         \"mainSheetId\":   'main table sheet id numbers as a string',         \"start_page_token\":   'changes tracking token',       })       self._create()<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0447\u0442\u0435\u043d\u0438\u0435 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c, \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0430\u0441\u0441\u0430 \u0432\u044b\u0437\u043e\u0432\u0435\u0442 \u043c\u0435\u0442\u043e\u0434 <code>_create()<\/code>. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0438 \u0432\u044b\u0434\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u0430 \u043d\u0430\u043c, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c, \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.<\/p>\n<p>\u041d\u043e \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u0434\u0440\u0443\u0433\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430. \u0414\u043b\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 API \u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 googleapiclient, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u0432\u0435\u0441\u044c \u043a\u043e\u0434, \u0435\u0441\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b, \u043e\u0434\u043d\u0430\u043a\u043e \u0438\u043d\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432 API \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 <a href=\"https:\/\/developers.google.com\/sheets\/api\/guides\/batch?hl=en\" rel=\"noopener noreferrer nofollow\">\u043c\u0435\u0442\u043e\u0434 batch<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0435\u043a\u0438\u0439 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 Google \u0438 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0432\u0441\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u041e\u0431\u0435\u0440\u043d\u0443 \u0435\u0433\u043e \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430:<\/p>\n<pre><code class=\"python\"># script\/lib\/GSheets.py    def batch(self, requests: list):     try:       return self.sheets_service.spreadsheets().batchUpdate(         spreadsheetId=self.spreadsheet_id,         body={           'requests': requests         }).execute()     except Exception as e:       print(f'GSheets error: unable to fetch {self.spreadsheet_id} table with Google Sheets API:', e)       return {}<\/code><\/pre>\n<p>\u0412\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u043f\u0440\u0430\u0432\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u0430. \u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u0437\u0430\u0434\u0430\u0447 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 <a href=\"https:\/\/developers.google.com\/sheets\/api\/guides\/create\" rel=\"noopener noreferrer nofollow\">sheets_service.spreadsheets().create(&#8230;)<\/a> \u0438 <a href=\"https:\/\/developers.google.com\/drive\/api\/v3\/reference\/permissions\/create\" rel=\"noopener noreferrer nofollow\">drive_service.permissions().create(&#8230;)<\/a>. \u041f\u0435\u0440\u0432\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u0432\u0435\u0440\u043d\u0435\u0442 SpreadsheetID. \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u043c \u0435\u0433\u043e \u0432 \u043f\u043e\u043b\u0435 self.spreadsheet_id.<\/p>\n<pre><code class=\"python\"># script\/lib\/GSheets.py      def _create(self):     # init     spreadsheet = self.sheets_service.spreadsheets().create(body = {       'properties': {'title': '\u041a\u043e\u043f\u0438\u044f \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430', 'locale': 'ru_RU'},     }).execute()      self.spreadsheet_id = spreadsheet['spreadsheetId']     print('GSheets: A spreadsheet created!\\n>', gen_ss_link(self.spreadsheet_id))      # \u043e\u0431 \u044d\u0442\u043e\u043c \u043f\u043e\u0437\u0436\u0435. \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.     response = self.drive_service.changes().getStartPageToken().execute()     self.start_page_token = response.get(\"startPageToken\")     self.page_token = response.get(\"startPageToken\")          for email in EMAIL_ADDRS:       self.drive_service.permissions().create(         fileId = self.spreadsheet_id,         body = {           'type': 'user',           'role': 'writer',           'emailAddress': email,         },         fields = 'id'       ).execute()      self._init_source_spreadsheet()     self._dump_config() <\/code><\/pre>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>_init_source_spreadsheet()<\/code> \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u043d\u043e\u0432\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0438 \u0434\u0430\u0441\u0442 \u0435\u0439 \u0438\u043c\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0432\u0443\u0445 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0439 \u043a API: sheets_service.spreadsheets().sheets().copyTo(&#8230;) \u0438 &#8230;, \u0430 \u0432\u043e\u0442 \u0442\u0443\u0442 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f batch():<\/p>\n<pre><code class=\"python\"># script\/lib\/GSheets.py    def _init_source_spreadsheet(self):     response = self.sheets_service.spreadsheets().sheets().copyTo(       spreadsheetId = SOURCE_SPREADSHEET,       sheetId = 0,       body = {         'destinationSpreadsheetId': self.spreadsheet_id,     }).execute()      self.main_sheet_id = response.get(\"sheetId\")     self.renameSheet(response.get(\"sheetId\"), TRACKED_SHEET_NAME)        def renameSheet(self, sheetId, newName):     return self.batch({       \"updateSheetProperties\": {         \"properties\": {           \"sheetId\": sheetId,           \"title\": newName,         },         \"fields\": \"title\",       }     })  <\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u043a\u043b\u0430\u0441\u0441\u0430 \u043d\u0430 \u043f\u043e\u0447\u0442\u0443 \u043f\u0440\u0438\u0434\u0435\u0442 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435 \u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043d\u0435\u043a\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u0430\u0432\u0442\u043e\u0440\u043e\u043c \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f &#8230;@&#8230; .iam.gserviceaccount.com. \u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u0432 \u043f\u0438\u0441\u044c\u043c\u0435 \u0438 \u0443\u0432\u0438\u0434\u0438\u043c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e7d\/51d\/60a\/e7d51d60ae1ef220714bacbf135f3b97.png\" alt=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a 3. \u041a\u043e\u043f\u0438\u044f \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\" title=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a 3. \u041a\u043e\u043f\u0438\u044f \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\" width=\"977\" height=\"1289\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e7d\/51d\/60a\/e7d51d60ae1ef220714bacbf135f3b97.png\"\/><figcaption>\u0420\u0438\u0441\u0443\u043d\u043e\u043a 3. \u041a\u043e\u043f\u0438\u044f \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442<\/figcaption><\/figure>\n<p>\u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u043d\u0430 \u0434\u0438\u0441\u043a, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0435\u0440\u0435\u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437:<\/p>\n<pre><code class=\"python\"># script\/lib\/GSheets.py      def _dump_config(self):     with open(os.path.join(SECRETS_DIRECTORY, CONFIG_FILENAME), 'w') as config_file:       json.dump({         \"spreadsheetId\": self.spreadsheet_id,         \"mainSheetId\":   self.main_sheet_id,         \"startPageToken\": self.start_page_token,         \"lastUpdate\": self.last_update,       }, config_file)<\/code><\/pre>\n<p>\u041d\u0430\u0443\u0447\u0438\u043c \u0441\u043a\u0440\u0438\u043f\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0438 \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b Google. \u0412 \u044d\u0442\u043e\u043c \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/developers.google.com\/sheets\/api\/reference\/rest\/v4\/spreadsheets.values\/get?hl=en\" rel=\"noopener noreferrer nofollow\">sheets_service.spreadsheets().values().get(&#8230;)<\/a>. \u041d\u043e\u0432\u0430\u044f \u0434\u043b\u044f \u043d\u0430\u0441 <a href=\"https:\/\/developers.google.com\/sheets\/api\/guides\/values?hl=en\" rel=\"noopener noreferrer nofollow\">\u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044f values<\/a>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0430\u044f\u0441\u044f \u0432 SheetsAPI \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0437\u0430 \u043e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u0434\u0440\u0443\u0433\u043e\u0439 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u0438 &#8212; range (\u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d) \u0432\u0438\u0434\u0430 A1:D4 &#8212; \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0435\u0433\u043e \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f \u043d\u0430 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0414\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 <a href=\"https:\/\/spreadsheets.values.batchGet\" rel=\"noopener noreferrer nofollow\">values.batchGet<\/a>.<\/p>\n<pre><code class=\"python\"># script\/lib\/GSheets.py    def get_chunk(self, start_idx):     '''     returns tuple:     0: idx to continue iteration     1: start row_idx in a google spreadsheet     2: end row_idx in a google spreadsheets     3: array of rows with values from START_COLUMN up to END_COLUMN     or NONE, if chunk is empty     '''     range_from_to = (start_idx + TRACKED_SHEET_HEADER_OFFSET + 1,                      start_idx + CHUNK_SIZE + TRACKED_SHEET_HEADER_OFFSET)     try:       response = self.sheets_service.spreadsheets().values().get(         spreadsheetId=self.spreadsheet_id,         range=f'{TRACKED_SHEET_NAME}!{START_COLUMN}{range_from_to[0]}:{END_COLUMN}{range_from_to[1]}'       ).execute()       return (start_idx + CHUNK_SIZE, *range_from_to, response.get(\"values\"))      except Exception as e:       print(f'GSheets error: unable to fetch {self.spreadsheet_id} table with Google Sheets API:', e)       return (start_idx + CHUNK_SIZE, *range_from_to, {})<\/code><\/pre>\n<p><a class=\"anchor\" name=\"stage-2-3\" id=\"stage-2-3\"><\/a><\/p>\n<h3>\u042d\u0442\u0430\u043f\u044b 2-4. \u0423\u0437\u043d\u0430\u0435\u043c \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \u0444\u0430\u0439\u043b\u0435<\/h3>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u043d\u0435\u0440\u0435\u0448\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 &#8212; \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.<\/p>\n<p>\u041a\u0430\u0437\u0443\u0441 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e SheetsAPI \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0443\u0437\u043d\u0430\u0442\u044c \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435, \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043f\u0440\u0438\u0447\u0435\u043c \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0439 \u0438 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f, \u0432 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0445 \u0435\u0441\u0442\u044c.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/afe\/963\/956\/afe963956691e488b1d15dd9d347a1b1.png\" alt=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a 4. \u0418\u0441\u0442\u043e\u0440\u0438\u044f \u0432\u0435\u0440\u0441\u0438\u0439 \u0432 \u0432\u0435\u0431-\u043a\u043b\u0438\u0435\u043d\u0442\u0435 GoogleSheets.\" title=\"\u0420\u0438\u0441\u0443\u043d\u043e\u043a 4. \u0418\u0441\u0442\u043e\u0440\u0438\u044f \u0432\u0435\u0440\u0441\u0438\u0439 \u0432 \u0432\u0435\u0431-\u043a\u043b\u0438\u0435\u043d\u0442\u0435 GoogleSheets.\" width=\"331\" height=\"352\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/afe\/963\/956\/afe963956691e488b1d15dd9d347a1b1.png\"\/><figcaption>\u0420\u0438\u0441\u0443\u043d\u043e\u043a 4. \u0418\u0441\u0442\u043e\u0440\u0438\u044f \u0432\u0435\u0440\u0441\u0438\u0439 \u0432 \u0432\u0435\u0431-\u043a\u043b\u0438\u0435\u043d\u0442\u0435 GoogleSheets.<\/figcaption><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f \u0444\u043e\u0440\u0443\u043c\u043e\u0432 \u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u0441\u0442\u0430\u043b\u043e \u044f\u0441\u043d\u043e, \u0447\u0442\u043e \u0443\u0437\u043d\u0430\u0442\u044c, \u0431\u044b\u043b\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0438\u043b\u0438 \u0438 \u043d\u0435\u0442, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043a\u0442\u043e \u0432\u043d\u043e\u0441\u0438\u043b \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f, \u043c\u043e\u0436\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 Google Drive API. \u041a\u0430\u043a\u0438\u0435 \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u0442\u0440\u043e\u043a\u0438 \u0431\u044b\u043b\u0438 \u0437\u0430\u0442\u0440\u043e\u043d\u0443\u0442\u044b, \u0443\u0437\u043d\u0430\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f. \u0422\u0443\u0442 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/developers.google.com\/drive\/api\/v2\/reference\/changes\/list\" rel=\"noopener noreferrer nofollow\">drive_service.changes().list(&#8230;)<\/a>. \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u0430 \u043d\u0430 Google \u0434\u0438\u0441\u043a\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0434\u043b\u044f \u043d\u0430\u0441, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 API, \u043a\u0430\u043a \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0440\u044f\u0434, \u0433\u0434\u0435 \u0432\u043c\u0435\u0441\u0442\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043c\u0435\u0442\u043a\u0438, \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c page_token. \u042d\u0442\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044e. \u041e\u0434\u043d\u0430\u043a\u043e, \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u043a API \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u0432\u0443\u0445 \u0432\u0438\u0434\u043e\u0432: \u043b\u0438\u0431\u043e \u0441 \u043f\u043e\u043b\u0435\u043c newStartPageToken, \u043b\u0438\u0431\u043e \u0441 \u043f\u043e\u043b\u0435\u043c nextPageToken.<\/p>\n<pre><code class=\"python\"># script\/lib\/GSheets.py    def check_changes(self):     self.page_token = self.start_page_token     were_changes_in_a_file = False     start_page_token = self.start_page_token      try:       while self.page_token is not None:          response = self.drive_service.changes().list(           pageToken=self.page_token,           spaces='drive',         ).execute()                  for change in response.get('changes'):           file_id =  change.get(\"fileId\")           # \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430, \u0447\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 - \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0430\u044f \u043d\u0430\u0441           # \u0442\u0430\u0431\u043b\u0438\u0446\u0430, \u0430 \u043d\u0435 \u0434\u0440\u0443\u0433\u043e\u0439 \u0444\u0430\u0439\u043b            if file_id == self.spreadsheet_id:             were_changes_in_a_file = True         if 'newStartPageToken' in response:           self.start_page_token = response.get('newStartPageToken')         self.page_token = response.get('nextPageToken')        # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0444\u0430\u043a\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f        if start_page_token != self.start_page_token:         self.last_update = f'{datetime.now():%X %d.%m.%Y}'         self._dump_config()          except Exception as e:       print(f'GSheets error: unable to fetch {self.spreadsheet_id} table with Google Sheets API:', e)     finally:       return were_changes_in_a_file<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u044f\u0441\u043d\u0438\u0442\u044c \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u044d\u0442\u043e\u0442 \u043a\u043e\u0434, \u043e\u0431\u0440\u0430\u0442\u0438\u043c\u0441\u044f \u043a \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043c\u0435\u0442\u043e\u0434\u0430 <a href=\"https:\/\/developers.google.com\/drive\/api\/v2\/reference\/changes\/list\" rel=\"noopener noreferrer nofollow\">drive_service.changes().list(&#8230;)<\/a>.<\/p>\n<blockquote>\n<p><strong>Request > parameters<\/strong><\/p>\n<p><code>pageToken<\/code> &#8212; <code>string<\/code> &#8212; The token for continuing a previous list request on the next page. This should be set to the value of &#8216;nextPageToken&#8217; from the previous response or to the response from the getStartPageToken method.<\/p>\n<p><strong>Response<\/strong><\/p>\n<p><code>nextPageToken<\/code> &#8212; <code>string<\/code> &#8212; The page token for the next page of changes. This will be absent if the end of the changes list has been reached. If the token is rejected for any reason, it should be discarded, and pagination should be restarted from the first page of results.<\/p>\n<p><code>newStartPageToken<\/code> &#8212; <code>string<\/code> &#8212; The starting page token for future changes. This will be present only if the end of the current changes list has been reached<\/p>\n<\/blockquote>\n<p>\u0418\u0442\u0430\u043a, \u0442\u0443\u0442 \u0441\u043a\u0430\u0437\u0430\u043d\u043e, \u0447\u0442\u043e \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u0440\u0430\u0437\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u041a\u0430\u0436\u0434\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446. \u041c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0435\u043a\u0438\u0439 pageToken \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c nextPageToken, \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443. \u0412 \u043e\u0442\u0432\u0435\u0442\u0435 \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441 \u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0435 nextPageToken \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c, \u0438 \u044d\u0442\u0430 \u0441\u0442\u0440\u043e\u0447\u043a\u0430 \u043a\u043e\u0434\u0430 <\/p>\n<pre><code class=\"python\">self.page_token = response.get('nextPageToken')<\/code><\/pre>\n<p>\u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442 \u0432 <code>self.page_token<\/code> \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>None<\/code> &#8212; \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u043c \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044e. \u042d\u0442\u0430 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043b\u0438\u0448\u044c \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f. \u041a\u0430\u043a \u0443\u0437\u043d\u0430\u0442\u044c, \u0447\u0442\u043e \u043f\u043e\u044f\u0432\u0438\u043b\u043e\u0441\u044c \u0435\u0449\u0435 \u043e\u0434\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435? \u0415\u0441\u043b\u0438 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043d\u043e\u0432\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435, \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0430\u043c \u043e\u0442\u0432\u0435\u0442 \u0441 \u043f\u043e\u043b\u0435\u043c newStartPageToken. \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u0438\u0442\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u044e \u043c\u043d\u043e\u0433\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u0443\u044e \u043a\u043d\u0438\u0433\u0443 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u043f\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0440\u0430\u0437, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 newStartPageToken \u043d\u0430 \u0434\u0438\u0441\u043a. \u0421\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u0445\u0440\u0430\u043d\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e:<\/p>\n<pre><code class=\"json\">{   \"spreadsheetId\": \"2Ds5A7z9AGs17B0xXBNCn_nSbE6zUitLvtLzrW3B05-w\",   \"mainSheetId\": 1886489919,   \"startPageToken\": \"129\",   \"lastUpdate\": \"02:25:10 12.07.2022\" }<\/code><\/pre>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438, \u043e\u0442\u043a\u0443\u0434\u0430 \u0431\u0440\u0430\u0442\u044c pageToken \u0434\u043b\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430? \u041d\u0430\u0434\u043e \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0435\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <a href=\"https:\/\/developers.google.com\/drive\/api\/v3\/reference\/changes\/getStartPageToken\" rel=\"noopener noreferrer nofollow\">drive_service.changes().getStartPageToken()<\/a>:<\/p>\n<pre><code class=\"python\">drive_service.changes().getStartPageToken().execute()<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u0432\u044b\u0437\u043e\u0432 \u043c\u044b \u0434\u0435\u043b\u0430\u043b\u0438 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>_create()<\/code> \u043a\u043b\u0430\u0441\u0441\u0430 <code>GSheets<\/code>, \u0435\u0441\u043b\u0438 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u043b\u0438 \u0442\u043e\u043a\u0435\u043d \u0432 \u0444\u0430\u0439\u043b\u0435 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435. <\/p>\n<p>\u041d\u0430\u0441 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442, \u043a\u0442\u043e \u0432\u043d\u0435\u0441 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f. \u0414\u043b\u044f \u043d\u0430\u0441 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u0430 &#8212; \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u0437\u043d\u0430\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0438\u043c\u0435\u043d\u043d\u043e \u0441\u0442\u0440\u043e\u043a\u0438 \u0431\u044b\u043b\u0438 \u0437\u0430\u0442\u0440\u043e\u043d\u0443\u0442\u044b &#8212; \u044d\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u043b\u043e \u0431\u044b \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 \u0441\u0435\u0442\u044c. \u041d\u043e \u0442\u0430\u043a \u043a\u0430\u043a API \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0442\u0430\u043a\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0440\u043e\u043b\u0438\u0441\u0442\u044b\u0432\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0432 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438.  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043e\u0442\u0432\u0435\u0442\u0430, \u043d\u0435\u0441\u0443\u0449\u0443\u044e \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e.<\/p>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u044d\u0442\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f ChangesResource \u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a (nextPageToken, newStartPageToken \u043e\u043f\u0443\u0449\u0435\u043d\u044b):<\/p>\n<pre><code class=\"json\">{   \"kind\": \"drive#change\",   \"type\": string,                    deprecated   \"changeType\": string,              file \u0438\u043b\u0438 drive   \"time\": datetime,   \"removed\": boolean,   \"fileId\": string,   \"file\": files Resource,   \"teamDriveId\": string,             deprecated   \"driveId\": string,   \"teamDrive\": teamdrives Resource,  deprecated   \"drive\": drives Resource } <\/code><\/pre>\n<p>\u041d\u0430 \u0434\u0435\u043b\u0435 \u043c\u044b \u043e\u0431\u044b\u0447\u043d\u043e\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043b\u0438\u0448\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043b\u044f. \u0427\u0430\u0441\u0442\u044c \u0438\u0437 \u043d\u0438\u0445 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u043c\u0438 JSON&#8217;\u0430\u043c\u0438. \u0412\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u044d\u0442\u043e:<\/p>\n<pre><code class=\"python\">self.drive_service.changes().list(pageToken=self.page_token,                                                    spaces='drive').execute()<\/code><\/pre>\n<p>\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 &#8212; \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 ChangesResource:<\/p>\n<pre><code class=\"json\">{'changes': [{'changeType': 'file',               'file': {'id': '1LuOz28wtbP5okLJ6nNwzuS03zynGYvwVw_9uaLF6n_U',                        'kind': 'drive#file',                        'mimeType': 'application\/vnd.google-apps.spreadsheet',                        'name': '\u041a\u043e\u043f\u0438\u044f \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430'},               'fileId': '1LuOz28wtbP5okLJ6nNwzuS03zynGYvwVw_9uaLF6n_U',               'kind': 'drive#change',               'removed': False,               'time': '2022-07-06T17:26:23.907Z',               'type': 'file'}],  'kind': 'drive#changeList',  'newStartPageToken': '54'} <\/code><\/pre>\n<p>\u041d\u0435 \u0431\u0443\u0434\u0435\u043c \u043d\u0438\u0447\u0435\u0433\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0442\u043e \u0434\u0435 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a API \u0435\u0449\u0435 \u0440\u0430\u0437:<\/p>\n<pre><code class=\"json\">{   'changes': [],   'kind': 'drive#changeList',   'newStartPageToken': '54' }<\/code><\/pre>\n<p>\u041f\u043e\u043c\u0435\u043d\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u043a\u043e\u0439-\u043d\u0438\u0431\u0443\u0434\u044c \u044f\u0447\u0435\u0439\u043a\u0438:<\/p>\n<pre><code class=\"json\">{'changes': [{'changeType': 'file',               'file': {'id': '1LuOz28wtbP5okLJ6nNwzuS03zynGYvwVw_9uaLF6n_U',                        'kind': 'drive#file',                        'mimeType': 'application\/vnd.google-apps.spreadsheet',                        'name': '\u041a\u043e\u043f\u0438\u044f \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430'},               'fileId': '1LuOz28wtbP5okLJ6nNwzuS03zynGYvwVw_9uaLF6n_U',               'kind': 'drive#change',               'removed': False,               'time': '2022-07-06T17:34:19.641Z',               'type': 'file'}],  'kind': 'drive#changeList',  'newStartPageToken': '56'}<\/code><\/pre>\n<p>newStartPageToken \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043d\u0430\u043c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e  \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u043b\u043e \u0432 \u0441\u0435\u0431\u044f \u043b\u0438\u0448\u044c \u043e\u0434\u043d\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435. \u041f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430\u0434\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c <code>{... \"pageToken\": \"56\" }<\/code>.<\/p>\n<p><a class=\"anchor\" name=\"stage-5\" id=\"stage-5\"><\/a><\/p>\n<h3>\u042d\u0442\u0430\u043f 5. \u041a\u043e\u043d\u0435\u0446 \u041f\u0443\u0442\u0438<\/h3>\n<p>\u0412\u043e\u0442, \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435, \u0438 \u0432\u0441\u0451, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u043c \u0434\u043b\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c main.py \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 GSheets, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043a SheetsAPI \u0438 Google Drive API, \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0430\u0441\u0441\u0430 CRB, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0431\u0435\u0440\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u0443\u0440\u0441\u0430 \u0434\u043e\u043b\u043b\u0430\u0440\u0430 \u0438 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0430\u0441\u0441\u0430 DBConnector, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u0437\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0421\u0423\u0411\u0414.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/apscheduler.readthedocs.io\/en\/3.x\/userguide.html\" rel=\"noopener noreferrer nofollow\">apscheduler  <\/a>\u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u044b\u0437\u043e\u0432 \u043a\u043e\u0440\u0443\u0442\u0438\u043d. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0430\u0440\u0443 \u043d\u043e\u0432\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u043a\u043e\u043d\u0444\u0438\u0433:<\/p>\n<pre><code class=\"python\"># script\/lib\/config.py  REQUEST_TIMEOUT = 200      # \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445 CRB_POLLING_INTERVAL = 1   # \u0432 \u0447\u0430\u0441\u0430\u0445 GS_POLLING_INTERVAL =  15  # \u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u043f\u0440\u043e\u0441\u0430 Google spreadsheet, \u0432 \u043c\u0438\u043d\u0443\u0442\u0430\u0445<\/code><\/pre>\n<pre><code class=\"python\"># script\/main.py  from lib.DBConnector import DB from lib.CRB import CRB from lib.GSheets import GSheets import asyncio from apscheduler.schedulers.asyncio import AsyncIOScheduler  from lib.config import CRB_POLLING_INTERVAL, GS_POLLING_INTERVAL   if __name__ == '__main__':   scheduler = AsyncIOScheduler()   sheet = GSheets()   print(sheet, '\\n')   crb = CRB()   with DB() as db:     db.update_cost_rub(crb.currency_rate)     print('USD currency rate:', crb.currency_rate, '\\n')     async def sheet_check_job():     print('[sheet_check_job] checking the spreadsheet')     if not sheet.check_changes():       print('[sheet_check_job] no changes')       return      with DB() as db:       print('[sheet_check_job] committing changes')       rows = ['']       idx = 0       while True:         idx, ss_start_row_idx, _, rows = sheet.get_chunk(idx)         if not rows:           break         for i, row in enumerate(rows):           # rows like [] or rows with spaces           if (len(row) &lt; 4):             continue           row.extend((ss_start_row_idx + i, 0))         db.upsert(rows)       print('[sheet_check_job] done!')    async def currency_rate_check_job():     with DB() as db:       db.update_cost_rub(crb.fetch_currency_rate())       print('[currency_rate_check_job] the USD-RUB currency rate has been updated:', crb.currency_rate)           scheduler.add_job(currency_rate_check_job, \"interval\", hours=CRB_POLLING_INTERVAL)   scheduler.add_job(sheet_check_job, \"interval\", minutes=GS_POLLING_INTERVAL)   scheduler.start()    print('Press Ctrl+C to exit\\n')    try:     asyncio.get_event_loop().run_forever()   except (KeyboardInterrupt, SystemExit):     scheduler.shutdown()     asyncio.get_event_loop().stop() <\/code><\/pre>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e, \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043e\u0441\u043e\u0431\u043e\u0439 \u043c\u0430\u0433\u0438\u0438 \u0442\u0443\u0442 \u043d\u0435\u0442, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 <a href=\"https:\/\/habr.com\/ru\/post\/266743\/\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u0440\u0443\u0442\u0438\u043d\u0430\u043c\u0438 \u0438\u0437 Python 3.5<\/a>. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435, \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c, \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430. \u0414\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432, \u043d\u0430\u0434\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c eventloop. \u042f \u0434\u0435\u043b\u0430\u044e \u044d\u0442\u043e \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 Ctrl + C. \u041c\u0435\u0436\u0434\u0443 Ctrl + C \u043d\u0430 Windows \u0438 Linux <a href=\"https:\/\/docs.microsoft.com\/ru-ru\/windows\/console\/ctrl-c-and-ctrl-break-signals\" rel=\"noopener noreferrer nofollow\">\u0435\u0441\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u0438\u0435<\/a>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 2 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u0432\u044b\u0437\u0432\u0430\u043d\u043d\u044b\u0435 \u043a\u0430\u043a \u0441\u0438\u0433\u043d\u0430\u043b\u043e\u043c SIGINT, \u0442\u0430\u043a \u0438 \u0441\u0438\u0433\u043d\u0430\u043b\u043e\u043c SIGBREAK. \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u0435\u0449\u0435 \u0441 \u0442\u0435\u043c, \u0447\u0442\u043e \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u0437\u0430\u0434\u0430\u0447\u0443 \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 eventloop, \u043f\u043e\u044d\u0442\u043e\u043c\u0443, \u0435\u0441\u043b\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u044b currency_rate_check_job \u0438 sheet_check_job \u043f\u0435\u0440\u0435\u0441\u0435\u043a\u0443\u0442\u0441\u044f, \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0430 \u0437\u0430\u0434\u0430\u0447\u0430, \u0430 \u0432\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043b\u043e\u0436\u0435\u043d\u0430 \u043d\u0430 \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0441\u0432\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b.<\/p>\n<p><a class=\"anchor\" name=\"conclusion\" id=\"conclusion\"><\/a><\/p>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u0420\u0435\u0441\u043f\u0435\u043a\u0442 \u0432\u0441\u0435\u043c, \u043a\u0442\u043e \u043f\u0440\u043e\u0447\u0435\u043b \u0434\u043e \u043a\u043e\u043d\u0446\u0430.<\/p>\n<p>Google Drive &#8212; \u043e\u0447\u0435\u043d\u044c \u043a\u043b\u0430\u0441\u0441\u043d\u044b\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442, \u0430 \u0435\u0433\u043e API \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0440\u0443\u0442\u0438\u043d\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447, \u043f\u0440\u0438\u0447\u0435\u043c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e, \u0447\u0442\u043e \u043d\u0435\u043c\u0430\u043b\u043e\u0432\u0430\u0436\u043d\u043e. \u041c\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044e \u0444\u0430\u0439\u043b\u043e\u0432 \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430\u0445 \u0447\u0435\u0440\u0435\u0437 \u043e\u0431\u043b\u0430\u043a\u043e, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0431\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u0440\u0430\u0437\u043d\u044b\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u0432 \u043e\u0434\u043d\u0443 \u0442\u0430\u0431\u043b\u0438\u0446\u0443, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u043b\u0430\u043a\u043e \u043a\u0430\u043a \u044f\u0434\u0440\u043e ETL-\u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0443 \u0432\u0430\u0436\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 \u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c. \u0423\u0432\u0435\u0440\u0435\u043d, \u0412\u044b \u043d\u0430\u0439\u0434\u0435\u0442\u0435 \u0435\u0449\u0435 \u043c\u0430\u0441\u0441\u0443 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0439 Google-\u0434\u0438\u0441\u043a\u0443 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0430\u043c\u044b\u0445 \u0440\u0430\u0437\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u044b\u0445 \u043e\u0444\u0438\u0441\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<p>\u041d\u0430\u043f\u043e\u043c\u043d\u044e, \u0447\u0442\u043e \u043a\u043e\u0434 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432\u0441\u0435\u043c \u0436\u0435\u043b\u0430\u044e\u0449\u0438\u043c <a href=\"https:\/\/gitlab.com\/Denactive\/test-task-numbers\" rel=\"noopener noreferrer nofollow\">\u0432 \u044d\u0442\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u043d\u0430 GitLab<\/a>.<\/p>\n<p>\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u043e\u0442\u0437\u044b\u0432\u044b! \u0415\u0441\u043b\u0438 \u0431\u0443\u0434\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445, \u0432\u044b\u043f\u0443\u0449\u0443 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435, \u0433\u0434\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0435\u0431-\u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u043e\u0434 \u044d\u0442\u043e \u0431\u0435\u0437\u043e\u0431\u0440\u0430\u0437\u0438\u0435 \u0441 \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c \u043d\u0430 Python Django \u0438 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u043e\u043c \u043d\u0430 JS React.<\/p>\n<p>\u0414\u043e \u0441\u0432\u044f\u0437\u0438!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/681994\/\"> https:\/\/habr.com\/ru\/post\/681994\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0414\u043e\u0431\u0440\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u0443\u0442\u043e\u043a, \u0445\u0430\u0431\u0440\u043e\u043d\u0430\u0432\u0442\u044b. \u041f\u043e\u0434\u0435\u043b\u044e\u0441\u044c \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 Google Drive API, \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0435\u0435, \u0441 SheetsAPI &#8212; API \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446.<\/p>\n<p><strong>\u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0441\u043b\u043e\u0432\u0430<\/strong>: <em>Google Sheets API, Google Drive API, Python, PostgreSQL, apscheduler, googleapiclient, oauth2client, psycopg2<\/em>.<\/p>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043a\u0435\u0439\u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f  Google Sheets API \u0438 Google Drive API. \u0417\u0430\u0434\u0430\u0447\u0430 \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0430 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0438  \u043a\u043e\u0434\u0430 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043e \u043d\u0435 \u043c\u0430\u043b\u043e (\u0435\u0433\u043e \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 <a href=\"https:\/\/gitlab.com\/Denactive\/test-task-numbers\" rel=\"noopener noreferrer nofollow\">\u0432 \u044d\u0442\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 GitLab<\/a>).<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0412\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 Google \u0438\u043b\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u044f Google-\u0442\u0430\u0431\u043b\u0438\u0446, \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c <a href=\"https:\/\/habr.com\/ru\/post\/483302\/\" rel=\"noopener noreferrer nofollow\">\u043a \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a>;<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0412\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0430 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u043d\u0430 Google-\u0434\u0438\u0441\u043a\u0435, \u043f\u0440\u043e\u043c\u0430\u0442\u044b\u0432\u0430\u0439\u0442\u0435 \u043d\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a &#171;<a href=\"#stage-2-3\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f\u044b 2-4. \u0423\u0437\u043d\u0430\u0435\u043c \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \u0444\u0430\u0439\u043b\u0435.<\/a>&#171;<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0412\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 SheetsAPI \u0432 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0441\u0435, \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0447\u0430\u0441\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e\u0434 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u043c &#171;<a href=\"#stage-2-2\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f\u044b 2-4. \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Sheets API.<\/a>&#171;;<\/p>\n<p>\u0422\u0435\u043c, \u043a\u0442\u043e \u0437\u0430\u0448\u0435\u043b \u0441\u044e\u0434\u0430, \u0436\u0435\u043b\u0430\u044f \u043f\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 PostreSQL \u0447\u0435\u0440\u0435\u0437 Python, \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0440\u0430\u0437\u0434\u0435\u043b &#171;<a href=\"#stage-1\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f 1. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a PostreSQL \u0438\u0437 Python.<\/a>&#171;.<\/p>\n<p>\u041f\u0440\u0438\u044f\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0447\u0442\u0435\u043d\u0438\u044f!<\/p>\n<h3>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h3>\n<ul>\n<li>\n<p><a href=\"#about-google-api\" rel=\"noopener noreferrer nofollow\">\u041f\u0430\u0440\u0430 \u0441\u043b\u043e\u0432 \u043f\u0440\u043e Google API<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#task\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#db_creation\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0411\u0414<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#configs\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f. \u041a\u043e\u043d\u0444\u0438\u0433\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-1\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f 1. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a PostreSQL \u0438\u0437 Python<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-2-1\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f 2. \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0443\u0440\u0441\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-2-2\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f\u044b 2-4. \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Sheets API<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-2-3\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f\u044b 2-4. \u0423\u0437\u043d\u0430\u0435\u043c \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \u0444\u0430\u0439\u043b\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#stage-5\" rel=\"noopener noreferrer nofollow\">\u042d\u0442\u0430\u043f 5. \u041a\u043e\u043d\u0435\u0446 \u043f\u0443\u0442\u0438<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"#conclusion\" rel=\"noopener noreferrer nofollow\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u0435\u043c \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e Google Drive &#8212; \u0432\u0441\u0435\u043c\u0438\u0440\u043d\u043e \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442 \u0432\u0441\u0435\u043c\u0438\u0440\u043d\u043e \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e\u0439 Alphabet. \u041e\u043d\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u043e\u043c \u043e\u0444\u0438\u0441\u043d\u044b\u0445 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435, \u043a\u0430\u043a \u044f \u0441\u0447\u0438\u0442\u0430\u044e, \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c MS Office \u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0443\u0434\u043e\u0431\u043d\u044b \u0434\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0434 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0432 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0433\u0440\u0443\u043f\u043f\u0430\u0445. \u041b\u0438\u0447\u043d\u043e \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e Google Drive \u0434\u043b\u044f \u0431\u044d\u043a\u0430\u043f\u0430 \u043b\u0438\u0447\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432. \u0410 \u043a\u0430\u043a \u043d\u0430\u0441\u0447\u0435\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 &#8212; \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0437 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0445 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445? \u042d\u0442\u0430\u043f\u044b \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043c\u044b \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u0435\u043c \u0432\u043c\u0435\u0441\u0442\u0435 \u0432 \u044d\u0442\u043e\u0439 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<p><a class=\"anchor\" name=\"about-google-api\" id=\"about-google-api\"><\/a><\/p>\n<h3>\u041f\u0430\u0440\u0430 \u0441\u043b\u043e\u0432 \u043f\u0440\u043e Google API<\/h3>\n<p>\u041d\u0435 \u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0432 \u0441\u0442\u043e\u043b\u044c \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442, \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f-\u0442\u0430\u043a\u0438 \u043f\u043e\u0434\u0435\u043b\u0438\u043b\u0430\u0441\u044c \u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c\u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e API. \u0414\u0430\u0436\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0435 \u044f\u0437\u044b\u043a\u0438 \u0438 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043a\u043e\u0434\u0430 \u043d\u0430 Python, Java, NodeJS, Go. PHP, C#, Ruby. \u041d\u043e \u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0442\u0430\u043c \u043d\u0435 \u043c\u0430\u043b\u043e.<\/p>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435, \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043a\u043e\u0434\u0435 \u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442 \u043d\u0430 Python, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 API \u0434\u043b\u044f Google-\u0442\u0430\u0431\u043b\u0438\u0446 &#8212; <a href=\"https:\/\/developers.google.com\/sheets\/api\/reference\/rest\" rel=\"noopener noreferrer nofollow\">SheetsAPI<\/a> &#8212; \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0439, \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c, \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<p>\u0421\u043a\u0440\u0438\u043f\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 Google Drive \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0445 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u043e\u0432 Google &#8212; <a href=\"https:\/\/cloud.google.com\/iam\/docs\/service-accounts\" rel=\"noopener noreferrer nofollow\">Google Service Accounts<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043e\u0442 \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0445 \u043d\u0430\u043c \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0415\u0441\u043b\u0438 \u043a\u0440\u0430\u0442\u043a\u043e, \u0442\u043e \u044d\u0442\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 Google Drive. \u0423 \u043d\u0435\u0435 \u0441\u0432\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c credentials (\u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445) &#8212; \u043d\u0430\u0431\u043e\u0440\u0443 \u043a\u043b\u044e\u0447\u0435\u0439 \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0438 \u0441\u0432\u043e\u0435 \u0434\u0438\u0441\u043a\u043e\u0432\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e. \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u043e \u0432\u0441\u0435\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u043e\u043c <a href=\"https:\/\/habr.com\/ru\/company\/vk\/blog\/115163\/\" rel=\"noopener noreferrer nofollow\">OAuth 2.0<\/a>.<\/p>\n<p>\u0421\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0435 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u044b &#8212; \u0443\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438 Google. \u041e\u043d\u0438 \u043d\u0443\u0436\u043d\u044b \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Google Drive \u0438 Google Sheets, \u043d\u043e \u0438 Google BigQuery, Google Cloud \u0438 \u043f\u0440\u043e\u0447\u0438\u043c\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c\u0438 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430. \u041f\u0440\u043e \u0441\u0445\u0435\u043c\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 \u043f\u0440\u043e\u0448\u0443 <a href=\"https:\/\/habr.com\/ru\/post\/483302\/\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u043e\u0447\u0435\u0441\u0442\u044c \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u0438\u043b\u044c\u043d\u043e \u043c\u043d\u0435 \u043f\u043e\u043c\u043e\u0433\u043b\u0430, \u0438 \u0430\u0432\u0442\u043e\u0440\u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u0435\u043d. <\/p>\n<p><a class=\"anchor\" name=\"task\" id=\"task\"><\/a><\/p>\n<h2>\u041f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0437\u0430\u0434\u0430\u0447\u0438<\/h2>\n<p>\u0423\u0442\u043e\u0447\u043d\u044e \u0446\u0435\u043b\u044c: \u0435\u0441\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043e \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0430\u0445:<\/p>\n<figure class=\"\"><figcaption>\u0420\u0438\u0441\u0443\u043d\u043e\u043a 1. \u0412\u0438\u0434 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b.<\/figcaption><\/figure>\n<p>\u0412 \u043d\u0435\u0439: \u043f\u043e\u0440\u044f\u0434\u043a\u043e\u0432\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 &#8212; \u0447\u0438\u0441\u043b\u043e, \u0438\u043d\u0434\u0435\u043a\u0441 \u0437\u0430\u043a\u0430\u0437\u0430 &#8212; \u0447\u0438\u0441\u043b\u043e, \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0432 \u0434\u043e\u043b\u043b\u0430\u0440\u0430\u0445 \u0421\u0428\u0410, \u0434\u0430\u0442\u0430 \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438 &#8212; \u0434\u0430\u0442\u0430 \u0432 <a href=\"https:\/\/tetran.ru\/SiteContent\/Details\/25\" rel=\"noopener noreferrer nofollow\">\u0440\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0434\u0430\u0442\u044b <\/a>(\u0432\u0441\u0435\u0433\u0434\u0430 \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0444\u043e\u0440\u043c\u0430\u0442\u044b \u0434\u0430\u0442\u044b \u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0442. \u043a. \u044d\u0442\u043e \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u0435\u0439 \u0441 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0438 \u0438\u043d\u0442\u0435\u0440\u043f\u0440\u0435\u0442\u0430\u0446\u0438\u0435\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0431\u0430\u0437\u0435). \u041d\u0443\u0436\u043d\u043e \u0432\u0437\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u0443\u0440\u0441\u0430 \u043f\u0430\u0440\u044b RUB-USD \u0441 <a href=\"https:\/\/www.cbr.ru\/development\/SXML\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0430\u0439\u0442\u0430 \u0426\u0411 \u0420\u0424<\/a> \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f, \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0441 \u043e\u0431\u043b\u0430\u043a\u0430 \u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432 \u0411\u0414 PostreSQL, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u043a\u043e\u043b\u043e\u043d\u043a\u0443 &#171;\u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c \u0432  \u0440\u0443\u0431\u043b\u044f\u0445&#187;. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443.<\/p>\n<p>\u041a\u0430\u043a\u0438\u0435 \u0435\u0449\u0435 \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u0441\u0442\u043e\u0438\u0442 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0411\u0414? \u0422.\u043a. \u043d\u043e\u043c\u0435\u0440\u0443 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0434\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043d\u0435 \u0441\u0442\u043e\u0438\u0442, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0433\u043e \u0437\u0430\u043d\u043e\u0441\u0438\u0442 \u0447\u0435\u043b\u043e\u0432\u0435\u043a, \u043a\u043e\u043b\u043e\u043d\u043a\u0443 \u0441 \u043d\u043e\u043c\u0435\u0440\u043e\u043c \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435. \u0421\u0442\u0440\u043e\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u043d\u0434\u0435\u043a\u0441 \u0441\u0442\u0440\u043e\u043a\u0438 \u0442\u043e\u0436\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043c. \u0422.\u043a. \u043c\u044b \u0434\u043b\u044f \u0447\u0435\u0433\u043e-\u0442\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432\u0441\u0451 \u0432 \u0441\u0432\u043e\u044e \u0411\u0414, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u0441\u0432\u043e\u0439 \u043a\u043b\u0438\u0435\u043d\u0442, \u0438 \u0442.\u043a. \u043c\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043b\u043e\u043d\u043a\u0443 updated_at \u0441 \u0434\u0430\u0442\u043e\u0439 \u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c (!) \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f. PostreSQL \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u043e\u043d\u043a\u0443 created_at, \u0442\u0430\u043a \u0447\u0442\u043e \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0445\u043e\u0440\u043e\u0448\u0438\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c. \u041e\u0431\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u0434\u043b\u044f PostgreSQL \u0442\u0438\u043f\u0430 \u0434\u0430\u0442\u044b-\u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0431\u0435\u0437 \u0443\u0447\u0435\u0442\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u044f\u0441\u043e\u0432 <a href=\"https:\/\/www.postgresql.org\/docs\/current\/datatype-datetime.html\" rel=\"noopener noreferrer nofollow\"> timestamp<\/a> (PostreSQL \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/ISO_8601\" rel=\"noopener noreferrer nofollow\">ISO 8601<\/a>).<\/p>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0440\u0443\u044e \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u044d\u0442\u0430\u043f\u044b:<\/p>\n<ul>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0411\u0414 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u043d\u0435\u0439 \u0438\u0437 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 Python.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0443\u0441\u0442\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u043b\u0438\u0441\u0442 \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b-\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043d\u043e\u0432\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443. \u0412\u043f\u0440\u043e\u0447\u0435\u043c, \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0441\u0440\u0430\u0437\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u043e\u043c, \u043d\u043e \u0442\u0430\u043a \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0432\u0435\u0440\u043d\u043e? \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u043c\u0443 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0443 \u0435\u0449\u0435 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0434\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 \/ \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0438 \u043f\u0440\u0435\u0436\u0434\u0435, \u0447\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 &#171;\u0431\u043e\u0435\u0432\u044b\u043c&#187; \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c, \u0440\u0430\u0437\u0443\u043c\u043d\u043e \u043d\u0430\u043b\u0430\u0434\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u043d\u0430 \u043a\u043e\u043f\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0434\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0441\u0432\u043e\u0435\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c\u0443 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0443;<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c Google-\u0434\u0438\u0441\u043a \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0432 \u0444\u0430\u0439\u043b\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0435\u0441\u0442\u044c, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0443 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0432 \u0411\u0414.<\/p>\n<\/li>\n<\/ul>\n<p>\u041e\u0442\u043c\u0435\u0447\u0443, \u0447\u0442\u043e \u0437\u0430\u0434\u0430\u0447\u0443 \u0432\u044b\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c, \u043d\u0435 \u0441\u0442\u0430\u0432\u043b\u044e. \u0418 \u043d\u0435\u0442, SheetAPI \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0445\u043e\u0442\u044f \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u0438\u0435\u043d\u0442 Google Sheets \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432\u0438\u0434\u043d\u044b, \u0430 \u0437\u043d\u0430\u0447\u0438\u0442 Google \u0438\u0445 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 \u0438 \u0445\u0440\u0430\u043d\u0438\u0442. \u0422\u0430\u043a\u0438\u0445 \u0434\u0435\u0442\u0430\u043b\u0435\u0439 \u0432 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 Sheets API \u0431\u0443\u0434\u0435\u0442 \u0435\u0449\u0435 \u043c\u043d\u043e\u0433\u043e, \u0438 \u043e \u043a\u0430\u0436\u0434\u043e\u0439 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0434\u0430\u043b\u0435\u0435.<\/p>\n<p>\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u0443\u0434\u0443 \u0447\u0430\u043d\u043a\u0430\u043c\u0438 (chunks) &#8212; \u043f\u043e\u0440\u0446\u0438\u044f\u043c\u0438 \u043f\u043e N \u0441\u0442\u0440\u043e\u043a. \u0422\u0430\u043a \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0431\u043e\u0435\u0432\u044b\u043c\u0438 \u0411\u0414, \u0432\u0435\u0434\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u0432 \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435 \u043d\u0435 \u0431\u0435\u0437\u0433\u0440\u0430\u043d\u0438\u0447\u043d\u043e, \u0430 \u043a\u0430\u0436\u0434\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u0432\u0435\u0441\u0438\u0442\u044c \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442\u044b. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441\u043d\u0438\u0437\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u043b\u043b\u0438\u0437\u0438\u0439 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0411\u0414 \u0432 \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435, \u0445\u043e\u0442\u044f \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435, \u0447\u0442\u043e \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u0441\u0442\u043e \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u044d\u0442\u043e \u043d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442. \u041e\u0434\u043d\u0430\u043a\u043e, \u0442\u0435\u043c\u0430 \u044d\u0442\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f. \u0421\u043f\u0440\u0430\u0432\u0435\u0434\u043b\u0438\u0432\u043e\u0439 \u0431\u0443\u0434\u0435\u0442 \u043a\u0440\u0438\u0442\u0438\u043a\u0430: &#171;\u0414\u0430 \u043a\u0442\u043e \u0436\u0435 \u0445\u0440\u0430\u043d\u0438\u0442 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u044b \u0441\u0442\u0440\u043e\u043a \u0432 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u0435!?&#187; &#8212; \u044d\u0442\u043e \u0432\u0435\u0440\u043d\u043e: \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u043d\u043e\u0432\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0442\u043e\u043b\u044c\u043a\u043e 1000 \u0441\u0442\u0440\u043e\u043a, \u043d\u043e \u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c, \u0430 \u0432 \u044f\u0447\u0435\u0439\u043a\u0430\u0445 \u0441\u0442\u0440\u043e\u043a\u0438 \u043c\u043e\u0436\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u0435\u043a\u0441\u0442\u0430. \u0412 \u043e\u0431\u0449\u0435\u043c, \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u0441\u0442\u043e \u0431\u044b\u0442\u044c \u0434\u0430\u043b\u0435\u0435, \u0430 \u0447\u0438\u0441\u043b\u043e \u0441\u0442\u0440\u043e\u043a, \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445 \u0437\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0446\u0435\u043b\u0435\u0441\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u043e \u043f\u043e\u0434\u0431\u0438\u0440\u0430\u0442\u044c \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0434\u0430\u043d\u043d\u044b\u0445, \u0445\u0440\u0430\u043d\u044f\u0449\u0438\u0445\u0441\u044f \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435, \u0438 \u0435\u0441\u043b\u0438 \u0412\u044b \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u043d\u0438\u0436\u0435 \u0432 \u0440\u0430\u0431\u043e\u0442\u0435, \u0442\u043e \u0432\u0441\u0435\u0433\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0441\u0435\u0442\u044c, \u043f\u043e\u0434\u043e\u0431\u0440\u0430\u0442\u044c \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0412\u0430\u0448\u0438\u0445 \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u0435\u0432.<\/p>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043b\u044e\u0447\u0430\u043c\u0438, \u043f\u0430\u0440\u043e\u043b\u044f\u043c\u0438 \u0438 \u0442.\u043f. \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430. \u0411\u0443\u0434\u0443 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0445 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0435 secret\/ \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 .json. \u0420\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0438\u043c\u0438 \u0431\u0443\u0434\u0435\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0434\u0443\u043b\u0435\u0439 os, json.<\/p>\n<p><a class=\"anchor\" name=\"db_creation\" id=\"db_creation\"><\/a><\/p>\n<h3>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0411\u0414<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0411\u0414 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0443. \u042f \u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0441\u044c \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c <a href=\"https:\/\/www.jetbrains.com\/ru-ru\/datagrip\/\" rel=\"noopener noreferrer nofollow\">JetBrains Datagrip<\/a>. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0441\u0430\u043c PostgreSQL \u0443\u0436\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e\u0442 \u0438\u043c\u0435\u043d\u0438 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<pre><code class=\"bash\">psql -U postgres<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0412\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\u0435 \u043d\u0430 Windows, \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u043a\u043e\u043d\u0441\u043e\u043b\u044c psql. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u043d\u0430 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044f \u0432 <em>C:\\Program Files\\PostgreSQL\\14\\scripts\\runpsql.bat<\/em>, \u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 \u044f\u0440\u043b\u044b\u043a <em>C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\PostgreSQL 14\\(psql).lnk<\/em>.<\/p>\n<p>\u0412\u0432\u043e\u0434\u0438\u043c \u043b\u043e\u0433\u0438\u043d postres, \u043f\u0430\u0440\u043e\u043b\u044c \u043e\u0442 \u0412\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435, \u043f\u043e\u0440\u0442 \u0438 \u0445\u043e\u0441\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: localhost:5432.<\/p>\n<pre><code class=\"sql\">CREATE USER user_example WITH password '123456'; CREATE DATABASE example_db OWNER user_example; # \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u044b\u0434\u0430\u0442\u044c \u043f\u0440\u0438\u0432\u0435\u043b\u0435\u0433\u0438\u0438: GRANT ALL PRIVILEGES ON DATABASE example_db TO &lt;yourname>;<\/code><\/pre>\n<p>\u0412\u044b\u0445\u043e\u0434\u0438\u043c (<code>exit;<\/code>), \u0437\u0430\u0445\u043e\u0434\u0438\u043c \u0432 DataGrip \u0424\u0430\u0439\u043b -> \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442, (+) -> \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a -> PostgreSQL -> \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043f\u043e\u043b\u044f, \u043a\u0430\u043a \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0438\u0436\u0435:<\/p>\n<figure class=\"full-width\"><figcaption>\u0420\u0438\u0441\u0443\u043d\u043e\u043a 2. \u041f\u0440\u043e\u0435\u043a\u0442 Datagrip<\/figcaption><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432\u0432\u043e\u0434\u0438\u043c \u0432\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c:<\/p>\n<pre><code class=\"sql\">-- \u0422\u0440\u0438\u0433\u0433\u0435\u0440 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435  CREATE     OR REPLACE FUNCTION trigger_set_timestamp()     RETURNS TRIGGER AS $$ BEGIN     NEW.updated_at         = NOW();     RETURN NEW; END; $$     LANGUAGE plpgsql;  -- \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0414\u0430\u0442\u0443 \u0438\u0437 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0445\u0440\u0430\u043d\u044e \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 delivery_date: \u0442\u0430\u043a \u043f\u0440\u043e\u0449\u0435 CREATE TABLE Orders (     id                        SERIAL                    NOT NULL PRIMARY KEY,     table_row_index           INTEGER                   NOT NULL UNIQUE,     -- \u043d\u043e\u043c\u0435\u0440 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 \u0430\u0434\u0440\u0435\u0441\u0435 \u0432\u0438\u0434\u0430 A1:A50     table_row_number          INTEGER                   NOT NULL,     order_number              INTEGER                   NOT NULL UNIQUE,     cost_usd                  NUMERIC(12, 2)            NOT NULL,     cost_rub                  NUMERIC(12, 2)            NOT NULL,     delivery_date             CHAR(10)                  NOT NULL,     created_at                TIMESTAMPTZ               NOT NULL DEFAULT NOW(),     updated_at                TIMESTAMPTZ               NOT NULL DEFAULT NOW() );  -- \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0442\u0440\u0438\u0433\u0433\u0435\u0440 CREATE TRIGGER set_timestamp     BEFORE UPDATE     ON Orders     FOR EACH ROW EXECUTE PROCEDURE trigger_set_timestamp(); <\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e delivery_date &#8212; \u0434\u0430\u0442\u0430 \u0438\u0437 Google-\u0442\u0430\u0431\u043b\u0438\u0446\u044b &#8212; \u0438\u043c\u0435\u0435\u0442 \u0442\u0438\u043f \u0441\u0442\u0440\u043e\u043a\u0438, \u0430 \u0434\u0435\u043d\u0435\u0436\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f NUMERIC(12, 2). \u0421\u043e \u0441\u0442\u0440\u043e\u043a\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0449\u0435, \u0438\u043d\u0430\u0447\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u043b\u043e\u0441\u044c \u0431\u044b \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 \u0434\u0430\u0442\u043e\u0439 \u0438\u0437 \u043e\u0431\u043b\u0430\u043a\u0430 \u0432 ISO 8601, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u0438\u0445\u0430\u0442\u044c \u0435\u0435 \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0421 \u0434\u0435\u043d\u0435\u0436\u043d\u044b\u043c\u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u043c\u0438 \u0442\u043e\u0436\u0435 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e, \u043d\u043e \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0441 \u0434\u0432\u0443\u043c\u044f \u0446\u0438\u0444\u0440\u0430\u043c\u0438 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439, \u043e\u0434\u043d\u0430\u043a\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0447\u0438\u0441\u043b\u043e, \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430:<\/p>\n<pre><code class=\"sql\">INSERT INTO Orders(   table_row_index,   table_row_number,   order_number,   cost_rub,   cost_usd,   delivery_date ) VALUES (   57, 1, 1342,   300::NUMERIC(12, 2),   -- \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430   18000::NUMERIC(12, 2), -- \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430   '04.07.2022'::char(10) -- \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 );<\/code><\/pre>\n<p>\u041f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u043e\u0431\u043b\u0430\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043b\u0438 \u0434\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u0443\u0436\u0435 \u0432 \u0411\u0414. \u041e\u0431\u0449\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0445\u043e\u0434, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u044e\u0449\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432\u0441\u0442\u0430\u0432\u043a\u0438 insert \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f update. \u041f\u0440\u0438\u0447\u0435\u043c \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u043f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438\u0437 Python!<\/p>\n<pre><code class=\"sql\">CREATE     OR REPLACE FUNCTION upsert_orders(         arg_table_row_index INTEGER,         arg_table_row_number INTEGER,         arg_order_number INTEGER,      <\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-336886","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/336886","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=336886"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/336886\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=336886"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=336886"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=336886"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}