Skip to content

Commit f32befb

Browse files
committed
Merge branch 'quarter-postfactum-evaluation'
2 parents b1251ef + fbe8648 commit f32befb

14 files changed

Lines changed: 150 additions & 60 deletions

File tree

AGENTS.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ Starmap is a corporate web application for managing technical team competencies,
4444
- Role-based validation and Quarter state constraints
4545

4646
**Quarterly Cycles**
47-
- Automated creation of new cycles with copying of previous ratings
47+
- Quarters are created **retrospectively**, after the evaluated period has ended
48+
- Previous quarter's ratings are copied as a starting point when a quarter is activated
4849
- Status workflow: draft → active → closed → archived
49-
- Editing restrictions based on quarter state ensure data integrity
50+
- Evaluation window (`evaluation_start_date`..`evaluation_end_date`) opens **after** the quarter's `end_date`
51+
- Editing restrictions based on quarter state and evaluation period ensure data integrity
5052

5153
**Analytics Dashboards**
5254
- **Overview Dashboard**: Unit-level metrics and risks for leadership
@@ -78,7 +80,7 @@ Status tracking of development plans linked to quarters and technologies.
7880
## Role Interactions
7981

8082
1. **Data Collection**: Engineer updates self-ratings → Team Lead validates and approves → data flows to metrics
81-
2. **Quarterly Cycle**: Admin/Unit Lead launches new quarter, copies past ratings, notifications sent
83+
2. **Quarterly Cycle**: Admin creates quarter retrospectively after period ends, activates it (copies past ratings as starting point), notifications sent
8284
3. **Analytics**: Unit Lead tracks Coverage/Maturity/Red Zones, Team Lead monitors team competencies, Engineer tracks personal progress
8385
4. **Risk Management**: Metrics signal gaps; Team Lead and Unit Lead plan expertise exchange
8486
5. **Development**: Action Plans link development goals to quarters, technologies, and employees
@@ -323,7 +325,7 @@ bundle exec brakeman
323325

324326
**Role-based Authorization**: Four distinct roles with Pundit policies enforcing granular permissions.
325327

326-
**Quarterly Data Model**: Immutable past quarters with draft/active states. Ratings editable only in non-archived quarters.
328+
**Quarterly Data Model**: Quarters created retrospectively after the period ends. Evaluation window opens after quarter's `end_date`. Ratings editable only during evaluation period of active quarters.
327329

328330
---
329331

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,14 @@ Every rating answers the question: *"What can I practically do with this technol
8989

9090
### Quarterly Cycle
9191

92-
The process repeats every quarter in three phases:
92+
Starmap evaluates competencies **retrospectively** — after a quarter ends, not while it's in progress. The process repeats every quarter:
9393

94-
1. **Self-assessment** — engineers rate themselves asynchronously across team technologies
95-
2. **1-on-1 dialogue** — team lead and engineer discuss ratings, sync on levels, and build a development plan
96-
3. **Execution** — development goals are integrated into sprint planning, code reviews, and mentoring
94+
1. **Quarter creation** — an admin creates a new quarter (e.g. "2026 Q1") after it has ended, sets the evaluation window dates
95+
2. **Activation & self-assessment** — the admin activates the quarter; previous quarter's ratings are copied as a starting point. Engineers then rate themselves asynchronously across team technologies during the evaluation window
96+
3. **1-on-1 dialogue** — team lead and engineer discuss ratings, sync on levels, and build a development plan
97+
4. **Close** — the admin closes the quarter; all remaining draft/submitted ratings are auto-approved. The quarter becomes read-only
98+
99+
Quarter lifecycle: `draft``active``closed``archived`
97100

98101
### User Roles
99102

app/controllers/admin/quarters_controller.rb

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,19 @@ def quarter_params
117117
end
118118

119119
def set_default_year_and_quarter
120-
current_year = Date.current.year
121-
existing_quarters = Quarter.where(year: current_year).pluck(:quarter_number)
122-
available_quarter = (1..4).find { |q| !existing_quarters.include?(q) }
120+
today = Date.current
121+
current_quarter_number = ((today.month - 1) / 3) + 1
123122

124-
@quarter.year = current_year
125-
@quarter.quarter_number = available_quarter || 1
123+
previous_quarter_year = (current_quarter_number == 1) ? today.year - 1 : today.year
124+
previous_quarter_number = (current_quarter_number == 1) ? 4 : current_quarter_number - 1
125+
126+
if Quarter.exists?(year: previous_quarter_year, quarter_number: previous_quarter_number)
127+
@quarter.year = today.year
128+
@quarter.quarter_number = current_quarter_number
129+
else
130+
@quarter.year = previous_quarter_year
131+
@quarter.quarter_number = previous_quarter_number
132+
end
126133
end
127134

128135
def load_quarter_metrics

app/models/quarter.rb

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ class Quarter < ApplicationRecord
2121
validates :status, presence: true, inclusion: {in: %w[draft active closed archived]}
2222
validates :quarter_number, uniqueness: {scope: :year}
2323

24-
validate :validate_year_is_current_or_future
2524
validate :validate_date_sequence
26-
validate :validate_evaluation_dates
25+
validate :validate_evaluation_after_quarter
2726

2827
before_validation :set_quarter_name, on: :create
28+
before_validation :set_quarter_dates, on: :create
2929
after_create :set_as_current_if_first
3030
after_update :handle_status_change
3131

@@ -50,22 +50,19 @@ def self.current
5050

5151
private
5252

53-
def validate_year_is_current_or_future
54-
return if year.blank?
55-
56-
errors.add(:year, :current_or_future_only) if year < Date.current.year
57-
end
58-
5953
def validate_date_sequence
6054
return unless start_date.present? && end_date.present?
6155
errors.add(:end_date, :after_start_date) if end_date <= start_date
6256

6357
return unless evaluation_start_date.present? && evaluation_end_date.present?
64-
errors.add(:evaluation_start_date, :within_quarter) if evaluation_start_date < start_date || evaluation_start_date > end_date
65-
errors.add(:evaluation_end_date, :within_quarter) if evaluation_end_date < start_date || evaluation_end_date > end_date
6658
errors.add(:evaluation_end_date, :after_eval_start) if evaluation_end_date <= evaluation_start_date
6759
end
6860

61+
def validate_evaluation_after_quarter
62+
return unless evaluation_start_date.present? && end_date.present?
63+
errors.add(:evaluation_start_date, :after_quarter_end) if evaluation_start_date < end_date
64+
end
65+
6966
def validate_evaluation_dates
7067
return unless evaluation_start_date.present? && evaluation_end_date.present?
7168

@@ -78,6 +75,13 @@ def set_quarter_name
7875
self.name ||= full_name
7976
end
8077

78+
def set_quarter_dates
79+
return unless year.present? && quarter_number.present?
80+
81+
self.start_date ||= Date.new(year, (quarter_number - 1) * 3 + 1, 1)
82+
self.end_date ||= start_date.end_of_quarter
83+
end
84+
8185
def set_as_current_if_first
8286
update!(is_current: true) if Quarter.count == 1
8387
end

app/services/quarter_status_service.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def activate
1818
status: :active,
1919
is_current: true
2020
)
21+
copy_previous_quarter_ratings
2122
end
2223

2324
true
@@ -99,4 +100,11 @@ def current_quarter_exists?
99100
def deactivate_current_quarter
100101
Quarter.where(is_current: true).update_all(is_current: false)
101102
end
103+
104+
def copy_previous_quarter_ratings
105+
previous = @quarter.previous_quarter
106+
return unless previous
107+
108+
QuarterDataCopier.new(@quarter, previous).copy_from_previous
109+
end
102110
end

app/views/admin/quarters/edit.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<div>
3535
<%= f.label :year, t("activerecord.attributes.quarter.year"), class: "form-label" %>
3636
<div class="form-field">
37-
<%= f.number_field :year, min: Date.current.year, required: true, class: "form-input" %>
37+
<%= f.number_field :year, min: 2000, required: true, class: "form-input" %>
3838
</div>
3939
</div>
4040

app/views/admin/quarters/new.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<div>
3535
<%= f.label :year, t("activerecord.attributes.quarter.year"), class: "form-label" %>
3636
<div class="form-field">
37-
<%= f.number_field :year, min: Date.current.year, required: true, class: "form-input" %>
37+
<%= f.number_field :year, min: 2000, required: true, class: "form-input" %>
3838
</div>
3939
</div>
4040

config/locales/en.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -538,14 +538,11 @@ en:
538538
attributes:
539539
quarter_number:
540540
taken: "already exists for this year"
541-
year:
542-
current_or_future_only: "must be current or future year"
543541
end_date:
544542
after_start_date: "must be after start date"
545543
evaluation_start_date:
546-
within_quarter: "must be within quarter period"
544+
after_quarter_end: "must be on or after quarter end date"
547545
evaluation_end_date:
548-
within_quarter: "must be within quarter period"
549546
after_eval_start: "must be after evaluation start date"
550547
messages:
551548
too_long_evaluation_period: "Evaluation period must not exceed 30 days"

config/locales/ru.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -523,14 +523,11 @@ ru:
523523
attributes:
524524
quarter_number:
525525
taken: "уже существует для этого года"
526-
year:
527-
current_or_future_only: "может быть только текущим или будущим годом"
528526
end_date:
529527
after_start_date: "должна быть позже даты начала"
530528
evaluation_start_date:
531-
within_quarter: "должна быть в периоде квартала"
529+
after_quarter_end: "должна быть не раньше даты окончания квартала"
532530
evaluation_end_date:
533-
within_quarter: "должна быть в периоде квартала"
534531
after_eval_start: "должна быть позже даты начала оценки"
535532
messages:
536533
too_long_evaluation_period: "период оценки не должен превышать 30 дней"

db/seeds.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ def target_experts_for_criticality(criticality)
202202
quarter.status = "active"
203203
quarter.description = "Quarter #{current_q_num} #{current_year}"
204204
quarter.is_current = true
205-
quarter.evaluation_start_date = current_start
206-
quarter.evaluation_end_date = current_start + 14.days
205+
quarter.evaluation_start_date = current_end
206+
quarter.evaluation_end_date = current_end + 14.days
207207
end
208208

209209
previous_quarter = Quarter.find_or_create_by!(
@@ -216,8 +216,8 @@ def target_experts_for_criticality(criticality)
216216
quarter.status = "closed"
217217
quarter.description = "Quarter #{prev_q_num} #{prev_year}"
218218
quarter.is_current = false
219-
quarter.evaluation_start_date = prev_start
220-
quarter.evaluation_end_date = prev_start + 14.days
219+
quarter.evaluation_start_date = prev_end
220+
quarter.evaluation_end_date = prev_end + 14.days
221221
end
222222

223223
Rails.logger.debug "Creating test users..."

0 commit comments

Comments
 (0)