gestion 1.9.12

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.
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