Browse Source

Add mass importing activities, activity index

Maarten van den Berg 7 years ago
parent
commit
3038e2830f

+ 3 - 2
Gemfile

48
 # Use Mailgun for mail
48
 # Use Mailgun for mail
49
 gem 'mailgun_rails'
49
 gem 'mailgun_rails'
50
 
50
 
51
-# Use Capistrano for deployment
52
-# gem 'capistrano-rails', group: :development
51
+# Pagination
52
+gem 'will_paginate', '~> 3.1.0'
53
+gem 'bootstrap-will_paginate'
53
 
54
 
54
 group :development, :test do
55
 group :development, :test do
55
   # Call 'byebug' anywhere in the code to stop execution and get a debugger console
56
   # Call 'byebug' anywhere in the code to stop execution and get a debugger console

+ 5 - 0
Gemfile.lock

50
     bootstrap-sass (3.3.7)
50
     bootstrap-sass (3.3.7)
51
       autoprefixer-rails (>= 5.2.1)
51
       autoprefixer-rails (>= 5.2.1)
52
       sass (>= 3.3.4)
52
       sass (>= 3.3.4)
53
+    bootstrap-will_paginate (1.0.0)
54
+      will_paginate
53
     builder (3.2.3)
55
     builder (3.2.3)
54
     byebug (9.0.6)
56
     byebug (9.0.6)
55
     coffee-rails (4.2.1)
57
     coffee-rails (4.2.1)
185
     websocket-driver (0.6.5)
187
     websocket-driver (0.6.5)
186
       websocket-extensions (>= 0.1.0)
188
       websocket-extensions (>= 0.1.0)
187
     websocket-extensions (0.1.2)
189
     websocket-extensions (0.1.2)
190
+    will_paginate (3.1.6)
188
     x-editable-rails (1.5.5)
191
     x-editable-rails (1.5.5)
189
       railties
192
       railties
190
     yard (0.9.8)
193
     yard (0.9.8)
197
   babel-transpiler
200
   babel-transpiler
198
   bcrypt (~> 3.1.7)
201
   bcrypt (~> 3.1.7)
199
   bootstrap-sass (~> 3.3.6)
202
   bootstrap-sass (~> 3.3.6)
203
+  bootstrap-will_paginate
200
   byebug
204
   byebug
201
   coffee-rails (~> 4.2)
205
   coffee-rails (~> 4.2)
202
   faker
206
   faker
218
   tzinfo-data
222
   tzinfo-data
219
   uglifier (>= 1.3.0)
223
   uglifier (>= 1.3.0)
220
   web-console
224
   web-console
225
+  will_paginate (~> 3.1.0)
221
   x-editable-rails
226
   x-editable-rails
222
   yard
227
   yard
223
 
228
 

+ 18 - 1
app/controllers/activities_controller.rb

3
   before_action :set_activity, only: [:show, :edit, :update, :destroy, :presence]
3
   before_action :set_activity, only: [:show, :edit, :update, :destroy, :presence]
4
   before_action :set_group
4
   before_action :set_group
5
   before_action :require_membership!
5
   before_action :require_membership!
6
+  before_action :require_leader!, only: [:mass_new, :mass_create, :new, :create, :destroy]
6
 
7
 
7
   # GET /groups/:id/activities
8
   # GET /groups/:id/activities
8
   # GET /activities.json
9
   # GET /activities.json
9
   def index
10
   def index
10
-    @activities = @group.activities
11
+    @activities = @group.activities.paginate(page: params[:page], per_page: 5)
11
   end
12
   end
12
 
13
 
13
   # GET /activities/1
14
   # GET /activities/1
130
     participant.update_attributes(params.permit(:notes, :attending))
131
     participant.update_attributes(params.permit(:notes, :attending))
131
   end
132
   end
132
 
133
 
134
+  def mass_new
135
+  end
136
+
137
+  def mass_create
138
+    require 'csv'
139
+    uploaded_io = params[:spreadsheet]
140
+    result = Activity.from_csv(uploaded_io.read, @group)
141
+
142
+    result.each do |a|
143
+      a.save!
144
+    end
145
+
146
+    flash_message(:success, I18n.t('activities.mass_imported', count: result.count))
147
+    redirect_to group_activities_path(@group)
148
+  end
149
+
133
   private
150
   private
134
     # Use callbacks to share common setup or constraints between actions.
151
     # Use callbacks to share common setup or constraints between actions.
135
     def set_activity
152
     def set_activity

+ 41 - 0
app/models/activity.rb

70
     self.participants.group(:attending).count
70
     self.participants.group(:attending).count
71
   end
71
   end
72
 
72
 
73
+  # Return participants attending, absent, unknown
74
+  def human_state_counts
75
+    c = self.state_counts
76
+    p = c[true]
77
+    a = c[false]
78
+    u = c[nil]
79
+    return "#{p or 0}, #{a or 0}, #{u or 0}"
80
+  end
81
+
73
   # Determine whether the passed Person may change this activity.
82
   # Determine whether the passed Person may change this activity.
74
   def may_change?(person)
83
   def may_change?(person)
75
     person.is_admin ||
84
     person.is_admin ||
94
     end
103
     end
95
   end
104
   end
96
 
105
 
106
+  # Create multiple Activities from data in a CSV file, assign to a group, return.
107
+  def self.from_csv(content, group)
108
+    reader = CSV.parse(content, {headers: true, skip_blanks: true})
109
+
110
+    result = []
111
+    reader.each do |row|
112
+      a = Activity.new
113
+      a.group       = group
114
+      a.name        = row['name']
115
+      a.description = row['description']
116
+      a.location    = row['location']
117
+
118
+      sd            = Date.strptime(row['start_date'])
119
+      st            = Time.strptime(row['start_time'], '%H:%M')
120
+      a.start       = DateTime.new(sd.year, sd.month, sd.day, st.hour, st.min)
121
+
122
+      if not row['end_date'].blank?
123
+        ed          = Date.strptime(row['end_date'])
124
+        et          = Time.strptime(row['end_time'], '%H:%M')
125
+        a.end       = DateTime.new(ed.year, ed.month, ed.day, et.hour, et.min)
126
+      end
127
+
128
+      dd            = Date.strptime(row['deadline_date'])
129
+      dt            = Time.strptime(row['deadline_time'], '%H:%M')
130
+      a.deadline    = DateTime.new(dd.year, dd.month, dd.day, dt.hour, dt.min)
131
+
132
+      result << a
133
+    end
134
+
135
+    result
136
+  end
137
+
97
   private
138
   private
98
   # Assert that the deadline for participants to change the deadline, if any,
139
   # Assert that the deadline for participants to change the deadline, if any,
99
   # is set before the event starts.
140
   # is set before the event starts.

+ 0 - 23
app/views/activities/index.html.erb

1
-<h1>Activities</h1>
2
-
3
-<table class="table">
4
-  <thead>
5
-    <tr>
6
-      <th colspan="3"></th>
7
-    </tr>
8
-  </thead>
9
-
10
-  <tbody>
11
-    <% @activities.each do |activity| %>
12
-      <tr>
13
-        <td><%= link_to t(:show), group_activity_path(@group, activity) %></td>
14
-        <td><%= link_to t(:edit), edit_group_activity_path(@group, activity) %></td>
15
-        <td><%= link_to t(:destroy), group_activity_path(@group, activity), method: :delete, data: { confirm: t(:areyousure) } %></td>
16
-      </tr>
17
-    <% end %>
18
-  </tbody>
19
-</table>
20
-
21
-<br>
22
-
23
-<%= link_to t('activities.new'), new_group_activity_path(@group) %>

+ 43 - 0
app/views/activities/index.html.haml

1
+%h1
2
+  = t 'activerecord.models.activity.other'
3
+
4
+= link_to new_group_activity_path(@group), class: 'btn btn-default pull-right' do
5
+  %i.fa.fa-plus
6
+  = t 'activities.new'
7
+
8
+- isleader = @group.leaders.include?(current_person) || current_person.is_admin?
9
+
10
+%table.table
11
+  %thead
12
+    %tr
13
+      %th
14
+        = t 'activerecord.attributes.activities.name'
15
+
16
+      %th
17
+        = t 'activerecord.attributes.activities.start'
18
+
19
+      %th
20
+        P/A/?
21
+
22
+      - if isleader
23
+        %th
24
+
25
+  %tbody
26
+    - @activities.each do |a|
27
+      %tr
28
+        %td
29
+          = link_to a.name, group_activity_path(@group, a)
30
+
31
+        %td
32
+          = l a.start, format: :short
33
+
34
+        %td
35
+          = a.human_state_counts
36
+
37
+        - if isleader
38
+          %td
39
+            = link_to edit_group_activity_path(@group, a) do
40
+              %i.fa.fa-pencil
41
+
42
+= will_paginate @activities
43
+

+ 6 - 0
app/views/activities/mass_new.html.haml

1
+.container
2
+  .row
3
+    .col-md-12
4
+      = form_tag(group_activities_mass_new_path(@group), method: 'post', multipart: true) do
5
+        = file_field_tag 'spreadsheet'
6
+        = submit_tag

+ 1 - 0
config/locales/activities/en.yml

7
     created: "Activity created."
7
     created: "Activity created."
8
     updated: "Activity updated."
8
     updated: "Activity updated."
9
     destroyed: "Activity destroyed."
9
     destroyed: "Activity destroyed."
10
+    mass_imported: "%{count} activities created!"
10
 
11
 
11
     upcoming_yours: "Upcoming activities organized by you"
12
     upcoming_yours: "Upcoming activities organized by you"
12
     yours: "Your activities"
13
     yours: "Your activities"

+ 1 - 0
config/locales/activities/nl.yml

7
     created: "Activiteit aangemaakt."
7
     created: "Activiteit aangemaakt."
8
     updated: "Activiteit bijgewerkt."
8
     updated: "Activiteit bijgewerkt."
9
     destroyed: "Activiteit verwijderd."
9
     destroyed: "Activiteit verwijderd."
10
+    mass_imported: "%{count} activiteiten aangemaakt!"
10
 
11
 
11
     upcoming_yours: "Aankomende activiteiten georganiseerd door jou"
12
     upcoming_yours: "Aankomende activiteiten georganiseerd door jou"
12
     yours: "Jouw activiteiten"
13
     yours: "Jouw activiteiten"

+ 3 - 0
config/routes.rb

37
       post 'demote', to: 'members#demote', on: :member
37
       post 'demote', to: 'members#demote', on: :member
38
     end
38
     end
39
 
39
 
40
+    get 'activities/mass_new', to: 'activities#mass_new'
41
+    post 'activities/mass_new', to: 'activities#mass_create'
42
+
40
     resources :activities do
43
     resources :activities do
41
       post 'change_organizer', to: 'activities#change_organizer'
44
       post 'change_organizer', to: 'activities#change_organizer'
42
       put 'presence', to: 'activities#presence', on: :member
45
       put 'presence', to: 'activities#presence', on: :member

+ 2 - 0
public/batch_activities.csv

1
+name,description,location,start_date,start_time,end_date,end_time,deadline_date,deadline_time
2
+Name,Description,Location,2017-12-31,12:34:00,2017-12-31,12:43:00,2017-12-28,13:37:00