{"action":"created","comment":{"url":"https://api.github.com/repos/Restream/obeder/pulls/comments/110873587","pull_request_review_id":32062309,"id":110873587,"diff_hunk":"@@ -0,0 +1,230 @@\n+class MonthlyReportService\n+  def initialize(date)\n+    @date = date\n+    @users = User.order(:name)\n+    menus = Menu.in_date_range(@date.beginning_of_month, @date.end_of_month).order(:date)\n+    @user_menus = UserMenu.joins(:menu).where(menu: menus, neem: false)\n+      .pluck(:'menus.date', :'user_id')\n+  end\n+\n+  class << self\n+    def export(month, year)\n+      date = self.date_from_params(month.to_i, year.to_i)\n+      instance = self.new(date)\n+      instance.export_to_xlsx_stream\n+    end\n+\n+    def filename(month, year)\n+      \"monthly_report_#{month}_#{year}.xlsx\"\n+    end\n+\n+    def type\n+      'application/vnd.ms-excel'\n+    end\n+\n+    def date_from_params(month, year)\n+      \"#{1}-#{month}-#{year}\".to_date\n+    end\n+  end\n+\n+  def export_to_xlsx_stream\n+    package = Axlsx::Package.new\n+    workbook = package.workbook\n+\n+    workbook_styles = {}\n+    styles.each { |name, style| workbook_styles[name] = workbook.styles.add_style(style) }\n+\n+    workbook.add_worksheet(name: month_comma_year(@date)) do |sheet|\n+      last_cell = report_width - 1\n+\n+      sheet.add_row(report_title, style: workbook_styles[:title])\n+      sheet.merge_cells(sheet.rows.last.cells[(0..last_cell)])\n+      sheet.add_row(report_upload_date, style: workbook_styles[:uploaded])\n+      sheet.add_row()\n+\n+      sheet.add_row(report_table_header,\n+        style: table_header_styles.map { |style| workbook_styles[style] })\n+      @users.each do |user|\n+        sheet.add_row(report_table_user_info(user),\n+          style: table_user_styles.map { |style| workbook_styles[style] })\n+      end\n+\n+      sheet.add_row(report_table_total, style: total_styles.map { |style| workbook_styles[style] })\n+\n+      sheet.column_widths *column_widths\n+    end\n+\n+    package.to_stream.read\n+  end\n+\n+  private\n+\n+  def styles\n+    {\n+      title: {\n+        font_name: 'Arial',\n+        sz: 22,\n+        b: true,\n+        alignment: { horizontal: :center }\n+      },\n+      uploaded: {\n+        font_name: 'Arial',\n+        sz: 9,\n+        b: false\n+      },\n+      header: {\n+        border: { color: 'FF000000', style: :medium },\n+        font_name: 'Arial',\n+        sz: 11,\n+        b: true,\n+        alignment: { horizontal: :center }\n+      },\n+      header_weekend: {\n+        border: { color: 'FF000000', style: :medium },\n+        font_name: 'Arial',\n+        sz: 11,\n+        b: true,\n+        alignment: { horizontal: :center },\n+        bg_color: '00ea829f'\n+      },\n+      name_cell: {\n+        border: { color: 'FF000000', style: :thin },\n+        font_name: 'Arial',\n+        sz: 11,\n+        b: false,\n+        alignment: { horizontal: :left }\n+      },\n+      default_cell: {\n+        border: { color: 'FF000000', style: :thin },\n+        font_name: 'Arial',\n+        sz: 11,\n+        b: false,\n+        alignment: { horizontal: :center }\n+      },\n+      weekend_cell: {\n+        border: { color: 'FF000000', style: :thin },\n+        font_name: 'Arial',\n+        sz: 11,\n+        b: false,\n+        alignment: { horizontal: :center },\n+        bg_color: '00ea829f'\n+      },\n+      bold_cell: {\n+        border: { color: 'FF000000', style: :thin },\n+        font_name: 'Arial',\n+        sz: 11,\n+        b: true,\n+        alignment: { horizontal: :center }\n+      },\n+      total: {\n+        border: { color: 'FF000000', style: :thin },\n+        font_name: 'Arial',\n+        sz: 11,\n+        b: true,\n+        alignment: { horizontal: :right }\n+      }\n+    }\n+  end\n+\n+  def translate(key)\n+    I18n.t('monthly_report.' + key)\n+  end\n+\n+  def month_translate(month_number)\n+    date = Date.current(month: month_number)\n+    I18n.l(date, format: :report_month)\n+  end\n+\n+  def date_by_day(date, day)\n+    date.change(day: day.to_i)\n+  end\n+\n+  def all_days_in_month(date)\n+    (1..Time.days_in_month(date.month, date.year)).to_a.map(&:to_s)\n+  end\n+\n+  def month_comma_year(date)\n+    I18n.t(\"months_from_digits.#{date.month}\") + ', ' + date.year.to_s\n+  end\n+\n+  def report_title\n+    title = [month_comma_year(@date)]\n+    width = report_width - 1\n+    width.times { title << nil }\n+    title\n+  end\n+\n+  def report_upload_date\n+    [translate('upload_date') + ' ' + I18n.l(Date.current, format: :report_default)]\n+  end\n+\n+  def report_table_total\n+    total = [translate('total')]\n+    width = report_width - 2\n+    width.times { total << nil }\n+    total << \"#{@user_menus.size}\"\n+  end\n+\n+  def report_table_header\n+    header = [translate('name')]\n+    header += all_days_in_month(@date)\n+    header << translate('sum')\n+  end\n+\n+  def report_table_user_info(user)\n+    user_row = [user.name]\n+\n+    total = 0\n+    all_days_in_month(@date).each do |day|\n+      date = date_by_day(@date, day)\n+      if @user_menus.include?([date, user.id])\n+        user_row << 1\n+        total += 1\n+      else\n+        user_row << nil\n+      end\n+    end\n+\n+    user_row << total\n+  end\n+\n+  def calendar_styles(weekend, work)\n+    all_days_in_month(@date).map do |day|\n+      date = date_by_day(@date, day)\n+      if date.saturday? || date.sunday?\n+        weekend\n+      else\n+        work\n+      end\n+    end\n+  end\n+\n+  def table_header_styles\n+    styles = [:header]\n+    styles += calendar_styles(:header_weekend, :header)\n+    styles << :header\n+  end\n+\n+  def table_user_styles\n+    styles = [:name_cell]\n+    styles += calendar_styles(:weekend_cell, :default_cell)\n+    styles << :default_cell\n+  end\n+\n+  def total_styles\n+    styles = [:total]\n+    all_days_in_month(@date).each { styles << :default_cell }\n+    styles << :bold_cell\n+  end\n+\n+  def report_width\n+    report_table_header.size\n+  end\n+\n+  def column_widths\n+    col_widths = [40]","path":"app/services/monthly_report_service.rb","position":225,"original_position":225,"commit_id":"561d3f4bb3f45f498dc5771ab293a620e7df2de8","original_commit_id":"8857c7bc5c6b0dbd34cf92293119144c984f9dd0","user":{"login":"snop-snov","id":4567456,"avatar_url":"https://avatars1.githubusercontent.com/u/4567456?v=3","gravatar_id":"","url":"https://api.github.com/users/snop-snov","html_url":"https://github.com/snop-snov","followers_url":"https://api.github.com/users/snop-snov/followers","following_url":"https://api.github.com/users/snop-snov/following{/other_user}","gists_url":"https://api.github.com/users/snop-snov/gists{/gist_id}","starred_url":"https://api.github.com/users/snop-snov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/snop-snov/subscriptions","organizations_url":"https://api.github.com/users/snop-snov/orgs","repos_url":"https://api.github.com/users/snop-snov/repos","events_url":"https://api.github.com/users/snop-snov/events{/privacy}","received_events_url":"https://api.github.com/users/snop-snov/received_events","type":"User","site_admin":false},"body":"просто магия!","created_at":"2017-04-11T11:05:33Z","updated_at":"2017-04-11T11:10:12Z","html_url":"https://github.com/Restream/obeder/pull/70#discussion_r110873587","pull_request_url":"https://api.github.com/repos/Restream/obeder/pulls/70","_links":{"self":{"href":"https://api.github.com/repos/Restream/obeder/pulls/comments/110873587"},"html":{"href":"https://github.com/Restream/obeder/pull/70#discussion_r110873587"},"pull_request":{"href":"https://api.github.com/repos/Restream/obeder/pulls/70"}}},"pull_request":{"url":"https://api.github.com/repos/Restream/obeder/pulls/70","id":115263764,"html_url":"https://github.com/Restream/obeder/pull/70","diff_url":"https://github.com/Restream/obeder/pull/70.diff","patch_url":"https://github.com/Restream/obeder/pull/70.patch","issue_url":"https://api.github.com/repos/Restream/obeder/issues/70","number":70,"state":"open","locked":false,"title":"issue#29 Добавлены ежемесячные отчеты","user":{"login":"mkalmykov","id":15163823,"avatar_url":"https://avatars1.githubusercontent.com/u/15163823?v=3","gravatar_id":"","url":"https://api.github.com/users/mkalmykov","html_url":"https://github.com/mkalmykov","followers_url":"https://api.github.com/users/mkalmykov/followers","following_url":"https://api.github.com/users/mkalmykov/following{/other_user}","gists_url":"https://api.github.com/users/mkalmykov/gists{/gist_id}","starred_url":"https://api.github.com/users/mkalmykov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mkalmykov/subscriptions","organizations_url":"https://api.github.com/users/mkalmykov/orgs","repos_url":"https://api.github.com/users/mkalmykov/repos","events_url":"https://api.github.com/users/mkalmykov/events{/privacy}","received_events_url":"https://api.github.com/users/mkalmykov/received_events","type":"User","site_admin":false},"body":"Лог изменений:\r\n- Кнопка \"Отчеты\" в навбаре теперь представляет из себя выпадющий список с возможностью выбора отчета (ежедневный или ежемесячный).\r\n- Добавлена страница с ежемесячными отчетами.\r\n- Добавлена возможность выгрузки ежемесячных отчетов в формате Excel.\r\n\r\n@igtulm","created_at":"2017-04-11T08:13:15Z","updated_at":"2017-04-11T11:10:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"bdd4466ef176ded0cb1f56f2113139bf3255cb6a","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/Restream/obeder/pulls/70/commits","review_comments_url":"https://api.github.com/repos/Restream/obeder/pulls/70/comments","review_comment_url":"https://api.github.com/repos/Restream/obeder/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Restream/obeder/issues/70/comments","statuses_url":"https://api.github.com/repos/Restream/obeder/statuses/561d3f4bb3f45f498dc5771ab293a620e7df2de8","head":{"label":"mkalmykov:feature/#29_Reports","ref":"feature/#29_Reports","sha":"561d3f4bb3f45f498dc5771ab293a620e7df2de8","user":{"login":"mkalmykov","id":15163823,"avatar_url":"https://avatars1.githubusercontent.com/u/15163823?v=3","gravatar_id":"","url":"https://api.github.com/users/mkalmykov","html_url":"https://github.com/mkalmykov","followers_url":"https://api.github.com/users/mkalmykov/followers","following_url":"https://api.github.com/users/mkalmykov/following{/other_user}","gists_url":"https://api.github.com/users/mkalmykov/gists{/gist_id}","starred_url":"https://api.github.com/users/mkalmykov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mkalmykov/subscriptions","organizations_url":"https://api.github.com/users/mkalmykov/orgs","repos_url":"https://api.github.com/users/mkalmykov/repos","events_url":"https://api.github.com/users/mkalmykov/events{/privacy}","received_events_url":"https://api.github.com/users/mkalmykov/received_events","type":"User","site_admin":false},"repo":{"id":85288394,"name":"obeder","full_name":"mkalmykov/obeder","owner":{"login":"mkalmykov","id":15163823,"avatar_url":"https://avatars1.githubusercontent.com/u/15163823?v=3","gravatar_id":"","url":"https://api.github.com/users/mkalmykov","html_url":"https://github.com/mkalmykov","followers_url":"https://api.github.com/users/mkalmykov/followers","following_url":"https://api.github.com/users/mkalmykov/following{/other_user}","gists_url":"https://api.github.com/users/mkalmykov/gists{/gist_id}","starred_url":"https://api.github.com/users/mkalmykov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mkalmykov/subscriptions","organizations_url":"https://api.github.com/users/mkalmykov/orgs","repos_url":"https://api.github.com/users/mkalmykov/repos","events_url":"https://api.github.com/users/mkalmykov/events{/privacy}","received_events_url":"https://api.github.com/users/mkalmykov/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mkalmykov/obeder","description":"Office dinner manager","fork":true,"url":"https://api.github.com/repos/mkalmykov/obeder","forks_url":"https://api.github.com/repos/mkalmykov/obeder/forks","keys_url":"https://api.github.com/repos/mkalmykov/obeder/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mkalmykov/obeder/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mkalmykov/obeder/teams","hooks_url":"https://api.github.com/repos/mkalmykov/obeder/hooks","issue_events_url":"https://api.github.com/repos/mkalmykov/obeder/issues/events{/number}","events_url":"https://api.github.com/repos/mkalmykov/obeder/events","assignees_url":"https://api.github.com/repos/mkalmykov/obeder/assignees{/user}","branches_url":"https://api.github.com/repos/mkalmykov/obeder/branches{/branch}","tags_url":"https://api.github.com/repos/mkalmykov/obeder/tags","blobs_url":"https://api.github.com/repos/mkalmykov/obeder/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mkalmykov/obeder/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mkalmykov/obeder/git/refs{/sha}","trees_url":"https://api.github.com/repos/mkalmykov/obeder/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mkalmykov/obeder/statuses/{sha}","languages_url":"https://api.github.com/repos/mkalmykov/obeder/languages","stargazers_url":"https://api.github.com/repos/mkalmykov/obeder/stargazers","contributors_url":"https://api.github.com/repos/mkalmykov/obeder/contributors","subscribers_url":"https://api.github.com/repos/mkalmykov/obeder/subscribers","subscription_url":"https://api.github.com/repos/mkalmykov/obeder/subscription","commits_url":"https://api.github.com/repos/mkalmykov/obeder/commits{/sha}","git_commits_url":"https://api.github.com/repos/mkalmykov/obeder/git/commits{/sha}","comments_url":"https://api.github.com/repos/mkalmykov/obeder/comments{/number}","issue_comment_url":"https://api.github.com/repos/mkalmykov/obeder/issues/comments{/number}","contents_url":"https://api.github.com/repos/mkalmykov/obeder/contents/{+path}","compare_url":"https://api.github.com/repos/mkalmykov/obeder/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mkalmykov/obeder/merges","archive_url":"https://api.github.com/repos/mkalmykov/obeder/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mkalmykov/obeder/downloads","issues_url":"https://api.github.com/repos/mkalmykov/obeder/issues{/number}","pulls_url":"https://api.github.com/repos/mkalmykov/obeder/pulls{/number}","milestones_url":"https://api.github.com/repos/mkalmykov/obeder/milestones{/number}","notifications_url":"https://api.github.com/repos/mkalmykov/obeder/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mkalmykov/obeder/labels{/name}","releases_url":"https://api.github.com/repos/mkalmykov/obeder/releases{/id}","deployments_url":"https://api.github.com/repos/mkalmykov/obeder/deployments","created_at":"2017-03-17T08:27:42Z","updated_at":"2017-03-17T08:27:44Z","pushed_at":"2017-04-11T11:00:49Z","git_url":"git://github.com/mkalmykov/obeder.git","ssh_url":"git@github.com:mkalmykov/obeder.git","clone_url":"https://github.com/mkalmykov/obeder.git","svn_url":"https://github.com/mkalmykov/obeder","homepage":"https://obeder.herokuapp.com","size":503,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Restream:develop","ref":"develop","sha":"2418fd3b353e01f2871561513abb78402d2fee8a","user":{"login":"Restream","id":15815128,"avatar_url":"https://avatars3.githubusercontent.com/u/15815128?v=3","gravatar_id":"","url":"https://api.github.com/users/Restream","html_url":"https://github.com/Restream","followers_url":"https://api.github.com/users/Restream/followers","following_url":"https://api.github.com/users/Restream/following{/other_user}","gists_url":"https://api.github.com/users/Restream/gists{/gist_id}","starred_url":"https://api.github.com/users/Restream/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Restream/subscriptions","organizations_url":"https://api.github.com/users/Restream/orgs","repos_url":"https://api.github.com/users/Restream/repos","events_url":"https://api.github.com/users/Restream/events{/privacy}","received_events_url":"https://api.github.com/users/Restream/received_events","type":"Organization","site_admin":false},"repo":{"id":80906459,"name":"obeder","full_name":"Restream/obeder","owner":{"login":"Restream","id":15815128,"avatar_url":"https://avatars3.githubusercontent.com/u/15815128?v=3","gravatar_id":"","url":"https://api.github.com/users/Restream","html_url":"https://github.com/Restream","followers_url":"https://api.github.com/users/Restream/followers","following_url":"https://api.github.com/users/Restream/following{/other_user}","gists_url":"https://api.github.com/users/Restream/gists{/gist_id}","starred_url":"https://api.github.com/users/Restream/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Restream/subscriptions","organizations_url":"https://api.github.com/users/Restream/orgs","repos_url":"https://api.github.com/users/Restream/repos","events_url":"https://api.github.com/users/Restream/events{/privacy}","received_events_url":"https://api.github.com/users/Restream/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Restream/obeder","description":"Office dinner manager","fork":false,"url":"https://api.github.com/repos/Restream/obeder","forks_url":"https://api.github.com/repos/Restream/obeder/forks","keys_url":"https://api.github.com/repos/Restream/obeder/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Restream/obeder/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Restream/obeder/teams","hooks_url":"https://api.github.com/repos/Restream/obeder/hooks","issue_events_url":"https://api.github.com/repos/Restream/obeder/issues/events{/number}","events_url":"https://api.github.com/repos/Restream/obeder/events","assignees_url":"https://api.github.com/repos/Restream/obeder/assignees{/user}","branches_url":"https://api.github.com/repos/Restream/obeder/branches{/branch}","tags_url":"https://api.github.com/repos/Restream/obeder/tags","blobs_url":"https://api.github.com/repos/Restream/obeder/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Restream/obeder/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Restream/obeder/git/refs{/sha}","trees_url":"https://api.github.com/repos/Restream/obeder/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Restream/obeder/statuses/{sha}","languages_url":"https://api.github.com/repos/Restream/obeder/languages","stargazers_url":"https://api.github.com/repos/Restream/obeder/stargazers","contributors_url":"https://api.github.com/repos/Restream/obeder/contributors","subscribers_url":"https://api.github.com/repos/Restream/obeder/subscribers","subscription_url":"https://api.github.com/repos/Restream/obeder/subscription","commits_url":"https://api.github.com/repos/Restream/obeder/commits{/sha}","git_commits_url":"https://api.github.com/repos/Restream/obeder/git/commits{/sha}","comments_url":"https://api.github.com/repos/Restream/obeder/comments{/number}","issue_comment_url":"https://api.github.com/repos/Restream/obeder/issues/comments{/number}","contents_url":"https://api.github.com/repos/Restream/obeder/contents/{+path}","compare_url":"https://api.github.com/repos/Restream/obeder/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Restream/obeder/merges","archive_url":"https://api.github.com/repos/Restream/obeder/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Restream/obeder/downloads","issues_url":"https://api.github.com/repos/Restream/obeder/issues{/number}","pulls_url":"https://api.github.com/repos/Restream/obeder/pulls{/number}","milestones_url":"https://api.github.com/repos/Restream/obeder/milestones{/number}","notifications_url":"https://api.github.com/repos/Restream/obeder/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Restream/obeder/labels{/name}","releases_url":"https://api.github.com/repos/Restream/obeder/releases{/id}","deployments_url":"https://api.github.com/repos/Restream/obeder/deployments","created_at":"2017-02-04T08:42:12Z","updated_at":"2017-04-04T11:01:16Z","pushed_at":"2017-04-11T11:00:50Z","git_url":"git://github.com/Restream/obeder.git","ssh_url":"git@github.com:Restream/obeder.git","clone_url":"https://github.com/Restream/obeder.git","svn_url":"https://github.com/Restream/obeder","homepage":"https://obeder.herokuapp.com","size":543,"stargazers_count":14,"watchers_count":14,"language":"Ruby","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":10,"forks":5,"open_issues":10,"watchers":14,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Restream/obeder/pulls/70"},"html":{"href":"https://github.com/Restream/obeder/pull/70"},"issue":{"href":"https://api.github.com/repos/Restream/obeder/issues/70"},"comments":{"href":"https://api.github.com/repos/Restream/obeder/issues/70/comments"},"review_comments":{"href":"https://api.github.com/repos/Restream/obeder/pulls/70/comments"},"review_comment":{"href":"https://api.github.com/repos/Restream/obeder/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Restream/obeder/pulls/70/commits"},"statuses":{"href":"https://api.github.com/repos/Restream/obeder/statuses/561d3f4bb3f45f498dc5771ab293a620e7df2de8"}}}}
{"actor":{"display_login":"snop-snov"}}