decidim-time_tracker 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE-AGPLv3.txt +661 -0
- data/README.md +174 -0
- data/Rakefile +40 -0
- data/app/assets/config/admin/decidim_time_tracker_manifest.css +2 -0
- data/app/assets/config/admin/decidim_time_tracker_manifest.js +1 -0
- data/app/assets/config/decidim_time_tracker_manifest.css +3 -0
- data/app/assets/config/decidim_time_tracker_manifest.js +1 -0
- data/app/assets/images/decidim/time_tracker/icon.svg +1 -0
- data/app/assets/javascripts/decidim/time_tracker/activity_ui.js.es6 +112 -0
- data/app/assets/javascripts/decidim/time_tracker/admin/assignations.js.es6 +35 -0
- data/app/assets/javascripts/decidim/time_tracker/reports/reports.js.es6 +19 -0
- data/app/assets/javascripts/decidim/time_tracker/time_tracker.js.es6 +82 -0
- data/app/assets/javascripts/decidim/time_tracker/timer_api.js.es6 +55 -0
- data/app/assets/stylesheets/decidim/time_tracker/_variables.scss +1 -0
- data/app/assets/stylesheets/decidim/time_tracker/admin/time_tracker.scss +59 -0
- data/app/assets/stylesheets/decidim/time_tracker/time_tracker.scss +118 -0
- data/app/cells/decidim/time_tracker/milestone/show.erb +42 -0
- data/app/cells/decidim/time_tracker/milestone_cell.rb +54 -0
- data/app/commands/decidim/time_tracker/admin/create_activity.rb +39 -0
- data/app/commands/decidim/time_tracker/admin/create_assignation.rb +75 -0
- data/app/commands/decidim/time_tracker/admin/create_task.rb +33 -0
- data/app/commands/decidim/time_tracker/admin/create_time_tracker.rb +76 -0
- data/app/commands/decidim/time_tracker/admin/destroy_activity.rb +43 -0
- data/app/commands/decidim/time_tracker/admin/destroy_assignation.rb +43 -0
- data/app/commands/decidim/time_tracker/admin/destroy_task.rb +43 -0
- data/app/commands/decidim/time_tracker/admin/export_time_tracker.rb +150 -0
- data/app/commands/decidim/time_tracker/admin/update_activity.rb +49 -0
- data/app/commands/decidim/time_tracker/admin/update_assignation.rb +42 -0
- data/app/commands/decidim/time_tracker/admin/update_task.rb +44 -0
- data/app/commands/decidim/time_tracker/create_milestone.rb +57 -0
- data/app/commands/decidim/time_tracker/create_request_assignation.rb +39 -0
- data/app/commands/decidim/time_tracker/start_time_event.rb +104 -0
- data/app/commands/decidim/time_tracker/stop_last_time_event.rb +45 -0
- data/app/controllers/concerns/decidim/time_tracker/admin/filterable_assignations.rb +50 -0
- data/app/controllers/decidim/time_tracker/activity_questionnaire_controller.rb +76 -0
- data/app/controllers/decidim/time_tracker/admin/activities_controller.rb +82 -0
- data/app/controllers/decidim/time_tracker/admin/activity_questionnaire_controller.rb +52 -0
- data/app/controllers/decidim/time_tracker/admin/application_controller.rb +22 -0
- data/app/controllers/decidim/time_tracker/admin/assignations_controller.rb +84 -0
- data/app/controllers/decidim/time_tracker/admin/assignee_questionnaire_controller.rb +58 -0
- data/app/controllers/decidim/time_tracker/admin/stats_controller.rb +24 -0
- data/app/controllers/decidim/time_tracker/admin/tasks_controller.rb +112 -0
- data/app/controllers/decidim/time_tracker/admin/time_tracker_exports_controller.rb +25 -0
- data/app/controllers/decidim/time_tracker/application_controller.rb +24 -0
- data/app/controllers/decidim/time_tracker/assignations_controller.rb +38 -0
- data/app/controllers/decidim/time_tracker/assignee_questionnaire_controller.rb +80 -0
- data/app/controllers/decidim/time_tracker/milestones_controller.rb +62 -0
- data/app/controllers/decidim/time_tracker/reports/user_controller.rb +38 -0
- data/app/controllers/decidim/time_tracker/time_events_controller.rb +68 -0
- data/app/controllers/decidim/time_tracker/time_tracker_controller.rb +43 -0
- data/app/forms/decidim/time_tracker/admin/activity_form.rb +27 -0
- data/app/forms/decidim/time_tracker/admin/assignation_form.rb +29 -0
- data/app/forms/decidim/time_tracker/admin/task_form.rb +22 -0
- data/app/forms/decidim/time_tracker/milestone_form.rb +32 -0
- data/app/forms/decidim/time_tracker/time_event_form.rb +47 -0
- data/app/helpers/decidim/time_tracker/admin/application_helper.rb +33 -0
- data/app/helpers/decidim/time_tracker/application_helper.rb +95 -0
- data/app/jobs/decidim/time_tracker/stop_counter_job.rb +29 -0
- data/app/models/decidim/time_tracker/activity.rb +135 -0
- data/app/models/decidim/time_tracker/application_record.rb +10 -0
- data/app/models/decidim/time_tracker/assignation.rb +68 -0
- data/app/models/decidim/time_tracker/assignee.rb +29 -0
- data/app/models/decidim/time_tracker/assignee_data.rb +19 -0
- data/app/models/decidim/time_tracker/milestone.rb +27 -0
- data/app/models/decidim/time_tracker/task.rb +44 -0
- data/app/models/decidim/time_tracker/time_event.rb +62 -0
- data/app/models/decidim/time_tracker/time_tracker.rb +46 -0
- data/app/models/decidim/time_tracker/tos_acceptance.rb +16 -0
- data/app/permissions/decidim/time_tracker/admin/permissions.rb +69 -0
- data/app/permissions/decidim/time_tracker/permissions.rb +69 -0
- data/app/presenters/decidim/time_tracker/admin_log/activity_presenter.rb +44 -0
- data/app/presenters/decidim/time_tracker/admin_log/assignation_presenter.rb +48 -0
- data/app/presenters/decidim/time_tracker/admin_log/task_presenter.rb +46 -0
- data/app/presenters/decidim/time_tracker/admin_log/value_types/activity_presenter.rb +28 -0
- data/app/presenters/decidim/time_tracker/admin_log/value_types/time_tracker_presenter.rb +28 -0
- data/app/views/decidim/time_tracker/admin/_breadcrumbs.html.erb +56 -0
- data/app/views/decidim/time_tracker/admin/activities/_form.html.erb +41 -0
- data/app/views/decidim/time_tracker/admin/activities/_index.html.erb +73 -0
- data/app/views/decidim/time_tracker/admin/activities/edit.html.erb +5 -0
- data/app/views/decidim/time_tracker/admin/activities/new.html.erb +5 -0
- data/app/views/decidim/time_tracker/admin/assignations/_form.html.erb +30 -0
- data/app/views/decidim/time_tracker/admin/assignations/index.html.erb +67 -0
- data/app/views/decidim/time_tracker/admin/assignations/new.html.erb +9 -0
- data/app/views/decidim/time_tracker/admin/questionnaires/_index.html.erb +27 -0
- data/app/views/decidim/time_tracker/admin/questionnaires/_questionnaire.html.erb +21 -0
- data/app/views/decidim/time_tracker/admin/stats/index.html.erb +50 -0
- data/app/views/decidim/time_tracker/admin/tasks/_assignations.html.erb +60 -0
- data/app/views/decidim/time_tracker/admin/tasks/_form.html.erb +7 -0
- data/app/views/decidim/time_tracker/admin/tasks/_index.html.erb +61 -0
- data/app/views/decidim/time_tracker/admin/tasks/edit.html.erb +16 -0
- data/app/views/decidim/time_tracker/admin/tasks/index.html.erb +5 -0
- data/app/views/decidim/time_tracker/admin/tasks/new.html.erb +12 -0
- data/app/views/decidim/time_tracker/milestones/index.html.erb +60 -0
- data/app/views/decidim/time_tracker/reports/user/index.html.erb +41 -0
- data/app/views/decidim/time_tracker/time_tracker/_activity.html.erb +43 -0
- data/app/views/decidim/time_tracker/time_tracker/_answer_questionnaire.html.erb +7 -0
- data/app/views/decidim/time_tracker/time_tracker/_assignee_data.html.erb +9 -0
- data/app/views/decidim/time_tracker/time_tracker/_callout_status.html.erb +3 -0
- data/app/views/decidim/time_tracker/time_tracker/_idle_activity.html.erb +27 -0
- data/app/views/decidim/time_tracker/time_tracker/_milestone_form.html.erb +35 -0
- data/app/views/decidim/time_tracker/time_tracker/_request_activity.html.erb +22 -0
- data/app/views/decidim/time_tracker/time_tracker/_sign_in.html.erb +5 -0
- data/app/views/decidim/time_tracker/time_tracker/index.html.erb +59 -0
- data/config/activity_questionnaire.yml +26 -0
- data/config/assignee_questionnaire.yml +37 -0
- data/config/i18n-tasks.yml +10 -0
- data/config/locales/ca.yml +394 -0
- data/config/locales/cs.yml +394 -0
- data/config/locales/en.yml +419 -0
- data/config/locales/es.yml +394 -0
- data/db/migrate/20200302115642_create_decidim_time_tracker_time_trackers.rb +10 -0
- data/db/migrate/20200302115654_create_decidim_time_tracker_tasks.rb +11 -0
- data/db/migrate/20200302115658_create_decidim_time_tracker_assignees.rb +12 -0
- data/db/migrate/20200302115700_create_decidim_time_tracker_activities.rb +19 -0
- data/db/migrate/20200302115708_create_decidim_time_tracker_milestones.rb +13 -0
- data/db/migrate/20200302115831_create_decidim_time_tracker_assignations.rb +16 -0
- data/db/migrate/20200302115840_create_decidim_time_tracker_time_events.rb +15 -0
- data/db/migrate/20201118153327_create_decidim_time_tracker_assignee_data.rb +10 -0
- data/db/migrate/20201223115130_create_decidim_time_tracker_tos_acceptances.rb +18 -0
- data/lib/decidim/time_tracker.rb +45 -0
- data/lib/decidim/time_tracker/admin.rb +10 -0
- data/lib/decidim/time_tracker/admin_engine.rb +51 -0
- data/lib/decidim/time_tracker/component.rb +248 -0
- data/lib/decidim/time_tracker/engine.rb +59 -0
- data/lib/decidim/time_tracker/reports.rb +10 -0
- data/lib/decidim/time_tracker/reports_engine.rb +34 -0
- data/lib/decidim/time_tracker/test/admin_log_presenter_examples.rb +34 -0
- data/lib/decidim/time_tracker/test/factories.rb +110 -0
- data/lib/decidim/time_tracker/test/time_tracker_context.rb +18 -0
- data/lib/decidim/time_tracker/time_tracker_activity_questionnaire_answers_serializer.rb +74 -0
- data/lib/decidim/time_tracker/version.rb +9 -0
- data/vendor/assets/javascripts/jsrender.min.js +4 -0
- metadata +276 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
module Admin
|
|
6
|
+
# Custom helpers, scoped to the time_tracker engine.
|
|
7
|
+
#
|
|
8
|
+
module ApplicationHelper
|
|
9
|
+
def change_assignation_status_button(assignation, success_path: nil)
|
|
10
|
+
path_reject = task_activity_assignation_path(assignation.task, assignation.activity, assignation, assignation_status: :rejected, success_path: success_path)
|
|
11
|
+
path_accept = task_activity_assignation_path(assignation.task, assignation.activity, assignation, assignation_status: :accepted, success_path: success_path)
|
|
12
|
+
label_reject = t("assignations.actions.reject", scope: "decidim.time_tracker.admin")
|
|
13
|
+
label_accept = t("assignations.actions.accept", scope: "decidim.time_tracker.admin")
|
|
14
|
+
|
|
15
|
+
icons = [
|
|
16
|
+
assignation.pending? || assignation.accepted? ? icon_link_to("x", path_reject, label_reject, method: :patch, class: "action-icon--status") : empty_icon,
|
|
17
|
+
assignation.pending? || assignation.rejected? ? icon_link_to("check", path_accept, label_accept, method: :patch, class: "action-icon--status") : empty_icon
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
safe_join(icons)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def empty_icon
|
|
24
|
+
content_tag :a, class: "action-icon" do
|
|
25
|
+
content_tag :span do
|
|
26
|
+
icon("", aria_label: "", role: "img")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
# Custom helpers, scoped to the time_tracker engine.
|
|
6
|
+
#
|
|
7
|
+
module ApplicationHelper
|
|
8
|
+
include Decidim::TranslatableAttributes
|
|
9
|
+
|
|
10
|
+
def milestones_path(params = {})
|
|
11
|
+
Decidim::EngineRouter.main_proxy(current_component).milestones_path(params)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def tasks_label
|
|
15
|
+
translated_attribute(component_settings.tasks_label).presence || t("models.task.name", scope: "decidim.time_tracker")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def activities_label
|
|
19
|
+
translated_attribute(component_settings.activities_label).presence || t("models.activity.name", scope: "decidim.time_tracker")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def assignations_label
|
|
23
|
+
translated_attribute(component_settings.assignations_label).presence || t("models.assignation.name", scope: "decidim.time_tracker")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def time_events_label
|
|
27
|
+
translated_attribute(component_settings.time_events_label).presence || t("models.time_entry.name", scope: "decidim.time_tracker")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def milestones_label
|
|
31
|
+
translated_attribute(component_settings.milestones_label).presence || t("models.milestone.name", scope: "decidim.time_tracker")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# turns a number of seconds to a string 0h 0m 0s
|
|
35
|
+
def clockify_seconds(total_seconds, padded: false)
|
|
36
|
+
total_seconds = total_seconds.to_i
|
|
37
|
+
|
|
38
|
+
clock = {
|
|
39
|
+
hours: total_seconds / (60 * 60),
|
|
40
|
+
minutes: (total_seconds / 60) % 60,
|
|
41
|
+
seconds: total_seconds % 60
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
content_tag :span, class: "time-tracker--clock" do
|
|
45
|
+
safe_join(
|
|
46
|
+
clock.map do |label, value|
|
|
47
|
+
string_value = padded ? value.to_s.rjust(2, "0") : value
|
|
48
|
+
content_tag(:span, t("decidim.time_tracker.clock.#{label}", n: string_value), class: ("text-muted" if value.zero?))
|
|
49
|
+
end
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def assignation_status_label(status)
|
|
55
|
+
klass = case status
|
|
56
|
+
when "accepted" then "success"
|
|
57
|
+
when "pending" then "warning"
|
|
58
|
+
when "rejected" then "danger"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
content_tag :span, class: "#{klass} label" do
|
|
62
|
+
t("models.assignation.fields.statuses.#{status}", scope: "decidim.time_tracker")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def assignation_date(assignation)
|
|
67
|
+
if assignation.invited_at.present?
|
|
68
|
+
t("models.assignation.fields.invited_at", time: l(assignation.invited_at, format: :short), scope: "decidim.time_tracker")
|
|
69
|
+
elsif assignation.requested_at.present?
|
|
70
|
+
t("models.assignation.fields.requested_at", time: l(assignation.requested_at, format: :short), scope: "decidim.time_tracker")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def user_total_time_dedicated(user)
|
|
75
|
+
Assignation.where(user: user).sum(&:time_dedicated)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def user_joined_at(user)
|
|
79
|
+
Assignee.for(user).tos_accepted_at(time_tracker)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def user_last_milestone(user)
|
|
83
|
+
Milestone.where(user: user).order(created_at: :desc).first
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def must_fill_in_data?
|
|
87
|
+
!current_assignee.tos_accepted?(time_tracker) && !activities_empty?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def activities_empty?
|
|
91
|
+
time_tracker.activities.active.empty?
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
# This jobs stops a counter if is over timed
|
|
5
|
+
module TimeTracker
|
|
6
|
+
class StopCounterJob < ApplicationJob
|
|
7
|
+
queue_as :default
|
|
8
|
+
|
|
9
|
+
# time_event - the counter to stop, only stopped if it's due time is reached
|
|
10
|
+
def perform(time_event)
|
|
11
|
+
@time_event = time_event
|
|
12
|
+
return if @time_event.stopped?
|
|
13
|
+
return if time_available?
|
|
14
|
+
|
|
15
|
+
@time_event.stop!
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def time_available?
|
|
21
|
+
date = Time.zone.at(@time_event.start)
|
|
22
|
+
last_elapsed = @time_event.seconds_elapsed
|
|
23
|
+
remaining = @time_event.activity.user_remaining_for_date(@time_event.user, date)
|
|
24
|
+
|
|
25
|
+
remaining > last_elapsed
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
# The data store for a Activity in the Decidim::TimeTracker component. It
|
|
6
|
+
# stores a description and other useful information related to an activity.
|
|
7
|
+
class Activity < ApplicationRecord
|
|
8
|
+
include Decidim::Traceable
|
|
9
|
+
include Decidim::Loggable
|
|
10
|
+
|
|
11
|
+
self.table_name = :decidim_time_tracker_activities
|
|
12
|
+
|
|
13
|
+
belongs_to :task,
|
|
14
|
+
class_name: "Decidim::TimeTracker::Task"
|
|
15
|
+
|
|
16
|
+
has_many :assignations,
|
|
17
|
+
class_name: "Decidim::TimeTracker::Assignation",
|
|
18
|
+
dependent: :destroy
|
|
19
|
+
|
|
20
|
+
has_many :time_events,
|
|
21
|
+
class_name: "Decidim::TimeTracker::TimeEvent"
|
|
22
|
+
|
|
23
|
+
has_many :milestones,
|
|
24
|
+
class_name: "Decidim::TimeTracker::Milestone"
|
|
25
|
+
|
|
26
|
+
scope :active, -> { where(active: true) }
|
|
27
|
+
|
|
28
|
+
delegate :questionnaire, to: :task
|
|
29
|
+
|
|
30
|
+
# total number of seconds spent by the user
|
|
31
|
+
# not counting current counters
|
|
32
|
+
def user_total_seconds(user)
|
|
33
|
+
time_events.where(user: user).sum(&:total_seconds).to_i
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def user_total_seconds_for_date(user, date)
|
|
37
|
+
time_events.started_between(date.beginning_of_day, date.end_of_day).where(user: user).sum(&:total_seconds).to_i
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Total number of seconds spent by the user
|
|
41
|
+
# and counting any possible running counters
|
|
42
|
+
def user_seconds_elapsed(user)
|
|
43
|
+
total = user_total_seconds(user)
|
|
44
|
+
total += last_event_for(user).seconds_elapsed if last_event_for(user)
|
|
45
|
+
total
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def counter_active_for?(user)
|
|
49
|
+
return false unless last_event_for(user)
|
|
50
|
+
|
|
51
|
+
!last_event_for(user).stopped?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns how many seconds are available for this task in the current day
|
|
55
|
+
# this can be less than the activity is allowed due the change of date
|
|
56
|
+
def remaining_seconds_for_today
|
|
57
|
+
@remaining_seconds_for_today ||= [max_minutes_per_day * 60, (Time.current.end_of_day - Time.current).to_i].min
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# how many seconds ara available for this task and user for the current day
|
|
61
|
+
def user_remaining_for_date(user, date)
|
|
62
|
+
total_seconds = user_total_seconds_for_date(user, date)
|
|
63
|
+
remaining = max_minutes_per_day * 60
|
|
64
|
+
remaining = remaining_seconds_for_today if date.beginning_of_day == Date.current
|
|
65
|
+
|
|
66
|
+
[remaining - total_seconds, 0].max
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def user_remaining_for_today(user)
|
|
70
|
+
user_remaining_for_date(user, Date.current)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def assignation_pending?(user)
|
|
74
|
+
assignations.pending.where(user: user).count.positive?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def assignation_accepted?(user)
|
|
78
|
+
assignations.accepted.where(user: user).count.positive?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def assignation_rejected?(user)
|
|
82
|
+
assignations.rejected.where(user: user).count.positive?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def has_assignation?(user)
|
|
86
|
+
assignations.where(user: user).count.positive?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def has_questions?
|
|
90
|
+
questionnaire.questions.any?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def allow_answers_for?(user)
|
|
94
|
+
return false if status == :inactive
|
|
95
|
+
|
|
96
|
+
return false unless has_questions?
|
|
97
|
+
|
|
98
|
+
assignation_accepted?(user)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def answered_by?(user)
|
|
102
|
+
questionnaire.answered_by? session_token(user)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# used as a unique idenfier when answering the task associated questionnaire
|
|
106
|
+
def session_token(user)
|
|
107
|
+
"#{Digest::SHA1.hexdigest(user.id.to_s)}-#{id}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Returns a identificative (I18n) string about the current status of activity
|
|
111
|
+
# Returns:
|
|
112
|
+
# :open if user can track time
|
|
113
|
+
# :finished if current date is passed end date
|
|
114
|
+
# :not_started if current date has not reach start date
|
|
115
|
+
# :inactive if current status is inactive
|
|
116
|
+
def status
|
|
117
|
+
return :inactive unless active?
|
|
118
|
+
return :not_started if start_date > Time.current.beginning_of_day
|
|
119
|
+
return :finished if end_date < Time.current.beginning_of_day
|
|
120
|
+
|
|
121
|
+
:open
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def self.log_presenter_class_for(_log)
|
|
125
|
+
Decidim::TimeTracker::AdminLog::ActivityPresenter
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def last_event_for(user)
|
|
131
|
+
time_events.last_for(user)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
# The data store for an assignation in the Decidim::TimeTracker component.
|
|
6
|
+
class Assignation < ApplicationRecord
|
|
7
|
+
include Decidim::Traceable
|
|
8
|
+
include Decidim::Loggable
|
|
9
|
+
|
|
10
|
+
self.table_name = :decidim_time_tracker_assignations
|
|
11
|
+
|
|
12
|
+
belongs_to :user,
|
|
13
|
+
foreign_key: "decidim_user_id",
|
|
14
|
+
class_name: "Decidim::User"
|
|
15
|
+
|
|
16
|
+
belongs_to :invited_by_user,
|
|
17
|
+
class_name: "Decidim::User",
|
|
18
|
+
optional: true
|
|
19
|
+
|
|
20
|
+
belongs_to :activity,
|
|
21
|
+
class_name: "Decidim::TimeTracker::Activity"
|
|
22
|
+
|
|
23
|
+
has_one :task,
|
|
24
|
+
class_name: "Decidim::TimeTracker::Task",
|
|
25
|
+
through: :activity
|
|
26
|
+
|
|
27
|
+
has_many :time_events,
|
|
28
|
+
class_name: "Decidim::TimeTracker::TimeEvent",
|
|
29
|
+
dependent: :nullify
|
|
30
|
+
|
|
31
|
+
has_many :milestones,
|
|
32
|
+
class_name: "Decidim::TimeTracker::Milestone",
|
|
33
|
+
through: :user
|
|
34
|
+
|
|
35
|
+
enum status: [:pending, :accepted, :rejected]
|
|
36
|
+
|
|
37
|
+
def assignee
|
|
38
|
+
Assignee.for(user)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def time_dedicated
|
|
42
|
+
time_events.sum(&:total_seconds)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def time_dedicated_to(activity)
|
|
46
|
+
time_events.where(activity: activity).sum(&:total_seconds)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def can_change_status?
|
|
50
|
+
time_events.empty?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# rubocop:disable Lint/UselessAssignment
|
|
54
|
+
def self.sorted_by_status(*statuses)
|
|
55
|
+
accepted = self.accepted.sort_by(&:time_dedicated).reverse
|
|
56
|
+
pending = self.pending
|
|
57
|
+
rejected = self.rejected
|
|
58
|
+
|
|
59
|
+
statuses.map { |status| send(status) }.sum
|
|
60
|
+
end
|
|
61
|
+
# rubocop:enable Lint/UselessAssignment
|
|
62
|
+
|
|
63
|
+
def self.log_presenter_class_for(_log)
|
|
64
|
+
Decidim::TimeTracker::AdminLog::AssignationPresenter
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
# The data store for an assignee in the Decidim::TimeTracker component.
|
|
6
|
+
class Assignee < ApplicationRecord
|
|
7
|
+
self.table_name = :decidim_time_tracker_assignees
|
|
8
|
+
|
|
9
|
+
belongs_to :user,
|
|
10
|
+
foreign_key: "decidim_user_id",
|
|
11
|
+
class_name: "Decidim::User"
|
|
12
|
+
|
|
13
|
+
has_many :tos_acceptances,
|
|
14
|
+
class_name: "Decidim::TimeTracker::TosAcceptance"
|
|
15
|
+
|
|
16
|
+
def self.for(user)
|
|
17
|
+
find_or_create_by(user: user)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def tos_accepted_at(time_tracker)
|
|
21
|
+
tos_acceptances.find_by(time_tracker: time_tracker)&.created_at
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def tos_accepted?(time_tracker)
|
|
25
|
+
tos_acceptances.exists?(time_tracker: time_tracker)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
# The data store for a AssigneeData in the Decidim::TimeTracker component.
|
|
6
|
+
class AssigneeData < ApplicationRecord
|
|
7
|
+
include Decidim::Forms::HasQuestionnaire
|
|
8
|
+
|
|
9
|
+
self.table_name = :decidim_time_tracker_assignee_data
|
|
10
|
+
|
|
11
|
+
belongs_to :time_tracker,
|
|
12
|
+
class_name: "Decidim::TimeTracker::TimeTracker"
|
|
13
|
+
|
|
14
|
+
delegate :questions, :answers, to: :questionnaire
|
|
15
|
+
|
|
16
|
+
delegate :organization, :component, to: :time_tracker
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module TimeTracker
|
|
5
|
+
# The data store for a Milestone in the Decidim::TimeTracker component.
|
|
6
|
+
class Milestone < ApplicationRecord
|
|
7
|
+
include Decidim::HasAttachments
|
|
8
|
+
include Decidim::Resourceable
|
|
9
|
+
|
|
10
|
+
self.table_name = :decidim_time_tracker_milestones
|
|
11
|
+
|
|
12
|
+
belongs_to :user,
|
|
13
|
+
foreign_key: "decidim_user_id",
|
|
14
|
+
class_name: "Decidim::User"
|
|
15
|
+
|
|
16
|
+
belongs_to :activity,
|
|
17
|
+
class_name: "Decidim::TimeTracker::Activity"
|
|
18
|
+
|
|
19
|
+
has_one :task,
|
|
20
|
+
through: :activity,
|
|
21
|
+
class_name: "Decidim::TimeTracker::Task"
|
|
22
|
+
|
|
23
|
+
delegate :component, to: :task
|
|
24
|
+
delegate :organization, to: :user
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|