gestion 1.9.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (233) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +48 -0
  3. data/.project +14 -0
  4. data/Binaries/adduser_to_buzz +15 -0
  5. data/Binaries/backup +7 -0
  6. data/Binaries/check_gestion +8 -0
  7. data/Binaries/gestion.gnumail +22 -0
  8. data/Binaries/gestion.logrotate +34 -0
  9. data/Binaries/gestion.service +12 -0
  10. data/Binaries/gestion_update.rb +183 -0
  11. data/Binaries/gestion_update.service +10 -0
  12. data/Binaries/get_compta +11 -0
  13. data/Binaries/kill_gestion +16 -0
  14. data/Binaries/ldap/add_indexes +51 -0
  15. data/Binaries/ldap/backup +2 -0
  16. data/Binaries/ldap/install_ldap +92 -0
  17. data/Binaries/ldap/restore +7 -0
  18. data/Binaries/lib_backup +5 -0
  19. data/Binaries/log_scan_errors +8 -0
  20. data/Binaries/loop_gestion +64 -0
  21. data/Binaries/onetimers/sync_courses_from_compta.rb +74 -0
  22. data/Binaries/onetimers/transfer_cash_from_ldap_to_csv +26 -0
  23. data/Binaries/reboot +5 -0
  24. data/Binaries/restore +3 -0
  25. data/Binaries/restore_do +22 -0
  26. data/Binaries/sort_events +31 -0
  27. data/Binaries/start_gestion +18 -0
  28. data/Binaries/swipe_gestion +18 -0
  29. data/Binaries/update_africompta +21 -0
  30. data/Binaries/update_users +3 -0
  31. data/Diplomas.src/accredited.odg +0 -0
  32. data/Diplomas.src/diploma.odg +0 -0
  33. data/Diplomas.src/label.odg +0 -0
  34. data/Diplomas.src/presence_sheet.ods +0 -0
  35. data/Diplomas.src/presence_sheet_small.ods +0 -0
  36. data/Diplomas.src/student_card.odg +0 -0
  37. data/Doc/130514-it-ideas.odt +0 -0
  38. data/Doc/Compta-cash.mm +179 -0
  39. data/Doc/General.odt +0 -0
  40. data/Entities/AccessGroups.rb +117 -0
  41. data/Entities/Activity.rb +178 -0
  42. data/Entities/ChatMsg.rb +142 -0
  43. data/Entities/Classroom.rb +11 -0
  44. data/Entities/Client.rb +19 -0
  45. data/Entities/Computer.rb +21 -0
  46. data/Entities/ConfigBase.rb +280 -0
  47. data/Entities/Course.rb +1588 -0
  48. data/Entities/CourseType.rb +171 -0
  49. data/Entities/DFiles.rb +466 -0
  50. data/Entities/FilesManage.rb +226 -0
  51. data/Entities/Grade.rb +186 -0
  52. data/Entities/Internet.rb +300 -0
  53. data/Entities/Netdev.rb +10 -0
  54. data/Entities/Person.rb +1175 -0
  55. data/Entities/Plug.rb +98 -0
  56. data/Entities/Quiz.rb +33 -0
  57. data/Entities/Recharges.rb +37 -0
  58. data/Entities/Report.rb +136 -0
  59. data/Entities/Room.rb +12 -0
  60. data/Entities/SMS.rb +30 -0
  61. data/Entities/ScheduleType.rb +33 -0
  62. data/Entities/Share.rb +120 -0
  63. data/Entities/Task.rb +51 -0
  64. data/Entities/Ticket.rb +72 -0
  65. data/Entities/Usage.rb +143 -0
  66. data/Entities/Worker.rb +29 -0
  67. data/Files/apache-profeda.conf +36 -0
  68. data/Files/label.erb +121 -0
  69. data/Files/label_notfound.erb +64 -0
  70. data/Files/label_notpassed.erb +84 -0
  71. data/Files/mobileinfo.erb +115 -0
  72. data/Files/smb.conf +333 -0
  73. data/Files/timetable.html +36 -0
  74. data/Files/timetable.js +239 -0
  75. data/Gemfile +12 -0
  76. data/Gemfile.dev +12 -0
  77. data/Gemfile.dev.lock +127 -0
  78. data/Gemfile.lock +127 -0
  79. data/Gemfile.prod +8 -0
  80. data/Gestion +35 -0
  81. data/Gestion.rb +220 -0
  82. data/INSTALL +40 -0
  83. data/Images/connection.xcf +0 -0
  84. data/Images/connection_no.png +0 -0
  85. data/Images/connection_wait.png +0 -0
  86. data/Images/connection_yes.png +0 -0
  87. data/Paths/Exas.rb +13 -0
  88. data/Paths/Files.rb +19 -0
  89. data/Paths/GetDiplomas.rb +20 -0
  90. data/Paths/Info.rb +114 -0
  91. data/Paths/Label.rb +187 -0
  92. data/Paths/MobileInfo.rb +19 -0
  93. data/Paths/internetCash.rb +34 -0
  94. data/Paths/internetWifi.rb +54 -0
  95. data/README.md +60 -0
  96. data/Rakefile +13 -0
  97. data/TODO +1391 -0
  98. data/Test/.gitignore +3 -0
  99. data/Test/Diplomas/base_gestion.odt +0 -0
  100. data/Test/Diplomas/base_report.odt +0 -0
  101. data/Test/Diplomas/carte_etudiant.odg +0 -0
  102. data/Test/Diplomas/exam_language.odt +0 -0
  103. data/Test/Diplomas/label.odg +0 -0
  104. data/Test/Diplomas/presence_sheet.ods +0 -0
  105. data/Test/Diplomas/presence_sheet_small.ods +0 -0
  106. data/Test/Diplomas/student_card.odg +0 -0
  107. data/Test/Manual/testMerge +18 -0
  108. data/Test/config_test.yaml +26 -0
  109. data/Test/db.testGestion +0 -0
  110. data/Test/dfiles/descs/avg-rescue.desc +10 -0
  111. data/Test/dfiles/descs/avg.desc +8 -0
  112. data/Test/dfiles/descs/driver.desc +8 -0
  113. data/Test/dfiles/descs/linuxmint.desc +7 -0
  114. data/Test/dfiles/files/avg-160203.exe +1 -0
  115. data/Test/dfiles/files/avg.iso +1 -0
  116. data/Test/dfiles/files/driver.exe +1 -0
  117. data/Test/dfiles/index_post.html +3 -0
  118. data/Test/dfiles/index_pre.html +8 -0
  119. data/Test/dfiles/priorities +5 -0
  120. data/Test/ge_activity.rb +124 -0
  121. data/Test/ge_chat.rb +106 -0
  122. data/Test/ge_compta.rb +67 -0
  123. data/Test/ge_configbase.rb +54 -0
  124. data/Test/ge_course.rb +1114 -0
  125. data/Test/ge_dfiles.rb +121 -0
  126. data/Test/ge_filesmanage.rb +180 -0
  127. data/Test/ge_info.rb +27 -0
  128. data/Test/ge_internet.rb +246 -0
  129. data/Test/ge_login.rb +55 -0
  130. data/Test/ge_person.rb +373 -0
  131. data/Test/ge_qvinfo.rb +28 -0
  132. data/Test/ge_report.rb +97 -0
  133. data/Test/ge_share.rb +27 -0
  134. data/Test/ge_sms.rb +34 -0
  135. data/Test/ge_tasks.rb +19 -0
  136. data/Test/ge_usage.rb +168 -0
  137. data/Test/ge_view.rb +46 -0
  138. data/Test/multiconf-captive +29 -0
  139. data/Test/test.conf +7 -0
  140. data/Test/test.rb +49 -0
  141. data/Test/test_bytes.png +0 -0
  142. data/VERSION +140 -0
  143. data/Views/Admin/Backup.rb +91 -0
  144. data/Views/Admin/Configuration.rb +44 -0
  145. data/Views/Admin/Credit.rb +32 -0
  146. data/Views/Admin/FilesManage.rb +219 -0
  147. data/Views/Admin/Function.rb +39 -0
  148. data/Views/Admin/Power.rb +49 -0
  149. data/Views/Admin/Printer.rb +37 -0
  150. data/Views/Admin/Server.rb +252 -0
  151. data/Views/Admin/Tabs.rb +5 -0
  152. data/Views/Admin/Update.rb +73 -0
  153. data/Views/Admin/UpdateSystem.rb +26 -0
  154. data/Views/Cashbox/Activity.rb +191 -0
  155. data/Views/Cashbox/Course.rb +141 -0
  156. data/Views/Cashbox/Credit.rb +79 -0
  157. data/Views/Cashbox/Report.rb +115 -0
  158. data/Views/Cashbox/Service.rb +105 -0
  159. data/Views/Cashbox/Tabs.rb +10 -0
  160. data/Views/Compta/Accounts.rb +36 -0
  161. data/Views/Compta/Course.rb +96 -0
  162. data/Views/Compta/Show.rb +6 -0
  163. data/Views/Compta/Transfer.rb +66 -0
  164. data/Views/Course/Diploma.rb +203 -0
  165. data/Views/Course/Grade.rb +401 -0
  166. data/Views/Course/Modify.rb +447 -0
  167. data/Views/Course/Print.rb +94 -0
  168. data/Views/Course/Responsible.rb +44 -0
  169. data/Views/Course/Stats.rb +76 -0
  170. data/Views/Course/Students.rb +92 -0
  171. data/Views/Course/Tabs.rb +220 -0
  172. data/Views/Internet/Access.rb +134 -0
  173. data/Views/Internet/ClassEdit.rb +24 -0
  174. data/Views/Internet/ClassUsers.rb +81 -0
  175. data/Views/Internet/Config.rb +32 -0
  176. data/Views/Internet/Mobile.rb +213 -0
  177. data/Views/Internet/Recharges.rb +49 -0
  178. data/Views/Internet/Tabs.rb +6 -0
  179. data/Views/Inventory/Computer.rb +24 -0
  180. data/Views/Inventory/Room.rb +18 -0
  181. data/Views/Inventory/Tabs.rb +9 -0
  182. data/Views/Inventory/TicketClosed.rb +7 -0
  183. data/Views/Inventory/TicketOpen.rb +23 -0
  184. data/Views/Library/Person.rb +36 -0
  185. data/Views/Library/Tabs.rb +7 -0
  186. data/Views/Network/Block.rb +87 -0
  187. data/Views/Network/Netdevs.rb +21 -0
  188. data/Views/Network/Restriction.rb +37 -0
  189. data/Views/Network/Share.rb +167 -0
  190. data/Views/Network/Tables.rb +28 -0
  191. data/Views/Network/Tabs.rb +6 -0
  192. data/Views/Person/Admin.rb +99 -0
  193. data/Views/Person/Center.rb +48 -0
  194. data/Views/Person/Course.rb +72 -0
  195. data/Views/Person/Modify.rb +153 -0
  196. data/Views/Person/Tabs.rb +162 -0
  197. data/Views/Report/ComptaExecutive.rb +221 -0
  198. data/Views/Report/ComptaFlat.rb +79 -0
  199. data/Views/Report/ReportCourse.rb +47 -0
  200. data/Views/Report/Tabs.rb +8 -0
  201. data/Views/Report/Usage.rb +52 -0
  202. data/Views/Report/UsageCases.rb +59 -0
  203. data/Views/Self/Cash.rb +67 -0
  204. data/Views/Self/Chat.rb +55 -0
  205. data/Views/Self/Concours.rb +109 -0
  206. data/Views/Self/Email.rb +34 -0
  207. data/Views/Self/Internet.rb +255 -0
  208. data/Views/Self/Results.rb +17 -0
  209. data/Views/Self/Services.rb +85 -0
  210. data/Views/Self/Show.rb +47 -0
  211. data/Views/Self/Tabs.rb +5 -0
  212. data/Views/Special/DFileEdit.rb +13 -0
  213. data/Views/Special/PlugEdit.rb +56 -0
  214. data/Views/Special/Tabs.rb +6 -0
  215. data/Views/Special/Vnc.rb +39 -0
  216. data/Views/Task/Client.rb +21 -0
  217. data/Views/Task/Edit.rb +33 -0
  218. data/Views/Task/List.rb +55 -0
  219. data/Views/Task/Tabs.rb +9 -0
  220. data/Views/Task/Worker.rb +30 -0
  221. data/Views/Template/Activity.rb +33 -0
  222. data/Views/Template/CourseType.rb +63 -0
  223. data/Views/Template/ScheduleType.rb +29 -0
  224. data/Views/Template/Tabs.rb +5 -0
  225. data/Views/Welcome.rb +121 -0
  226. data/config.yaml.default +36 -0
  227. data/po/Gestion-ar.po +2356 -0
  228. data/po/Gestion-en.mo +0 -0
  229. data/po/Gestion-en.po +4363 -0
  230. data/po/Gestion-fr.mo +0 -0
  231. data/po/Gestion-fr.po +4345 -0
  232. data/po/traduction-ar.rtf +76 -0
  233. metadata +381 -0
@@ -0,0 +1,226 @@
1
+ # FilesManage holds different classes that store information about the
2
+ # managed files.
3
+
4
+ class FMDirs < Entities
5
+ attr_accessor :dir_base
6
+
7
+ def create(fields)
8
+ if search_by_path(fields._name, fields._parent)
9
+ return
10
+ end
11
+ super(fields)
12
+ end
13
+
14
+ def setup_data
15
+ @dir_base = '/opt/Files'
16
+ value_str :name
17
+ # If the parent is empty, this is a top-dir
18
+ value_str :parent
19
+ end
20
+
21
+ def base_dirs
22
+ # puts @data
23
+ return search_by_parent('^$')
24
+ end
25
+
26
+ def sub_dirs(base)
27
+ name = base
28
+ if name.class != String
29
+ name = base._name
30
+ end
31
+ return search_by_parent(name)
32
+ end
33
+
34
+ def search_by_path(n, p = nil)
35
+ if p
36
+ filter_by(name: "^#{n}$", parent: "^#{p}$").first
37
+ else
38
+ find_by_name("^#{n}$")
39
+ end
40
+ end
41
+
42
+ def self.accents_replace(str)
43
+ str = str.downcase.gsub(/ /, '_')
44
+ accents = Hash[*%w( a àáâä e éèêë i ìíîï o òóôöœ u ùúûü c ç ss ß )]
45
+ dputs(4) { "String was #{str}" }
46
+ accents.each { |k, v|
47
+ str.gsub!(/[#{v}]/, k)
48
+ }
49
+ str.gsub!(/[^a-z0-9_\.-]/, '_')
50
+ dputs(4) { "String is #{str}" }
51
+ str
52
+ end
53
+ end
54
+
55
+ class FMDir < Entity
56
+ def path(*f)
57
+ p = [name]
58
+ parent and p.unshift(parent)
59
+ File.join(FMDirs.dir_base, *p, *f)
60
+ end
61
+
62
+ def sub_dirs
63
+ return FMDirs.search_by_parent(name)
64
+ end
65
+
66
+ # Returns all entries in that directory
67
+ def entries
68
+ return [] unless parent != ''
69
+ FMEntries.search_all_.select { |e|
70
+ dir = e._directory
71
+ dir._parent == parent && dir._name == name
72
+ }
73
+ end
74
+
75
+ # Searches for files that are not an entry yet, adds them
76
+ # and returns the array of new entries.
77
+ def update_files
78
+ # dputs_func
79
+ ffiles = []
80
+ fentries = entries
81
+ Dir.glob(File.join(path, '*')).each { |f|
82
+ next if File.directory? f
83
+ f = File.basename(f)
84
+ if f !~ /\.file$/
85
+ dputs(3) { "Found file #{f}" }
86
+ if fentries.select { |e|
87
+ dputs(3) { "Checking with #{e._name} / #{e.file_name}" }
88
+ e.file_name == f
89
+ }.size == 0
90
+ f_sanitized = FMDirs.accents_replace(f)
91
+ if f_sanitized != f
92
+ File.rename(path(f), path(f_sanitized))
93
+ end
94
+ dputs(3) { "Creating entry for #{f}/#{f_sanitized} with dir #{self.inspect}" }
95
+ desc = ''
96
+ if f.size > 16
97
+ desc = f
98
+ desc.sub(/\..*?$/, '')
99
+ end
100
+ ffiles.push FMEntries.create(name: f_sanitized, url_file: "localhost://#{f_sanitized}",
101
+ directory: self, tags: [], description: desc)
102
+ end
103
+ end
104
+ }
105
+ ffiles
106
+ end
107
+
108
+ # If it's an OS-directory, it adds all its subdirectories that
109
+ # are not yet stored.
110
+ def update_dirs
111
+ return if parent && parent != ''
112
+ fdirs = []
113
+ Dir.glob(File.join(path, '*/')).each{|d|
114
+ d = File.basename(d)
115
+ if FMDirs.search_by_path(d, name)
116
+ next
117
+ end
118
+ fdirs.push FMDirs.create(name: d, parent: name)
119
+ }
120
+ fdirs
121
+ end
122
+ end
123
+
124
+ class FMEntries < Entities
125
+ self.needs %w(FMDirs)
126
+
127
+ def setup_data
128
+ value_str :name
129
+ value_str :url_file
130
+ value_str :url_page
131
+ value_str :description
132
+ value_entity_FMDir :directory
133
+ value_str :tags
134
+ value_bool :changed
135
+ end
136
+
137
+ def load(has_static = true)
138
+ file_id = 1
139
+ FMDirs.base_dirs.each { |base|
140
+ FMDirs.sub_dirs(base._name).each { |sub|
141
+ Dir.glob(File.join(sub.path, '*.file')).each { |f|
142
+ lines = IO.readlines(f).collect { |l| l.chomp }
143
+ tags = lines[7] || ''
144
+ @data[file_id] = {
145
+ fmentry_id: file_id,
146
+ name: File.basename(f),
147
+ url_file: lines[0],
148
+ url_page: lines[1],
149
+ description: lines[3],
150
+ directory: sub,
151
+ tags: tags.split(',').map{|t| t.sub(' ', '')},
152
+ changed: false,
153
+ }
154
+ file_id += 1
155
+ }
156
+ }
157
+ }
158
+ end
159
+
160
+ def save
161
+ FMEntries.search_all_.each { |e|
162
+ e.save_file
163
+ }
164
+ end
165
+
166
+ def create(*args)
167
+ e = super(*args)
168
+ e._changed = true
169
+ e._name = e.file_name + '.file'
170
+ e.save_file
171
+ e
172
+ end
173
+
174
+ def search_by_directory(dir)
175
+ FMEntries.search_all_.select{|e|
176
+ d = e._directory
177
+ d._name == dir._name && d._parent == dir._parent
178
+ }
179
+ end
180
+ end
181
+
182
+
183
+ class FMEntry < Entity
184
+ def save_file(force = false)
185
+ if changed == true || force
186
+ if !tags || tags == ''
187
+ self._tags = []
188
+ end
189
+ File.open(directory.path(name), 'w') { |f|
190
+ f.write("#{url_file}\n#{url_page}\n\n#{description}\n\n"+
191
+ "#{directory._parent}\n#{directory._name}\n" +
192
+ "#{tags.join(', ')}")
193
+ }
194
+ self.changed = false
195
+ end
196
+ end
197
+
198
+ def file_name
199
+ if !url_file
200
+ return name.chomp('.file')
201
+ end
202
+ if url_file =~ /^:(.*?):/
203
+ return $1
204
+ end
205
+ File.basename url_file
206
+ end
207
+
208
+ def full_path
209
+ return File.join(directory.path, name)
210
+ end
211
+
212
+ def delete
213
+ FileUtils.rm_f File.join(directory.path, file_name)
214
+ FileUtils.rm_f File.join(directory.path, name)
215
+ super
216
+ end
217
+
218
+ def rename(new_name)
219
+ nname = FMDirs.accents_replace(new_name)
220
+ File.rename(directory.path(file_name), directory.path(nname))
221
+ File.rename(full_path, directory.path(nname+'.file'))
222
+ self.name = nname + '.file'
223
+ self.url_file = "http://localhost/#{nname}"
224
+ save_file(true)
225
+ end
226
+ end
data/Entities/Grade.rb ADDED
@@ -0,0 +1,186 @@
1
+ class Grades < Entities
2
+
3
+ def setup_data
4
+ value_entity_course :course
5
+ value_entity_person :student
6
+ value_int :random
7
+
8
+ value_block :info
9
+ value_list_int :means
10
+ value_int :mean
11
+ value_str :remark
12
+ end
13
+
14
+ def match_by_course_person(course, student)
15
+ # dputs_func
16
+ if course.class != Course
17
+ course = Courses.match_by_course_id(course)
18
+ end
19
+ if student.class != Person
20
+ student = Persons.match_by_login_name(student)
21
+ end
22
+ if course && student
23
+ dputs(4) { "Found #{course.name} - #{student.login_name}" }
24
+ grades = Grades.matches_by_course(course.course_id)
25
+ grades.each { |g|
26
+ dputs(4) { "Checking grade #{g.student}-#{g}-#{student}" }
27
+ if g.student == student
28
+ dputs(4) { "Found grade #{g} for #{student.login_name} in #{course.name}" }
29
+ return g
30
+ end
31
+ }
32
+ end
33
+ return nil
34
+ end
35
+
36
+ def save_data(d)
37
+ #dputs_func
38
+ if not d.has_key? :grade_id
39
+ id = matches_by_course(d[:course].course_id).select { |g|
40
+ g.student == d[:student]
41
+ }
42
+ if id.length > 0
43
+ dputs(2) { "Saving grade with existing id of #{id}" }
44
+ d[:grade_id] = id[0].grade_id
45
+ end
46
+ end
47
+ dputs(4) { "data is #{d.inspect}" }
48
+ if d._means.count > 0
49
+ d[:mean] = d[:means].reduce(:+) / d[:means].count
50
+ dputs(4) { "data is #{d.inspect}" }
51
+ else
52
+ d._mean = 0
53
+ end
54
+ dputs(4) { "data before storing is #{d.inspect}" }
55
+ super(d)
56
+ end
57
+
58
+ def self.grade_to_mean(g)
59
+ case g
60
+ when /P/ then
61
+ 10
62
+ when /AB/ then
63
+ 13
64
+ when /B/ then
65
+ 15
66
+ when /TB/ then
67
+ 17
68
+ when /E/ then
69
+ 19
70
+ else
71
+ 9
72
+ end
73
+ end
74
+
75
+ def migration_1_raw(g)
76
+ course = Courses.match_by_course_id(g._course_id)
77
+ if course and course.ctype
78
+ g._means = [g._mean || 0] * course.ctype.tests_nbr.to_i
79
+ dputs(4) { "means is #{g._means.inspect} - tests are #{course.ctype.tests_nbr.inspect}" }
80
+ else
81
+ dputs(0) { "Error: Migrating without ctype for #{g.inspect}..." }
82
+ exit
83
+ end
84
+ end
85
+
86
+ def migration_2_raw(g)
87
+ g._course = g._course_id
88
+ g._student = g._person_id
89
+ end
90
+
91
+ def self.create(data)
92
+ grade = super(data)
93
+ data._means and grade.means = data._means
94
+ grade
95
+ end
96
+ end
97
+
98
+ class Grade < Entity
99
+ def setup_instance
100
+ if ConfigBase.has_function? :course_server
101
+ init_random
102
+ end
103
+ end
104
+
105
+ def to_s
106
+ begin
107
+ value = (data_get(:mean).to_f * 2).round / 2
108
+ rescue FloatDomainError => _e
109
+ return 'NP'
110
+ end
111
+
112
+ case value
113
+ when 10..11.5 then
114
+ 'P'
115
+ when 12..13 then
116
+ 'AB'
117
+ when 14..15.5 then
118
+ 'B'
119
+ when 16..17.5 then
120
+ 'TB'
121
+ when 18..20 then
122
+ 'E'
123
+ else
124
+ 'NP'
125
+ end
126
+ end
127
+
128
+ def mention
129
+ case to_s
130
+ when 'P' then
131
+ 'Passable'
132
+ when 'AB' then
133
+ 'Assez bien'
134
+ when 'B' then
135
+ 'Bien'
136
+ when 'TB' then
137
+ 'Très bien'
138
+ when 'E' then
139
+ 'Excellent'
140
+ else
141
+ 'PAS PASSÉ'
142
+ end
143
+ end
144
+
145
+ def init_random
146
+ while not self.random
147
+ r = rand(1_000_000_000).to_s.rjust(9, '0')
148
+ Grades.match_by_random(r) or self.random = r
149
+ end
150
+ end
151
+
152
+ def get_url_label
153
+ # dputs_func
154
+ init_random
155
+ dputs(4) { "Course is #{course.inspect}" }
156
+ center_id = course.center ? course.center.login_name : 'pit'
157
+ dputs(4) { "Course is #{course.inspect}" }
158
+ "#{ConfigBase.get_url(:label_url)}/#{center_id}/#{random}"
159
+ end
160
+
161
+ def person
162
+ dputs(0) { "Error: Deprecated - use student in #{caller.inspect}" }
163
+ student
164
+ end
165
+
166
+ def means=(m)
167
+ if m != self._means
168
+ if ConfigBase.has_function? :course_client
169
+ self.random = nil
170
+ end
171
+ end
172
+ if m
173
+ self._means = m.collect { |v| [20.0, [0.0, v.to_f].max].min }
174
+ self._mean = means.reduce(:+).to_f / m.count.to_f
175
+ end
176
+ end
177
+
178
+ def remark=(r)
179
+ if r != self._remark
180
+ if ConfigBase.has_function? :course_client
181
+ self.random = nil
182
+ end
183
+ end
184
+ self._remark = r
185
+ end
186
+ end
@@ -0,0 +1,300 @@
1
+ =begin rdoc
2
+ === Internet
3
+
4
+ This module links the following external parts:
5
+ * Network::Device - to handle internet-captive devices
6
+ * Monitor::Traffic - to watch individual traffic from users
7
+
8
+ To use it, call the #setup method, which will list all devices and start
9
+ listening for new ones.
10
+ =end
11
+
12
+ # Holds the different type of users with regard to internet.
13
+ # TODO: add limit_class and limit_course to +type+
14
+ class InternetClasses < Entities
15
+ def setup_data
16
+ value_str :name
17
+ value_int :limit
18
+ value_list_drop :type, '%w(unlimited limit_daily_mo limit_daily_min)'
19
+ end
20
+
21
+ def migration_1_raw(i)
22
+ i._type == ['limit_daily'] and i._type = ['limit_daily_mo']
23
+ i._limit = i._limit_mo
24
+ end
25
+ end
26
+
27
+ # Represents one usage-type
28
+ class InternetClass < Entity
29
+ # Checks whether a given user is in limits of InternetClasses and thus
30
+ # allowed to use the internet
31
+ def in_limits?(login, today = Date.today)
32
+ return true if type == ['unlimited']
33
+ return true unless t = Network::Captive.traffic
34
+ t.get_day(login, 1, today.to_time).flatten[0..1].inject(:+) < limit.to_i * 1_000_000
35
+ end
36
+ end
37
+
38
+ class InternetPersons < Entities
39
+ def setup_data
40
+ value_entity_person :person
41
+ value_entity_internetClasses :iclass
42
+ value_date :start
43
+ value_int :duration
44
+ end
45
+ end
46
+
47
+ class InternetPerson < Entity
48
+ # checks whether that person has an active reference to #InternetClasses
49
+ def is_active?(today = Date.today)
50
+ duration.to_i == 0 ||
51
+ (start.to_s != '' && (Date.from_web(start) + duration.to_i > today))
52
+ end
53
+
54
+ # checks whether a person is active and in limits
55
+ def in_limits?(today = Date.today)
56
+ is_active?(today) && iclass.in_limits?(person.login_name, today)
57
+ end
58
+ end
59
+
60
+ module Internet
61
+ attr_accessor :device
62
+ extend self
63
+ include Network
64
+
65
+ @operator_local = nil
66
+
67
+ # Gets all devices and adds an observer for new devices. Also sets up
68
+ # traffic-tables for users, loading if some already exist
69
+ def setup
70
+ #dputs_func
71
+ @traffic_save = Statics.get(:GestionTraffic)
72
+ dputs(4) { "@traffic is #{@traffic_save.data_str}" }
73
+ if ConfigBase.has_function?(:internet_captive)
74
+ @device = nil
75
+
76
+ Device.add_observer(self)
77
+ dev, op = if (dev_id = ConfigBase.captive_dev.to_s).length > 0
78
+ dev_id.sub!(/:.*$/, '')
79
+ dputs(2) { "Searching for #{dev_id} in #{Network::Device.list}" }
80
+ [Network::Device.search_dev({uevent: {interface: dev_id}}).first,
81
+ 'add_captive']
82
+ else
83
+ [Network::Device.search_dev({uevent: {driver: 'option'}}).first, 'add']
84
+ end
85
+ dev and update(op, dev)
86
+ end
87
+ end
88
+
89
+ # Whenever a new device or a new operator is detected, this function
90
+ # updates the internal variables.
91
+ def update(operation, dev = nil)
92
+ dputs(3) { "Updating operation #{operation} with dev #{dev.inspect}" }
93
+ case operation
94
+ when /del/
95
+ if @device == dev
96
+ log_msg :Internet, "Lost device #{@device}"
97
+ @device.delete_observer(self)
98
+ @device = nil
99
+ Captive.accept_all
100
+ end
101
+ when /add/
102
+ if dev && dev.dev._uevent && dev.dev._uevent._driver == 'option' ||
103
+ operation == 'add_captive'
104
+ @device = dev
105
+ @device.add_observer(self)
106
+ @operator_local = @device.operator
107
+ @operator_local and Captive.setup(@device, @traffic_save.data_str)
108
+ log_msg :Internet, "Got new device #{@device}"
109
+ else
110
+ log_msg :Internet, "New device #{dev} that doesn't match option"
111
+ end
112
+ when /operator/
113
+ @operator_local = @device.operator
114
+ Captive.setup(@device, @traffic_save.data_str)
115
+ log_msg :Internet, "Got new operator #{@operator_local}"
116
+ end
117
+ end
118
+
119
+ # Scans all connected users and deduces money from all connected, non-free
120
+ # users. If there is not enough money left, it kicks the user.
121
+ def take_money
122
+ #dputs_func
123
+ return unless @operator_local
124
+
125
+ Captive.cleanup
126
+ Captive.users_connected.each { |u|
127
+ HelperClasses::System.rescue_all do
128
+ dputs(3) { "User is #{u}" }
129
+ cost = @operator_local.user_cost_now.to_i
130
+
131
+ dputs(3) { "ISP is #{@operator_local.name} and conn_type is "+
132
+ "#{@operator_local.connection_type}" }
133
+ user = Persons.match_by_login_name(u)
134
+ if user
135
+ dputs(3) { "Found user #{u}: #{user.full_name}" }
136
+ if not (ag = AccessGroups.allow_user_now(u))[0]
137
+ log_msg 'take_money', "Kicking user #{u} because of accessgroups: #{ag[1]}"
138
+ user_disconnect user.login_name
139
+ elsif self.free(user)
140
+ dputs(2) { "User #{u} goes free" }
141
+ Captive.user_keep(user.login_name, ConfigBase.keep_idle_free.to_i)
142
+ elsif @device.connection_status == Device::CONNECTED
143
+ dputs(3) { "User #{u} will pay #{cost}" }
144
+ if user.internet_credit.to_i >= cost
145
+ dputs(3) { "Taking #{cost} internet_credits from #{u} who has #{user.internet_credit}" }
146
+ user.internet_credit = user.internet_credit.to_i - cost
147
+ else
148
+ log_msg 'take_money', "User #{u} has not enough money left - kicking"
149
+ user_disconnect user.login_name
150
+ end
151
+ end
152
+ else
153
+ dputs(0) { "Error: Captive said #{u} is connected, but couldn't find that user!" +
154
+ " Users connected: #{Captive.users_connected.inspect}" }
155
+ end
156
+ end
157
+ }
158
+ end
159
+
160
+ def connection_status
161
+ return [ 0, "No device" ] unless @device
162
+ if ConfigBase.connection_status_log && ConfigBase.connection_status_log.length > 0
163
+ reply = System.run_str("cat #{ConfigBase.connection_status_log}")
164
+ if reply.length > 0
165
+ return reply.split(" ")
166
+ else
167
+ return [ 2, "Error: #{reply}" ]
168
+ end
169
+ else
170
+ if @device.connection_status == Device::CONNECTED
171
+ return [ 4, "Up" ]
172
+ else
173
+ return [ 2, "Connecting" ]
174
+ end
175
+ end
176
+ end
177
+
178
+ # Scan for all active courses (date_start <= date_now <= date_end) for a
179
+ # specific +user+, which should be of type #Persons
180
+ def active_course_for(user)
181
+ # We want an exact match, so we put the name between ^ and $
182
+ courses = Courses.search_by_students("^#{user.login_name}$")
183
+ if courses
184
+ dputs(3) { "Courses : #{courses.inspect}" }
185
+ courses.each { |c|
186
+ dputs(3) { "Searching course #{c}" }
187
+ if c.name and c.start and c.end
188
+ dputs(3) { "Searching course for #{user.full_name}" }
189
+ dputs(3) { [c.name, c.start, c.end].inspect }
190
+ begin
191
+ c_start = Date.strptime(c.start, '%d.%m.%Y')
192
+ c_end = Date.strptime(c.end, '%d.%m.%Y')
193
+ rescue
194
+ c_start = c_end = Date.new
195
+ end
196
+ if c_start <= Date.today and Date.today <= c_end
197
+ return true
198
+ end
199
+ end
200
+ }
201
+ end
202
+ return false
203
+ end
204
+
205
+ # Decides whether a person is allowed to surf for free, depending on:
206
+ # - ConfigBase.allow_free ('all' - 'false' - 'true')
207
+ # - permissions (FlagInternetFree and internet_free_staff)
208
+ # - freesurf-group
209
+ # - courses (date between :start and :end and internet_free_course)
210
+ # - InternetPersons, where the different allowed traffics might be stored
211
+ def free(user)
212
+ # dputs_func
213
+ case ConfigBase.allow_free[0]
214
+ when /all/
215
+ dputs(3) { "User #{user} is free because ALL are free" }
216
+ return true
217
+ when /false/
218
+ dputs(3) { "User #{user} is NOT free because NONE are free" }
219
+ return false
220
+ end
221
+ if user.class != Person
222
+ user = Persons.match_by_login_name(user)
223
+ dputs(4) { "Found user #{user.login_name}" }
224
+ end
225
+ login = user.login_name
226
+ if user
227
+ dputs(3) { "Searching groups for user #{login}: #{user.groups.inspect}" }
228
+ if user.groups && user.groups.index('freesurf')
229
+ dputs(3) { "User #{login} is on freesurf" }
230
+ return true
231
+ end
232
+
233
+ if ConfigBase.has_function?(:internet_free_staff) &&
234
+ Permission.can_view(user.permissions, 'FlagInternetFree')
235
+ dputs(3) { "User #{login} has FlagInternetFree" }
236
+ return true
237
+ end
238
+
239
+ if ConfigBase.has_function?(:internet_free_course) &&
240
+ self.active_course_for(user)
241
+ dputs(3) { "User #{login} is free for a course" }
242
+ return true
243
+ end
244
+
245
+ if (ip = InternetPersons.match_by_person(user)) && ip.iclass
246
+ dputs(3) { "User #{login} has internetpersons #{ip.in_limits?}" }
247
+ return ip.in_limits?
248
+ end
249
+
250
+ Activities.search_by_tags('internet').each { |act|
251
+ dputs(3) { "Searching activity #{act}" }
252
+ if act.start_end(user) != [nil, nil]
253
+ if (il = act.internet_limit) == nil
254
+ dputs(3) { "User #{user.login_name} goes free" }
255
+ return true
256
+ else
257
+ dputs(3) { "found limit #{il} for user #{user.login_name}" }
258
+ return il.in_limits?(user.login_name)
259
+ end
260
+ end
261
+ }
262
+
263
+ if ic = ConfigBase.iclass_default
264
+ dputs(3) { "User #{login} falls into default : #{ic.in_limits?(login)}" }
265
+ return ic.in_limits?(login)
266
+ end
267
+ end
268
+ dputs(3) { 'Found nothing' }
269
+ return false
270
+ end
271
+
272
+ # Fetches new traffic and saves the actual traffic in Statics
273
+ def update_traffic
274
+ return unless Captive.traffic
275
+ Captive.traffic.update
276
+ @traffic_save.data_str = Captive.traffic.to_json
277
+ end
278
+
279
+ def operator
280
+ # Wow - this is really ugly. Why do two different listeners (Internet and MobileControl)
281
+ # don't agree on who is the operator?
282
+ return @operator_local unless $MobileControl
283
+ $MobileControl.operator
284
+ end
285
+
286
+ # Lets a user connect and adds its IP to the traffic-table
287
+ def user_connect(name, ip)
288
+ return unless @operator_local
289
+
290
+ # Free users have different auto-disconnect time than non-free users
291
+ Captive.user_connect name, ip, (self.free(name) ? 'yes' : 'no')
292
+ end
293
+
294
+ # Disconnects the user and removes its IP from the traffic-table
295
+ def user_disconnect(name)
296
+ return unless @operator_local
297
+
298
+ Captive.user_disconnect_name name
299
+ end
300
+ end