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.
- checksums.yaml +7 -0
- data/.gitignore +48 -0
- data/.project +14 -0
- data/Binaries/adduser_to_buzz +15 -0
- data/Binaries/backup +7 -0
- data/Binaries/check_gestion +8 -0
- data/Binaries/gestion.gnumail +22 -0
- data/Binaries/gestion.logrotate +34 -0
- data/Binaries/gestion.service +12 -0
- data/Binaries/gestion_update.rb +183 -0
- data/Binaries/gestion_update.service +10 -0
- data/Binaries/get_compta +11 -0
- data/Binaries/kill_gestion +16 -0
- data/Binaries/ldap/add_indexes +51 -0
- data/Binaries/ldap/backup +2 -0
- data/Binaries/ldap/install_ldap +92 -0
- data/Binaries/ldap/restore +7 -0
- data/Binaries/lib_backup +5 -0
- data/Binaries/log_scan_errors +8 -0
- data/Binaries/loop_gestion +64 -0
- data/Binaries/onetimers/sync_courses_from_compta.rb +74 -0
- data/Binaries/onetimers/transfer_cash_from_ldap_to_csv +26 -0
- data/Binaries/reboot +5 -0
- data/Binaries/restore +3 -0
- data/Binaries/restore_do +22 -0
- data/Binaries/sort_events +31 -0
- data/Binaries/start_gestion +18 -0
- data/Binaries/swipe_gestion +18 -0
- data/Binaries/update_africompta +21 -0
- data/Binaries/update_users +3 -0
- data/Diplomas.src/accredited.odg +0 -0
- data/Diplomas.src/diploma.odg +0 -0
- data/Diplomas.src/label.odg +0 -0
- data/Diplomas.src/presence_sheet.ods +0 -0
- data/Diplomas.src/presence_sheet_small.ods +0 -0
- data/Diplomas.src/student_card.odg +0 -0
- data/Doc/130514-it-ideas.odt +0 -0
- data/Doc/Compta-cash.mm +179 -0
- data/Doc/General.odt +0 -0
- data/Entities/AccessGroups.rb +117 -0
- data/Entities/Activity.rb +178 -0
- data/Entities/ChatMsg.rb +142 -0
- data/Entities/Classroom.rb +11 -0
- data/Entities/Client.rb +19 -0
- data/Entities/Computer.rb +21 -0
- data/Entities/ConfigBase.rb +280 -0
- data/Entities/Course.rb +1588 -0
- data/Entities/CourseType.rb +171 -0
- data/Entities/DFiles.rb +466 -0
- data/Entities/FilesManage.rb +226 -0
- data/Entities/Grade.rb +186 -0
- data/Entities/Internet.rb +300 -0
- data/Entities/Netdev.rb +10 -0
- data/Entities/Person.rb +1175 -0
- data/Entities/Plug.rb +98 -0
- data/Entities/Quiz.rb +33 -0
- data/Entities/Recharges.rb +37 -0
- data/Entities/Report.rb +136 -0
- data/Entities/Room.rb +12 -0
- data/Entities/SMS.rb +30 -0
- data/Entities/ScheduleType.rb +33 -0
- data/Entities/Share.rb +120 -0
- data/Entities/Task.rb +51 -0
- data/Entities/Ticket.rb +72 -0
- data/Entities/Usage.rb +143 -0
- data/Entities/Worker.rb +29 -0
- data/Files/apache-profeda.conf +36 -0
- data/Files/label.erb +121 -0
- data/Files/label_notfound.erb +64 -0
- data/Files/label_notpassed.erb +84 -0
- data/Files/mobileinfo.erb +115 -0
- data/Files/smb.conf +333 -0
- data/Files/timetable.html +36 -0
- data/Files/timetable.js +239 -0
- data/Gemfile +12 -0
- data/Gemfile.dev +12 -0
- data/Gemfile.dev.lock +127 -0
- data/Gemfile.lock +127 -0
- data/Gemfile.prod +8 -0
- data/Gestion +35 -0
- data/Gestion.rb +220 -0
- data/INSTALL +40 -0
- data/Images/connection.xcf +0 -0
- data/Images/connection_no.png +0 -0
- data/Images/connection_wait.png +0 -0
- data/Images/connection_yes.png +0 -0
- data/Paths/Exas.rb +13 -0
- data/Paths/Files.rb +19 -0
- data/Paths/GetDiplomas.rb +20 -0
- data/Paths/Info.rb +114 -0
- data/Paths/Label.rb +187 -0
- data/Paths/MobileInfo.rb +19 -0
- data/Paths/internetCash.rb +34 -0
- data/Paths/internetWifi.rb +54 -0
- data/README.md +60 -0
- data/Rakefile +13 -0
- data/TODO +1391 -0
- data/Test/.gitignore +3 -0
- data/Test/Diplomas/base_gestion.odt +0 -0
- data/Test/Diplomas/base_report.odt +0 -0
- data/Test/Diplomas/carte_etudiant.odg +0 -0
- data/Test/Diplomas/exam_language.odt +0 -0
- data/Test/Diplomas/label.odg +0 -0
- data/Test/Diplomas/presence_sheet.ods +0 -0
- data/Test/Diplomas/presence_sheet_small.ods +0 -0
- data/Test/Diplomas/student_card.odg +0 -0
- data/Test/Manual/testMerge +18 -0
- data/Test/config_test.yaml +26 -0
- data/Test/db.testGestion +0 -0
- data/Test/dfiles/descs/avg-rescue.desc +10 -0
- data/Test/dfiles/descs/avg.desc +8 -0
- data/Test/dfiles/descs/driver.desc +8 -0
- data/Test/dfiles/descs/linuxmint.desc +7 -0
- data/Test/dfiles/files/avg-160203.exe +1 -0
- data/Test/dfiles/files/avg.iso +1 -0
- data/Test/dfiles/files/driver.exe +1 -0
- data/Test/dfiles/index_post.html +3 -0
- data/Test/dfiles/index_pre.html +8 -0
- data/Test/dfiles/priorities +5 -0
- data/Test/ge_activity.rb +124 -0
- data/Test/ge_chat.rb +106 -0
- data/Test/ge_compta.rb +67 -0
- data/Test/ge_configbase.rb +54 -0
- data/Test/ge_course.rb +1114 -0
- data/Test/ge_dfiles.rb +121 -0
- data/Test/ge_filesmanage.rb +180 -0
- data/Test/ge_info.rb +27 -0
- data/Test/ge_internet.rb +246 -0
- data/Test/ge_login.rb +55 -0
- data/Test/ge_person.rb +373 -0
- data/Test/ge_qvinfo.rb +28 -0
- data/Test/ge_report.rb +97 -0
- data/Test/ge_share.rb +27 -0
- data/Test/ge_sms.rb +34 -0
- data/Test/ge_tasks.rb +19 -0
- data/Test/ge_usage.rb +168 -0
- data/Test/ge_view.rb +46 -0
- data/Test/multiconf-captive +29 -0
- data/Test/test.conf +7 -0
- data/Test/test.rb +49 -0
- data/Test/test_bytes.png +0 -0
- data/VERSION +140 -0
- data/Views/Admin/Backup.rb +91 -0
- data/Views/Admin/Configuration.rb +44 -0
- data/Views/Admin/Credit.rb +32 -0
- data/Views/Admin/FilesManage.rb +219 -0
- data/Views/Admin/Function.rb +39 -0
- data/Views/Admin/Power.rb +49 -0
- data/Views/Admin/Printer.rb +37 -0
- data/Views/Admin/Server.rb +252 -0
- data/Views/Admin/Tabs.rb +5 -0
- data/Views/Admin/Update.rb +73 -0
- data/Views/Admin/UpdateSystem.rb +26 -0
- data/Views/Cashbox/Activity.rb +191 -0
- data/Views/Cashbox/Course.rb +141 -0
- data/Views/Cashbox/Credit.rb +79 -0
- data/Views/Cashbox/Report.rb +115 -0
- data/Views/Cashbox/Service.rb +105 -0
- data/Views/Cashbox/Tabs.rb +10 -0
- data/Views/Compta/Accounts.rb +36 -0
- data/Views/Compta/Course.rb +96 -0
- data/Views/Compta/Show.rb +6 -0
- data/Views/Compta/Transfer.rb +66 -0
- data/Views/Course/Diploma.rb +203 -0
- data/Views/Course/Grade.rb +401 -0
- data/Views/Course/Modify.rb +447 -0
- data/Views/Course/Print.rb +94 -0
- data/Views/Course/Responsible.rb +44 -0
- data/Views/Course/Stats.rb +76 -0
- data/Views/Course/Students.rb +92 -0
- data/Views/Course/Tabs.rb +220 -0
- data/Views/Internet/Access.rb +134 -0
- data/Views/Internet/ClassEdit.rb +24 -0
- data/Views/Internet/ClassUsers.rb +81 -0
- data/Views/Internet/Config.rb +32 -0
- data/Views/Internet/Mobile.rb +213 -0
- data/Views/Internet/Recharges.rb +49 -0
- data/Views/Internet/Tabs.rb +6 -0
- data/Views/Inventory/Computer.rb +24 -0
- data/Views/Inventory/Room.rb +18 -0
- data/Views/Inventory/Tabs.rb +9 -0
- data/Views/Inventory/TicketClosed.rb +7 -0
- data/Views/Inventory/TicketOpen.rb +23 -0
- data/Views/Library/Person.rb +36 -0
- data/Views/Library/Tabs.rb +7 -0
- data/Views/Network/Block.rb +87 -0
- data/Views/Network/Netdevs.rb +21 -0
- data/Views/Network/Restriction.rb +37 -0
- data/Views/Network/Share.rb +167 -0
- data/Views/Network/Tables.rb +28 -0
- data/Views/Network/Tabs.rb +6 -0
- data/Views/Person/Admin.rb +99 -0
- data/Views/Person/Center.rb +48 -0
- data/Views/Person/Course.rb +72 -0
- data/Views/Person/Modify.rb +153 -0
- data/Views/Person/Tabs.rb +162 -0
- data/Views/Report/ComptaExecutive.rb +221 -0
- data/Views/Report/ComptaFlat.rb +79 -0
- data/Views/Report/ReportCourse.rb +47 -0
- data/Views/Report/Tabs.rb +8 -0
- data/Views/Report/Usage.rb +52 -0
- data/Views/Report/UsageCases.rb +59 -0
- data/Views/Self/Cash.rb +67 -0
- data/Views/Self/Chat.rb +55 -0
- data/Views/Self/Concours.rb +109 -0
- data/Views/Self/Email.rb +34 -0
- data/Views/Self/Internet.rb +255 -0
- data/Views/Self/Results.rb +17 -0
- data/Views/Self/Services.rb +85 -0
- data/Views/Self/Show.rb +47 -0
- data/Views/Self/Tabs.rb +5 -0
- data/Views/Special/DFileEdit.rb +13 -0
- data/Views/Special/PlugEdit.rb +56 -0
- data/Views/Special/Tabs.rb +6 -0
- data/Views/Special/Vnc.rb +39 -0
- data/Views/Task/Client.rb +21 -0
- data/Views/Task/Edit.rb +33 -0
- data/Views/Task/List.rb +55 -0
- data/Views/Task/Tabs.rb +9 -0
- data/Views/Task/Worker.rb +30 -0
- data/Views/Template/Activity.rb +33 -0
- data/Views/Template/CourseType.rb +63 -0
- data/Views/Template/ScheduleType.rb +29 -0
- data/Views/Template/Tabs.rb +5 -0
- data/Views/Welcome.rb +121 -0
- data/config.yaml.default +36 -0
- data/po/Gestion-ar.po +2356 -0
- data/po/Gestion-en.mo +0 -0
- data/po/Gestion-en.po +4363 -0
- data/po/Gestion-fr.mo +0 -0
- data/po/Gestion-fr.po +4345 -0
- data/po/traduction-ar.rtf +76 -0
- 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
|