Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage report functionality from main branch #485

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

ajit3190
Copy link
Contributor

No description provided.

@@ -52,6 +52,7 @@ function FormSectionField({ checkErrors, field, formMethods, formMode, disableUn
<div className={classes} data-testid="form-section-field">
{renderField}
</div>
<div className={`${classes} ${css[commonInputProps.id]}`}>{renderField}</div>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for? We are already rendering renderField above


.To{
margin-left: 307px;
margin-top: -103px;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct indents, and rename this css class to something a little more verbose if still used. If not used delete it.

@@ -0,0 +1,74 @@
// Copyright (c) 2014 - 2023 UNICEF. All rights reserved.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm run test and npm run lint and fix test and lint issues from output

import PropTypes from "prop-types";
import DateFnsUtils from "@date-io/date-fns";
import { Controller, useWatch } from "react-hook-form";
import { MuiPickersUtilsProvider, KeyboardDatePicker } from "@material-ui/pickers";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this file and use the DateInput component in this directory

@@ -232,6 +234,8 @@ export default (field, { checkErrors, errors, formMode, disableUnderline }) => {
return HiddenInput;
case LINK_FIELD:
return LinkField;
case DATE_PICKER:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this

close={dialogClose}
setPending={setDialogPending}
/>

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove space

@@ -0,0 +1,41 @@
// Copyright (c) 2014 - 2023 UNICEF. All rights reserved.

import { SwapVert } from "@material-ui/icons";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change package @mui/icons-material

import { saveExport } from "../../../../../components/record-actions/exports/action-creators";
import { formatFileName } from "../../../../../components/record-actions/exports/utils";


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove one empty line

// Form submission
const onSubmit = getData => {
// Check if any of the required fields are null
if (!getData.From) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use yup for validation instead of the following. You can pass a schema into like

import { object, string } from "yup";

export const validationSchema = object().shape({
  message: string().required()
});

<Form validations={validationSchema} />

into the form component

https://github.com/jquense/yup

@@ -35,7 +35,8 @@ function Component({
formClassName,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes in this file aren't needed.

Copy link
Contributor

@pnabutovsky pnabutovsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure all unit tests and linters pass for both JS and Ruby

# API endpoint for generating exports, in bulk or for individual records
class Api::V2::UsageReportsController < ApplicationApiController
def create
@export = ExportService.build(export_params, current_user)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint needs an authorization check like the others. Otherwise stats can be pulled by anyone who hasn't logged in.

@export_params ||= params.require(:data).permit(
:record_type, :export_format,
:order, :query, :file_name, :password, :selected_from_date, :selected_to_date,
{ custom_export_params: {} }, { filters: {} }, :match_criteria
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we know exactly what the filters and custom export params are let's explicitly specify them in this structure rather than just using the permissive {}

@@ -5,6 +5,7 @@
# Users CRUD API
class Api::V2::UsersController < ApplicationApiController
include Api::V2::Concerns::Pagination
include Api::V2::Concerns::Export
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this code actually get used?

def export(password)
process_records_in_batches(500) { |records_batch| exporter.export(records_batch) }
def export(password, kpi_parameters)
if check_usage_report
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I m not convinced you need to use the BulkExport here at all. The class is designed for exporting 1000's of records. A Primero instance should have significantly less users.

However, if you do need to leverage the bulk/batch logic of a bulk export (I don't see that being done in the proposed code) this would be a good situation to use class inheritance. Instead of inserting if/else logic in the BulkExport you should create a new UserKPIBulkExport that inherits from it. But again, I don't think you need to touch BulkExports at all.


# Export forms to an Excel file (.xlsx)
# rubocop:disable Metrics/ClassLength
class Exporters::UsageReportExporter < Exporters::BaseExporter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class has a lot in common with Exporters::ExcelExporter. You should inherit from that class instead of Exporters::BaseExporter


# class for Usage Report
class UsageReport
include ActiveModel::Model
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason that this is a Rails ActiveModel object rather than a plain Ruby object? Please move this business logic to a service, something like UsageReportService

end

def get_common_keys(module_id, start_date, end_date)
['', UsageReport.total_records(module_id, Child).count, UsageReport.open_cases(module_id).count,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the wrong place to do the .count query. It needs to take place on the UsageReport

class UsageReport
include ActiveModel::Model

class << self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the point of this is to run the .count queries, move the counts to this class


def new_records_quarter(module_id, start_date, end_date, recordtype)
recordtype.where(
"data->>'module_id' = ? AND CAST(data->>'created_at' AS DATE) BETWEEN ? AND ?",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CAST(...) AS DATE may lead to performance issues in large databases. This query can be improved in v2.13 where certain JSON data fields are normalized into tables.

)
end

def total_services(module_id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the two methods below you are loading the entire set of cases into memory. This will crash Primero if it has more than 500-1000 cases. You need to do a more nuanced count query directly on the database that counts the services/followup subform entries. To do that you will probably need to do a CROSS JOIN. See how this was done for ReportableService on the Report. If you are writing complex SQL, don't forget to sanitize code to avoid SQL injections

@@ -124,6 +125,7 @@
end
end
resources :bulk_exports, as: :exports, path: :exports, only: %i[index show create destroy]
resources :usage_reports
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restrict to only: %i[create]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants