backlog 0.33.1 → 0.34
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.
- data/History.txt +14 -0
- data/app/controllers/dashboard_controller.rb +17 -0
- data/app/helpers/dashboard_helper.rb +2 -0
- data/app/models/work.rb +39 -0
- data/app/models/work_lock_nagger.rb +4 -3
- data/app/views/dashboard/show.rhtml +24 -0
- data/app/views/tasks/edit.rhtml +1 -1
- data/app/views/works/_buttons.rhtml +1 -7
- data/lang/en.yaml +6 -0
- data/lang/no.yaml +6 -0
- data/lib/version_from_history.rb +1 -1
- data/public/images/flag_red.png +0 -0
- data/public/images/flag_yellow.png +0 -0
- data/public/images/user group.png +0 -0
- data/public/stylesheets/backlog.css +37 -0
- data/test/functional/dashboard_controller_test.rb +18 -0
- metadata +17 -4
data/History.txt
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
== 0.34 2008-07-18
|
|
2
|
+
|
|
3
|
+
== Features
|
|
4
|
+
|
|
5
|
+
* Added dashboard controller and view, showing summary of hours for users you're currently monitoring
|
|
6
|
+
|
|
7
|
+
=== Fixes
|
|
8
|
+
|
|
9
|
+
* Fixed exception in WorkLogNagger when no config file is present.
|
|
10
|
+
* Made link from period to daily work sheet a detour.
|
|
11
|
+
* Fixed bug when assigning a subtask from a backlog to a sprint.
|
|
12
|
+
|
|
1
13
|
== 0.33.1 2008-05-28
|
|
2
14
|
|
|
3
15
|
=== Fixes
|
|
@@ -10,6 +22,7 @@
|
|
|
10
22
|
* Fixed filtering on works list and excel export to use work record start date instead of completetion date.
|
|
11
23
|
* Fixed bug where a display of a weekly work sheet for a non-existing user would produce an application error.
|
|
12
24
|
|
|
25
|
+
|
|
13
26
|
== 0.33.0 2008-05-09
|
|
14
27
|
|
|
15
28
|
== Features
|
|
@@ -31,6 +44,7 @@
|
|
|
31
44
|
* Fixed migrations to work with PostgreSQL 8.3
|
|
32
45
|
* Removed automatic setting of "completed at" time for works.
|
|
33
46
|
|
|
47
|
+
|
|
34
48
|
== 0.32.0 2008-05-08
|
|
35
49
|
|
|
36
50
|
== Features
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class DashboardController < ApplicationController
|
|
2
|
+
def show
|
|
3
|
+
date = Date.today
|
|
4
|
+
year = date.year
|
|
5
|
+
week = date.cweek
|
|
6
|
+
last_week = 1.week.ago.to_date.cweek
|
|
7
|
+
last_year = week == 1 ? year - 1 : year
|
|
8
|
+
month = Date.today.month
|
|
9
|
+
last_month = month == 1 ? 12 : month - 1
|
|
10
|
+
last_months_year = month == 1 ? year - 1 : year
|
|
11
|
+
@week_day = Date.today.wday
|
|
12
|
+
@users = []
|
|
13
|
+
for user in current_user.work_lock_subscriptions
|
|
14
|
+
@users<<[user, Work.work_sum_per_week(last_year, last_week, user), Work.work_sum_per_week(year, week, user), Work.work_sum_per_day(date, user), Work.work_sum_per_month(year, month, user), Work.work_sum_per_month(last_months_year, last_month, user), Work.invoice_work_sum_per_week(last_year, last_week, user), Work.invoice_work_sum_per_week(year, week, user), Work.invoice_work_sum_per_day(date, user), Work.invoice_work_sum_per_month(year, month, user), Work.invoice_work_sum_per_month(last_months_year, last_month, user)]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/app/models/work.rb
CHANGED
|
@@ -107,6 +107,45 @@ class Work < ActiveRecord::Base
|
|
|
107
107
|
Work.find(:all, :conditions => "started_on = '#{date}' AND user_id = #{user.id}",
|
|
108
108
|
:order => 'start_time, completed_at')
|
|
109
109
|
end
|
|
110
|
+
|
|
111
|
+
def self.work_sum_per_month(year, month_no, user = current_user)
|
|
112
|
+
first = Date.new(year, month_no, 1)
|
|
113
|
+
last = Date.new(year, month_no, -1)
|
|
114
|
+
count = find_by_sql("select sum(hours) from works where completed_at IS NOT NULL AND started_on BETWEEN '#{first}' AND '#{last}' AND user_id = #{user.id}")[0]['sum']
|
|
115
|
+
count ? count : 0
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.invoice_work_sum_per_month(year, month_no, user = current_user)
|
|
119
|
+
first = Date.new(year, month_no, 1)
|
|
120
|
+
last = Date.new(year, month_no, -1)
|
|
121
|
+
count = find_by_sql("select sum(hours) from works where invoice = true AND completed_at IS NOT NULL AND started_on BETWEEN '#{first}' AND '#{last}' AND user_id = #{user.id}")[0]['sum']
|
|
122
|
+
count ? count : 0
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.work_sum_per_week(year, week_no, user = current_user)
|
|
126
|
+
first = Date.commercial(year, week_no, 1)
|
|
127
|
+
last = first + 6
|
|
128
|
+
count = find_by_sql("select sum(hours) from works where completed_at IS NOT NULL AND started_on BETWEEN '#{first}' AND '#{last}' AND user_id = #{user.id}")[0]['sum']
|
|
129
|
+
count ? count : 0
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def self.invoice_work_sum_per_week(year, week_no, user = current_user)
|
|
133
|
+
first = Date.commercial(year, week_no, 1)
|
|
134
|
+
last = first + 6
|
|
135
|
+
count = find_by_sql("select sum(hours) from works where invoice = true AND completed_at IS NOT NULL AND started_on BETWEEN '#{first}' AND '#{last}' AND user_id = #{user.id}")[0]['sum']
|
|
136
|
+
count ? count : 0
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def self.work_sum_per_day(date, user = current_user)
|
|
140
|
+
count = find_by_sql("select sum(hours) from works where completed_at IS NOT NULL AND started_on = '#{date}' AND user_id = #{user.id}")[0]['sum']
|
|
141
|
+
count ? count : 0
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def self.invoice_work_sum_per_day(date, user = current_user)
|
|
145
|
+
count = find_by_sql("select sum(hours) from works where invoice = true AND completed_at IS NOT NULL AND started_on = '#{date}' AND user_id = #{user.id}")[0]['sum']
|
|
146
|
+
count ? count : 0
|
|
147
|
+
end
|
|
148
|
+
|
|
110
149
|
|
|
111
150
|
def started?
|
|
112
151
|
completed_at.nil?
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
class WorkLockNagger
|
|
2
2
|
include ActionController::UrlWriter
|
|
3
3
|
|
|
4
|
-
def initialize
|
|
4
|
+
def initialize(start_delay = 1)
|
|
5
|
+
@start_delay = start_delay
|
|
5
6
|
end
|
|
6
7
|
|
|
7
8
|
def nag
|
|
8
|
-
sleep
|
|
9
|
+
sleep @start_delay.minute
|
|
9
10
|
puts "Work Lock Nagger started"
|
|
10
11
|
begin
|
|
11
12
|
if File.exists? APP_CONFIG_FILE
|
|
@@ -16,7 +17,7 @@ class WorkLockNagger
|
|
|
16
17
|
url = app_url + 'works/weekly_work_sheet'
|
|
17
18
|
else
|
|
18
19
|
host = Socket::gethostname
|
|
19
|
-
port = config[:port] || 3000
|
|
20
|
+
port = config && config[:port] || 3000
|
|
20
21
|
url = url_for(:host => host, :port => port, :controller => 'works', :action => :weekly_work_sheet)
|
|
21
22
|
end
|
|
22
23
|
rescue Exception => e
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<% @page_title = "#{l :dashboard}" %>
|
|
2
|
+
|
|
3
|
+
<h1>Dashboard</h1>
|
|
4
|
+
|
|
5
|
+
<div style="background-color: white;">
|
|
6
|
+
<% for user, hours_last_week, hours_this_week, hours_today, hours_this_month, hours_last_month, invoice_hours_last_week, invoice_hours_this_week, invoice_hours_today, invoice_hours_this_month, invoice_hours_last_month in @users %>
|
|
7
|
+
<div class="user_box">
|
|
8
|
+
<dl>
|
|
9
|
+
<dt><img src="/images/user.png" height="20px" /><%=h user.name %></dt>
|
|
10
|
+
<dd>
|
|
11
|
+
<table>
|
|
12
|
+
<tr><td><%= l :hours_last_week %>:</td><td><%=hours_last_week%>/<%=invoice_hours_last_week%></td><td><%= hours_last_week.nil? || hours_last_week <= 0 ? '<img src="/images/flag_red.png" height="20px" />' : '' %><%= hours_last_week.nil? == false && hours_last_week > 0 && hours_last_week < 30 ? '<img src="/images/flag_yellow.png" height="20px" />' : '' %></td></tr>
|
|
13
|
+
<tr><td><%= l :hours_this_week %>:</td><td><%=hours_this_week%>/<%=invoice_hours_this_week%></td><td><%= @week_day > 1 && (hours_this_week.nil? || hours_this_week <= 0) ? '<img src="/images/flag_red.png" height="20px" />' : '' %></td></tr>
|
|
14
|
+
<tr><td><%= l :hours_today %>:</td><td><%=hours_today%>/<%=invoice_hours_today%></td><td></td></tr>
|
|
15
|
+
<tr><td><%= l :hours_last_month %>:</td><td><%=hours_last_month%>/<%=invoice_hours_last_month%></td><td></td></tr>
|
|
16
|
+
<tr><td><%= l :hours_this_month %>:</td><td><%=hours_this_month%>/<%=invoice_hours_this_month%></td><td></tr>
|
|
17
|
+
</table>
|
|
18
|
+
</dd>
|
|
19
|
+
</dl>
|
|
20
|
+
</div>
|
|
21
|
+
<% end %>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<br />
|
data/app/views/tasks/edit.rhtml
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
<%=l :started_at%> <%=f.text_field 'start_time', :id => "work_#{@task.started_work.id}_start_time", :value => @task.started_work.started_at ? @task.started_work.started_at.strftime('%H:%M') : '',
|
|
35
35
|
:class => :task_time, :maxlength => 5 %>
|
|
36
36
|
<% end %>
|
|
37
|
-
<% elsif @task.track_times? && (@task.
|
|
37
|
+
<% elsif @task.track_times? && (@task.period.nil? || @task.period.active?) %>
|
|
38
38
|
<%=image_link_to_remote 'hammer.png', l(:start_work), {:controller => 'tasks', :action => :start_work, :id => @task.id}, nil, true %>
|
|
39
39
|
<% end -%>
|
|
40
40
|
<% end -%>
|
|
@@ -1,8 +1,2 @@
|
|
|
1
1
|
<%=image_detour_to('hammer.png', l(:weekly_work_sheet), :controller => 'works', :action => :weekly_work_sheet)%>
|
|
2
|
-
|
|
3
|
-
<%=image_detour_to('hammer.png', l(:weekly_work_sheet), :controller => 'works', :action => :weekly_work_sheet_details)%>
|
|
4
|
-
-->
|
|
5
|
-
<%=link_to(image_tag(url_for("hammer.png"), :alt => l(:daily_work_sheet), :title => l(:daily_work_sheet), :class => 'image-submit'), :controller => 'works', :action => :daily_work_sheet)%>
|
|
6
|
-
<!--
|
|
7
|
-
<%=link_to(image_tag(url_for("hammer.png"), :alt => l(:edit_works), :title => l(:edit_works), :class => 'image-submit'), :controller => 'periods', :action => 'list_work', :id => (@period ? @period.id : (@backlog && @backlog.periods.first ? @backlog.periods.first.id : nil))) if @period || @backlog%>
|
|
8
|
-
-->
|
|
2
|
+
<%=image_detour_to('hammer.png', l(:daily_work_sheet), :controller => 'works', :action => :daily_work_sheet)%>
|
data/lang/en.yaml
CHANGED
|
@@ -24,6 +24,7 @@ confirmation: Are you sure?
|
|
|
24
24
|
created_by: Created by
|
|
25
25
|
customer: Customer
|
|
26
26
|
daily_work_sheet: Daily work sheet
|
|
27
|
+
dashboard: Dashboard
|
|
27
28
|
delete: Delete
|
|
28
29
|
description: Description
|
|
29
30
|
details: Details
|
|
@@ -51,6 +52,11 @@ holiday: Holiday
|
|
|
51
52
|
holidays_used: Holidays used
|
|
52
53
|
home: Home
|
|
53
54
|
hours: Hours
|
|
55
|
+
hours_last_month: Hours last month
|
|
56
|
+
hours_last_week: Hours last week
|
|
57
|
+
hours_this_month: Hours this month
|
|
58
|
+
hours_this_week: Hours this week
|
|
59
|
+
hours_today: Hours today
|
|
54
60
|
invite: Invite
|
|
55
61
|
invoice: Invoice
|
|
56
62
|
invoice_code: Invoice Code
|
data/lang/no.yaml
CHANGED
|
@@ -24,6 +24,7 @@ confirmation: Er du sikker?
|
|
|
24
24
|
created_by: Opprettet av
|
|
25
25
|
customer: Kunde
|
|
26
26
|
daily_work_sheet: Timeføringsskjema
|
|
27
|
+
dashboard: Dashboard
|
|
27
28
|
delete: Slett
|
|
28
29
|
description: Beskrivelse
|
|
29
30
|
details: Detaljer
|
|
@@ -51,6 +52,11 @@ holiday: Ferie
|
|
|
51
52
|
holidays_used: Brukte feriedager
|
|
52
53
|
home: Hjem
|
|
53
54
|
hours: Timer
|
|
55
|
+
hours_last_month: Timer forrige måned
|
|
56
|
+
hours_last_week: Timer forrige uke
|
|
57
|
+
hours_this_month: Timer denne måned
|
|
58
|
+
hours_this_week: Timer denne uke
|
|
59
|
+
hours_today: Timer i dag
|
|
54
60
|
invite: Invitèr
|
|
55
61
|
invoice: Fakturerbart
|
|
56
62
|
invoice_code: Faktureringskode
|
data/lib/version_from_history.rb
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -96,3 +96,40 @@ div.progressBar div.background {
|
|
|
96
96
|
height: 18px;
|
|
97
97
|
width: 0%;
|
|
98
98
|
}
|
|
99
|
+
|
|
100
|
+
.user_box {
|
|
101
|
+
float:left;
|
|
102
|
+
width:250px;
|
|
103
|
+
height: 150px;
|
|
104
|
+
padding: 10px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.user_box dt {
|
|
108
|
+
background-color: #FFFFFF;
|
|
109
|
+
color:#444444;
|
|
110
|
+
display:inline;
|
|
111
|
+
font-family:Verdana,sans-serif;
|
|
112
|
+
font-size:1.1em;
|
|
113
|
+
font-weight:bold;
|
|
114
|
+
margin-left:10px;
|
|
115
|
+
padding-left:1px;
|
|
116
|
+
padding-right:5px;
|
|
117
|
+
position:relative;
|
|
118
|
+
top:-1em;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.user_box dl {
|
|
122
|
+
background:#FFFFFF none repeat scroll 0% 0%;
|
|
123
|
+
border:1px solid #D0D0D0;
|
|
124
|
+
display:block;
|
|
125
|
+
margin:0px;
|
|
126
|
+
padding:0px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.user_box dd {
|
|
130
|
+
background:#FFFFFF none repeat scroll 0% 0%;
|
|
131
|
+
color:#444444;
|
|
132
|
+
margin:0px;
|
|
133
|
+
padding:5px 10px;
|
|
134
|
+
text-align:left;
|
|
135
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
require 'dashboard_controller'
|
|
3
|
+
|
|
4
|
+
# Re-raise errors caught by the controller.
|
|
5
|
+
class DashboardController; def rescue_action(e) raise e end; end
|
|
6
|
+
|
|
7
|
+
class DashboardControllerTest < Test::Unit::TestCase
|
|
8
|
+
def setup
|
|
9
|
+
@controller = DashboardController.new
|
|
10
|
+
@request = ActionController::TestRequest.new
|
|
11
|
+
@response = ActionController::TestResponse.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Replace this with your real tests.
|
|
15
|
+
def test_truth
|
|
16
|
+
assert true
|
|
17
|
+
end
|
|
18
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: backlog
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: "0.34"
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Uwe Kubosch
|
|
@@ -9,11 +9,12 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2008-
|
|
12
|
+
date: 2008-09-10 00:00:00 +02:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: rails
|
|
17
|
+
type: :runtime
|
|
17
18
|
version_requirement:
|
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
|
19
20
|
requirements:
|
|
@@ -23,6 +24,7 @@ dependencies:
|
|
|
23
24
|
version:
|
|
24
25
|
- !ruby/object:Gem::Dependency
|
|
25
26
|
name: gruff
|
|
27
|
+
type: :runtime
|
|
26
28
|
version_requirement:
|
|
27
29
|
version_requirements: !ruby/object:Gem::Requirement
|
|
28
30
|
requirements:
|
|
@@ -32,6 +34,7 @@ dependencies:
|
|
|
32
34
|
version:
|
|
33
35
|
- !ruby/object:Gem::Dependency
|
|
34
36
|
name: postgres
|
|
37
|
+
type: :runtime
|
|
35
38
|
version_requirement:
|
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
40
|
requirements:
|
|
@@ -41,6 +44,7 @@ dependencies:
|
|
|
41
44
|
version:
|
|
42
45
|
- !ruby/object:Gem::Dependency
|
|
43
46
|
name: slave
|
|
47
|
+
type: :runtime
|
|
44
48
|
version_requirement:
|
|
45
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
46
50
|
requirements:
|
|
@@ -50,12 +54,13 @@ dependencies:
|
|
|
50
54
|
version:
|
|
51
55
|
- !ruby/object:Gem::Dependency
|
|
52
56
|
name: hoe
|
|
57
|
+
type: :development
|
|
53
58
|
version_requirement:
|
|
54
59
|
version_requirements: !ruby/object:Gem::Requirement
|
|
55
60
|
requirements:
|
|
56
61
|
- - ">="
|
|
57
62
|
- !ruby/object:Gem::Version
|
|
58
|
-
version: 1.
|
|
63
|
+
version: 1.7.0
|
|
59
64
|
version:
|
|
60
65
|
description: Welcome to Backlog! Backlog is a tool to help you collect and organize all your tasks, whether you are a single person or a small or large group.
|
|
61
66
|
email: uwe@kubosch.no
|
|
@@ -109,6 +114,8 @@ files:
|
|
|
109
114
|
- app/views/work_locks/edit.rhtml
|
|
110
115
|
- app/views/work_locks/_form.rhtml
|
|
111
116
|
- app/views/work_locks/show.rhtml
|
|
117
|
+
- app/views/dashboard
|
|
118
|
+
- app/views/dashboard/show.rhtml
|
|
112
119
|
- app/views/absences
|
|
113
120
|
- app/views/absences/new.rhtml
|
|
114
121
|
- app/views/absences/list.rhtml
|
|
@@ -224,6 +231,7 @@ files:
|
|
|
224
231
|
- app/controllers/user_controller.rb
|
|
225
232
|
- app/controllers/customers_controller.rb
|
|
226
233
|
- app/controllers/welcome_controller.rb
|
|
234
|
+
- app/controllers/dashboard_controller.rb
|
|
227
235
|
- app/controllers/backlogs_controller.rb
|
|
228
236
|
- app/controllers/estimates_controller.rb
|
|
229
237
|
- app/controllers/application.rb~
|
|
@@ -239,6 +247,7 @@ files:
|
|
|
239
247
|
- app/helpers
|
|
240
248
|
- app/helpers/estimates_helper.rb
|
|
241
249
|
- app/helpers/application_helper.rb
|
|
250
|
+
- app/helpers/dashboard_helper.rb
|
|
242
251
|
- app/helpers/groups_helper.rb
|
|
243
252
|
- app/helpers/work_locks_helper.rb
|
|
244
253
|
- app/helpers/customers_helper.rb
|
|
@@ -323,6 +332,7 @@ files:
|
|
|
323
332
|
- test/functional/periods_controller_test.rb
|
|
324
333
|
- test/functional/estimates_controller_test.rb
|
|
325
334
|
- test/functional/task_files_controller_test.rb
|
|
335
|
+
- test/functional/dashboard_controller_test.rb
|
|
326
336
|
- test/functional/works_controller_test.rb
|
|
327
337
|
- test/functional/tasks_controller_test.rb
|
|
328
338
|
- test/functional/welcome_controller_test.rb
|
|
@@ -602,9 +612,11 @@ files:
|
|
|
602
612
|
- public/images/grab_gray.png
|
|
603
613
|
- public/images/arrow07_4.png
|
|
604
614
|
- public/images/checkmark.png
|
|
615
|
+
- public/images/flag_red.png
|
|
605
616
|
- public/images/appunti_architetto_franc_01.svg
|
|
606
617
|
- public/images/group.png
|
|
607
618
|
- public/images/ernes_stop_org.png
|
|
619
|
+
- public/images/flag_yellow.png
|
|
608
620
|
- public/images/user.png
|
|
609
621
|
- public/images/period.png
|
|
610
622
|
- public/images/clipboard.svg
|
|
@@ -628,6 +640,7 @@ files:
|
|
|
628
640
|
- public/images/work_account.png
|
|
629
641
|
- public/images/ernes_stop.png
|
|
630
642
|
- public/images/cestino_pieno_architetto_01.svg
|
|
643
|
+
- public/images/user group.png
|
|
631
644
|
- public/images/eraser_org.png
|
|
632
645
|
- public/images/person.org.png
|
|
633
646
|
- public/stylesheets
|
|
@@ -758,7 +771,7 @@ requirements:
|
|
|
758
771
|
- ImageMagick
|
|
759
772
|
- PostgreSQL
|
|
760
773
|
rubyforge_project: backlog
|
|
761
|
-
rubygems_version: 1.
|
|
774
|
+
rubygems_version: 1.2.0
|
|
762
775
|
signing_key:
|
|
763
776
|
specification_version: 2
|
|
764
777
|
summary: Application to aid collecting, processing, organizing, reviewing and doing tasks.
|