The Netguru team currently consists of over 170 people who are assigned to various projects on a regular basis. You can imagine how much time it requires to prepare comprehensive reports on their work for different clients. We wanted to help our scheduler - Natalia - who had to regularly create multiple reports in XLS files. To this end, we created a simple Rails application which auto-generates the XLS reports based on data acquired from our People app’s API, which Natalia had created manually.
Problem - No Time for Reporting
Every week and every month our scheduler needs to manually create multiple XLS reports containing information about our team based on data from our People app. The reports mainly contain statistics such as the number of available developers of various types within one or two weeks. When done manually, this required a few hours of work each month and was quite prone to human error. The aim of the app was to automate this process - basically to map all the conditions and decision processes of our scheduler, then generate such reports in seconds.
Process - a Tailor-made Talent Manager App
Two developers built this small app from scratch. It’s pretty straightforward: the scheduler visits the page, and she has multiple buttons available. Each button generates and downloads a different report. Our People app had to expose an API in order to provide full, up-to-date information about our team and the availability of each developer. The developers had to regularly check whether their auto-generated reports were consistent with the scheduler’s manual reports for the same time frame. All discrepancies then had to be discussed with the scheduler to find out which conditions or decisions processed were not implemented correctly and how the edge cases should be handled.
Code - Short But Powerful
Let’s say we want to create a new report named SampleReport to display users with roles RoR and those without a role. We want a cell which displays centred text and "-" if there is no content:
module SampleReport module Cells class Centered < Base::Cell def content return "-" if data.blank? data end def format super.merge!(align: :center) end end end end
Next, we need a row to store our cells:
module SampleReport class Row < Base::Row def cells [ Base::Cell.new(data: data.last_name), Base::Cell.new(data: data.first_name), SampleReport::Cells::Centered.new(data: data.role) ] end end end
We use Base:Cell to display plain data.
Now, let's add a Table:
module SampleReport class Table < Base::Table def header [ SampleReport::Cells::Centered.new(data: "Last Name"), SampleReport::Cells::Centered.new(data: "First Name"), SampleReport::Cells::Centered.new(data: "Role"), ] end
def rows data.map do |user| SampleReport::Row.new(data: user) end end end end
We used our SampleReport::Cells::Centered to centre text in header cells.
Repositories are introduced to filter and mangle data fetched from the API. We need one of those too:
module SampleReport class TechnicalUsersRepository < Base::TechnicalUsersRepository def all @all ||= super.select do |user| user.role.in?(["RoR", ""]) end end end end
The SampleReport::GenerateTable service just needs to specify which module we want to use in the report_module method:
module SampleReport class GenerateTable < ::Base::GenerateTable private def report_module SampleReport end end end
Finally, we need it to add the `sample_report` action to ReportsController (don't forget about updating routes.rb):
def sample_report table = SampleReport::GenerateTable.new.call send_xls(table: table, file_name: "sample_report") end
This way, we’ve created a new report which can be downloaded when accessing the sample_report path.
No database is needed to make sure the data is always up to date as each report generation fetches most the recent data from the API. User authorisation is done with Google Accounts.
The whole process of building the app from scratch took us approximately one month. The app’s main function is basically to map Natalia’s brain, and it does so with the use of the Ruby code we wrote. Having listened to Natalia’s problems carefully, we were able to create a tailor-made solution which saves our scheduler a LOT of time.
0 time wasted on reporting by our scheduler Natalia;