Skip to content

Commit

Permalink
Merge pull request #45 from ElemarJR/hours-by-everhour-project
Browse files Browse the repository at this point in the history
Hours by everhour project and Clients CTA
  • Loading branch information
ElemarJR authored Nov 21, 2024
2 parents 3759f73 + 797b677 commit 234355e
Show file tree
Hide file tree
Showing 25 changed files with 2,066 additions and 1,340 deletions.
11 changes: 8 additions & 3 deletions backend/src/api/analytics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from ariadne import QueryType
from ariadne import ObjectType, QueryType

from .week_review import resolve_week_review
from .timeliness_review import resolve_timeliness_review
from .approved_vs_actual import resolve_approved_vs_actual
from .performance_analysis import resolve_performance_analysis
from .performance_analysis import resolve_performance_analysis, resolve_performance_analysis_pivoted

def setup_query_for_analytics(query: QueryType):
query.set_field("weekReview", resolve_week_review)
query.set_field("timelinessReview", resolve_timeliness_review)
query.set_field("approvedVsActual", resolve_approved_vs_actual)
query.set_field("performanceAnalysis", resolve_performance_analysis)
query.set_field("performanceAnalysis", resolve_performance_analysis)

performance_analysis_type = ObjectType('PerformanceAnalysis')
performance_analysis_type.set_field('pivoted', resolve_performance_analysis_pivoted)

return [performance_analysis_type]
453 changes: 452 additions & 1 deletion backend/src/api/analytics/performance_analysis.py

Large diffs are not rendered by default.

117 changes: 105 additions & 12 deletions backend/src/api/analytics/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type WorkingDayHours {
byCase: [CaseHours!]!
}

type OneWeekRegularCasePerformanceSummary {
type RegularCasePerformanceSummary {
id: String!
title: String!
sponsor: String!
Expand All @@ -26,7 +26,7 @@ type OneWeekRegularCasePerformanceSummary {
overApprovedHours: Float!
}

type OneWeekPreContractedCasePerformanceSummary {
type PreContractedCasePerformanceSummary {
id: String!
title: String!
sponsor: String!
Expand Down Expand Up @@ -56,50 +56,143 @@ type TotalsPreContracted {
}

type Totals {
regular: TotalsRegular!
preContracted: TotalsPreContracted!
regular: TotalsRegular
preContracted: TotalsPreContracted
}

type SponsorPerformanceSummary {
name: String!
totals: Totals!
regularCases: [OneWeekRegularCasePerformanceSummary!]!
preContractedCases: [OneWeekPreContractedCasePerformanceSummary!]!
regularCases: [RegularCasePerformanceSummary!]!
preContractedCases: [PreContractedCasePerformanceSummary!]!
}

type ClientPerformanceSummary {
name: String!
totals: Totals!
regularCases: [OneWeekRegularCasePerformanceSummary!]!
preContractedCases: [OneWeekPreContractedCasePerformanceSummary!]!
regularCases: [RegularCasePerformanceSummary!]!
preContractedCases: [PreContractedCasePerformanceSummary!]!
sponsors: [SponsorPerformanceSummary!]!
}

type AccountManagerPerformanceSummary {
name: String!
totals: Totals!
regularCases: [OneWeekRegularCasePerformanceSummary!]!
preContractedCases: [OneWeekPreContractedCasePerformanceSummary!]!
regularCases: [RegularCasePerformanceSummary!]!
preContractedCases: [PreContractedCasePerformanceSummary!]!
clients: [ClientPerformanceSummary!]!
}

type WeekPerformanceAnalysis {
start: Date!
end: Date!
regularCases: [OneWeekRegularCasePerformanceSummary!]!
preContractedCases: [OneWeekPreContractedCasePerformanceSummary!]!
periodType: String!
regularCases: [RegularCasePerformanceSummary!]!
preContractedCases: [PreContractedCasePerformanceSummary!]!
clients: [ClientPerformanceSummary!]!
sponsors: [SponsorPerformanceSummary!]!
accountManagers: [AccountManagerPerformanceSummary!]!
totals: Totals!
actualWorkHours: [WorkingDayHours!]!
}

type PastPerformanceAnalysis {
regularCases: [RegularCasePerformanceSummary!]!
preContractedCases: [PreContractedCasePerformanceSummary!]!
clients: [ClientPerformanceSummary!]!
sponsors: [SponsorPerformanceSummary!]!
accountManagers: [AccountManagerPerformanceSummary!]!
totals: Totals!
}

type PerformanceAnalysisPivoted {
regular: PerformanceAnalysisPivotedRegular!
preContracted: PerformanceAnalysisPivotedPreContracted!
}

type PerformanceAnalysisPivotedRegular {
byAccountManager: [PerformanceAnalysisPivotedRegularByAccountManager!]!
}

type PerformanceAnalysisPivotedPreContracted {
byAccountManager: [PerformanceAnalysisPivotedPreContractedByAccountManager!]!
}

type PerformanceAnalysisPivotedRegularByAccountManager {
name: String!
weeks: [PerformanceAnalysisPivotedRegularWeek!]!
past: TotalsRegular
byClient: [PerformanceAnalysisPivotedRegularByClient!]!
}

type PerformanceAnalysisPivotedPreContractedByAccountManager {
name: String!
weeks: [PerformanceAnalysisPivotedPreContractedWeek!]!
past: TotalsPreContracted
byClient: [PerformanceAnalysisPivotedPreContractedByClient!]!
}

type PerformanceAnalysisPivotedRegularByClient {
name: String!
weeks: [PerformanceAnalysisPivotedRegularWeek!]!
past: TotalsRegular
bySponsor: [PerformanceAnalysisPivotedRegularBySponsor!]!
}

type PerformanceAnalysisPivotedPreContractedByClient {
name: String!
weeks: [PerformanceAnalysisPivotedPreContractedWeek!]!
past: TotalsPreContracted
bySponsor: [PerformanceAnalysisPivotedPreContractedBySponsor!]!
}

type PerformanceAnalysisPivotedRegularBySponsor {
name: String!
weeks: [PerformanceAnalysisPivotedRegularWeek!]!
past: TotalsRegular
byCase: [PerformanceAnalysisPivotedRegularByCase!]!
}

type PerformanceAnalysisPivotedPreContractedBySponsor {
name: String!
weeks: [PerformanceAnalysisPivotedPreContractedWeek!]!
past: TotalsPreContracted
byCase: [PerformanceAnalysisPivotedPreContractedByCase!]!
}

type PerformanceAnalysisPivotedRegularByCase {
title: String!
weeks: [PerformanceAnalysisPivotedRegularWeek!]!
past: TotalsRegular
}

type PerformanceAnalysisPivotedPreContractedByCase {
title: String!
weeks: [PerformanceAnalysisPivotedPreContractedWeek!]!
past: TotalsPreContracted
}

type PerformanceAnalysisPivotedRegularWeek {
start: Date!
end: Date!
periodType: String!
totals: TotalsRegular!
}

type PerformanceAnalysisPivotedPreContractedWeek {
start: Date!
end: Date!
periodType: String!
totals: TotalsPreContracted!
}

type PerformanceAnalysis {
start: Date!
end: Date!
dateOfInterest: Date!
weeks: [WeekPerformanceAnalysis!]!
past: PastPerformanceAnalysis!
pivoted: PerformanceAnalysisPivoted!
}

# Week Review types
Expand Down
7 changes: 6 additions & 1 deletion backend/src/api/domain/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ def build_case_dictionary(map, case):
result['tracker'] = [
{
'id': project.id,
'name': project.name
'name': project.name,
'kind': project.kind,
'budget': {
'period': project.budget.period,
'hours': project.budget.hours
} if project.budget else None
}
for project in case.tracker_info
]
Expand Down
7 changes: 7 additions & 0 deletions backend/src/api/domain/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,16 @@ type Case {
ontologyUrl: String
}

type TrackerProjectBudget {
hours: Float
period: String
}

type TrackerProject {
id: String!
name: String!
kind: String!
budget: TrackerProjectBudget
}

type CaseUpdate {
Expand Down
29 changes: 29 additions & 0 deletions backend/src/api/inconsistencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,34 @@ def resolve_inconsistencies(_, info) -> list[Inconsistency]:
'Sponsors not found in CRM',
f'{len(sponsors_without_crm_id)} sponsor(s) were not found in the CRM system.'
))

# Build mapping of everhour project IDs to cases
everhour_id_to_cases = {}
for case in cases:
if case.is_active and case.everhour_projects_ids:
for everhour_id in case.everhour_projects_ids:
if everhour_id not in everhour_id_to_cases:
everhour_id_to_cases[everhour_id] = []
everhour_id_to_cases[everhour_id].append(case)

# Find duplicate everhour project IDs
duplicate_everhour_ids = {
everhour_id: cases_list
for everhour_id, cases_list in everhour_id_to_cases.items()
if len(cases_list) > 1
}

if duplicate_everhour_ids:
details = []
for everhour_id, cases_list in duplicate_everhour_ids.items():
case_names = [case.title for case in cases_list]
details.append(f"Everhour project ID {everhour_id} is used in cases: {', '.join(case_names)}")

result.append(Inconsistency(
'Duplicate Everhour Project IDs',
f'{len(duplicate_everhour_ids)} Everhour project ID(s) are used in multiple cases:\n' + '\n'.join(details)
))



return result
6 changes: 3 additions & 3 deletions backend/src/api/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ def load_schema():

query = QueryType()

additional_types = setup_query_for_domain(query)
additional_types_for_domain = setup_query_for_domain(query)
setup_query_for_datasets(query)
setup_query_for_analytics(query)
additional_types_for_analytics = setup_query_for_analytics(query)

query.set_field('inconsistencies', resolve_inconsistencies)
query_types = [query] + additional_types
query_types = [query] + additional_types_for_domain + additional_types_for_analytics
type_defs = load_schema()

__all__ = ['schema', 'type_defs']
2 changes: 1 addition & 1 deletion backend/src/api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type Query {
timelinessReview(date_of_interest: Date!, filters: [FilterInput]): TimelinessReview!
projects: [Project!]!
approvedVsActual(start: Date!, end: Date!): ApprovedVsActual!
performanceAnalysis(date_of_interest: Date!): PerformanceAnalysis!
performanceAnalysis(dateOfInterest: Date!): PerformanceAnalysis!

activeDeals(accountManagerSlug: String, accountManagerName: String): [ActiveDeal!]!
activeDeal(id: Int): ActiveDeal
Expand Down
Loading

0 comments on commit 234355e

Please sign in to comment.