class Course

Attributes

make_pdfs_state[R]
sync_state[R]
thread[R]

Public Instance Methods

abort_pdfs() click to toggle source
# File Entities/Course.rb, line 1338
def abort_pdfs
  if @thread
    dputs(2) { "Killing thread #{@thread}" }
    @thread.kill
    @thread.join
    @thread = nil
    @make_pdfs_state = {'0' => 'done'}
    dputs(3) { 'Joined thread' }
  end
end
center() click to toggle source
# File Entities/Course.rb, line 1334
def center
  return _center || Persons.find_by_permissions(:center)
end
check_dir() click to toggle source
# File Entities/Course.rb, line 452
def check_dir
  [dir_diplomas, dir_exams, dir_exams_share].each { |d|
    (!File.exists? d) and FileUtils.mkdir_p(d)
  }
end
check_students_dir() click to toggle source
# File Entities/Course.rb, line 458
def check_students_dir
  check_dir
  students and students.each { |s|
    [dir_exams, dir_exams_share].each { |d|
      (!File.exists? "#{d}/#{s}") and FileUtils.mkdir("#{d}/#{s}")
    }
  }
end
create_account() click to toggle source
# File Entities/Course.rb, line 1472
def create_account
  if ctype.account_base
    self.entries = Accounts.create_path(
        "#{ctype.account_base.path}::#{name}")
  else
    dputs(1) { "Trying to create account for #{name} but " +
        " #{ctype.name} has no base-account" }
  end
end
ctype=(ct) click to toggle source
# File Entities/Course.rb, line 434
def ctype=(ct)
  self._ctype = ct
  if ctype.file_exam and ctype.file_exam.length > 0
    update_exam_file
  end
end
date_en(d, show_year = true) click to toggle source
# File Entities/Course.rb, line 526
def date_en(d, show_year = true)
  day, month, year = d.split('.')
  day = day.gsub(/^0/, '')
  day_str = case day.to_i
              when 1, 21, 31
                "#{day}st"
              when 2, 22
                "#{day}nd"
              when 3, 23
                "#{day}rd"
              when 4..20, 24..30
                "#{day}th"
            end
  month = %w( January February March April May June July August September October November December )[month.to_i-1]
  if show_year
    "#{day_str} of #{month}, #{year}"
  else
    "#{day_str} of #{month}"
  end
end
date_fr(d, show_year = true) click to toggle source
# File Entities/Course.rb, line 512
def date_fr(d, show_year = true)
  day, month, year = d.split('.')
  day = day.gsub(/^0/, '')
  if day == '1'
    day = '1er'
  end
  month = %w( janvier février mars avril mai juin juillet août septembre octobre novembre décembre )[month.to_i-1]
  if show_year
    [day, month, year].join(' ')
  else
    [day, month].join(' ')
  end
end
date_i18n(d, show_year = true) click to toggle source
# File Entities/Course.rb, line 547
def date_i18n(d, show_year = true)
  return unless ctype.diploma_lang
  case ctype.diploma_lang.first
    when /en/
      date_en(d, show_year)
    when /fr/
      date_fr(d, show_year)
    else
      dputs(0) { "Unknown date type #{ctype.diploma_lang.inspect}" }
  end
end
delete() click to toggle source
Calls superclass method
# File Entities/Course.rb, line 1349
def delete
  abort_pdfs

  [dir_diplomas, dir_exams, dir_exams_share].each { |d|
    FileUtils.remove_entry_secure(d, true)
  }
  super
end
dend() click to toggle source
# File Entities/Course.rb, line 615
def dend
  Date.strptime(data_get(:end), '%d.%m.%Y')
end
dir_diplomas() click to toggle source
# File Entities/Course.rb, line 467
def dir_diplomas
  @proxy.dir_diplomas + "/#{self.name}"
end
dir_exams() click to toggle source
# File Entities/Course.rb, line 471
def dir_exams
  @proxy.dir_exams + "/#{self.name}"
end
dir_exams_share() click to toggle source
# File Entities/Course.rb, line 475
def dir_exams_share
  @proxy.dir_exams_share + "/#{self.name}"
end
dstart() click to toggle source
# File Entities/Course.rb, line 611
def dstart
  Date.strptime(start, '%d.%m.%Y')
end
exam_files(student) click to toggle source
# File Entities/Course.rb, line 1111
def exam_files(student)
  student_name = student.class == Person ? student.login_name : student
  dputs(4) { "Student-name is #{student_name.inspect}" }
  dir_exams = @proxy.dir_exams + "/#{name}"
  dir_student = "#{dir_exams}/#{student_name}"
  File.exists?(dir_student) ?
      Dir.entries(dir_student).select { |f| !(f =~ /^\./) } : []
end
exas_fetch_files() click to toggle source
# File Entities/Course.rb, line 1138
def exas_fetch_files
  name.length == 0 and return
  dputs(3) { "Starting to fetch files for #{name}" }
  if File.exists? dir_exams_share
    dputs(3) { "#{dir_exams_share} exists" }
    File.exists? dir_exams or FileUtils.mkdir dir_exams
    students.each { |s|
      dputs(3) { "Checking on student #{s}" }
      dir_student = "#{dir_exams_share}/#{s}"
      if File.exists? dir_student
        dputs(3) { "Moving student-dir of #{s}" }
        FileUtils.move dir_student, "#{dir_exams}"
      end
    }
  end
  FileUtils.rm_rf dir_exams_share
end
exas_prepare_files() click to toggle source
# File Entities/Course.rb, line 1120
def exas_prepare_files
  name.length == 0 and return
  if File.exists? dir_exams_share
    FileUtils.rm_rf dir_exams_share
  end

  FileUtils.mkdir dir_exams_share
  students.each { |s|
    dir_s_exas = "#{dir_exams}/#{s}"
    if File.exists? dir_s_exas
      FileUtils.mv dir_s_exas, dir_exams_share
    else
      FileUtils.mkdir "#{dir_exams_share}/#{s}"
    end
  }
  FileUtils.rm_rf(dir_exams)
end
export_check() click to toggle source

Tests if we have everything necessary handy

# File Entities/Course.rb, line 500
def export_check
  missing_data = []
  %w( start end sign duration teacher responsible description contents ).each { |s|
    d = data_get s
    if not d
      dputs(1) { "Failed checking #{s}: #{d}" }
      missing_data.push s
    end
  }
  return missing_data.size == 0 ? nil : missing_data
end
export_diploma() click to toggle source
# File Entities/Course.rb, line 559
  def export_diploma
    return if export_check

    d_start, d_end, d_sign = data_get(%w( start end sign ))
    same_year = 0
    [d_start, d_end, d_sign].each { |d|
      year = d.gsub(/.*\./, '')
      if same_year == 0
        same_year = year
      elsif same_year != year
        same_year = false
      end
    }
    txt = <<-END
base_gestion
#{teacher.full_name}
    #{responsible.full_name}
    #{duration}
    #{description}
    #{contents}

    #{date_fr(d_start, same_year)}
    #{date_fr(d_end, same_year)}
    #{date_fr(d_sign)}
    END
    students.each { |s|
      grade = Grades.match_by_course_person(course_id, s)
      if grade
        txt += "#{grade} #{grade.student.full_name}\n" +
            "#{grade.remark}\n"
      end
    }
    dputs(2) { "Text is: #{txt.gsub(/\n/, '*')}" }
    txt
  end
get_diploma_filename(student, ext = 'odt', diplomadir = true) click to toggle source
# File Entities/Course.rb, line 848
def get_diploma_filename(student, ext = 'odt', diplomadir = true)
  digits = students.size.to_s.size
  counter = students.index(student) + 1
  str = diplomadir ? dir_diplomas : name
  "#{str}/#{counter.to_s.rjust(digits, '0')}-#{student}.#{ext}"
end
get_duration_adds() click to toggle source
# File Entities/Course.rb, line 619
def get_duration_adds
  return [0, 0] if not start or not data_get(:end)
  dputs(4) { "start is: #{start} - end is #{data_get(:end)} - dow is #{dow}" }
  days_per_week, adds = case dow.to_s
                          when /lu-me-ve/, /ma-je-sa/
                            [3, [0, 2, 4]]
                          when /lu-ve/, /ma-sa/
                            [5, [0, 1, 2, 3, 4]]
                        end
  weeks = ((dend - dstart) / 7).ceil
  day = 0
  dow_adds = (0...weeks).collect { |w| adds.collect { |a|
    day += 1
    [/#{1000 + day}/, a + w * 7]
  }
  }.flatten(1)
  dputs(4) { "dow_adds is now #{dow_adds.inspect}" }

  [days_per_week * weeks, dow_adds]
end
get_files() click to toggle source
# File Entities/Course.rb, line 595
def get_files
  if File::directory?(dir_diplomas)
    files = if ctype.output[0] == 'certificate'
              Dir::glob("#{dir_diplomas}/*pdf")
            else
              Dir::glob("#{dir_diplomas}/*png") +
                  Dir::glob("#{dir_diplomas}/*zip")
            end
    files.collect { |f|
      File::basename(f)
    }.sort
  else
    []
  end
end
get_grade_args(student, update = false) click to toggle source
# File Entities/Course.rb, line 715
def get_grade_args(student, update = false)
  grade = Grades.match_by_course_person(course_id, student)
  dputs(3) { "Course is #{name} - student is #{student} - ctype is #{ctype.inspect} and grade is " +
      "#{grade.inspect} - #{grade.to_s}" }

  state = if (ctype.diploma_type[0] == 'accredited') && grade &&
      (!grade.random)
            'not synched'
          elsif exam_files(student).count < ctype.files_nbr.to_i
            'incomplete'
          elsif !grade
            'no grade'
            # Reports are created even for those who failed
          elsif grade.to_s == 'NP' && ctype.diploma_type != %w(report)
            'not passed'
          elsif update
            if get_files.find { |f| f =~ /^[0-9]+-#{student.login_name}\./ }
              'done'
            else
              'not created'
            end
          else
            'queued'
          end
  mean = if grade and grade.mean
           grade.mean
         else
           '-'
         end
  dputs(4) { "State is #{state}" }
  ln = student.login_name
  @make_pdfs_state[ln] = [mean, state, get_diploma_filename(ln, 'pdf', false)]

  [grade, state]
end
get_unique() click to toggle source
# File Entities/Course.rb, line 1330
def get_unique
  name
end
list_students(by_id = false) click to toggle source
# File Entities/Course.rb, line 479
def list_students(by_id = false)
  dputs(3) { "Students for #{self.name} are: #{self.students.inspect}" }
  ret = []
  if self.students
    ret = self.students.collect { |s|
      if person = Persons.match_by_login_name(s)
        [(by_id ? person.person_id : s),
         "#{person.full_name} - #{person.login_name}:#{person.password_plain}"]
      end
    }.compact.sort { |a, b| a[1] <=> b[1] }
  end
  ret
end
make_pdfs(convert) click to toggle source
# File Entities/Course.rb, line 855
def make_pdfs(convert)
  # dputs_func
  if @thread
    dputs(2) { 'Thread is here, killing' }
    begin
      abort_pdfs
    rescue Exception => e
      dputs(0) { "Error while killing: #{e.message}" }
      dputs(0) { "#{e.inspect}" }
      dputs(0) { "#{e.to_s}" }
      puts e.backtrace
    end
  end
  @make_pdfs_state = {'0' => 'collecting'}

  dputs(2) { 'Starting new thread' }
  @thread = Thread.new {
    begin
      counter = 1
      dputs(2) { "Preparing students: #{students.inspect}" }
      if !@only_psnup
        students.sort.each { |s|
          student = Persons.match_by_login_name(s)
          if student
            dputs(4) { "Is #{s} == #{student.login_name}?" }
            student_file = get_diploma_filename(s)
            dputs(2) { "Doing #{counter}: #{s} - file: #{student_file}" }
            FileUtils.cp("#{Courses.dir_diplomas}/#{ctype.file_diploma.join}",
                         student_file)
            update_student_diploma(student_file, student)
          end
          counter += 1
        }
      end

      dputs(3) { "Convert is #{convert.inspect}" }
      if convert
        @make_pdfs_state['0'] = 'converting'
        old = Dir.glob(dir_diplomas + '/content.xml*')
        list = Dir.glob(dir_diplomas + '/*odt')
        format = ctype.output[0].to_sym

        dputs(4) { "old is #{old.inspect}" }
        dputs(4) { "list is #{list.inspect}" }

        FileUtils.rm(old)
        if list.size == 0
          dputs(2) { 'No files here, quitting' }
          @make_pdfs_state['0'] = 'done'
          @thread.kill
        end
        dputs(2) { "Creating -#{format.inspect}-#{list.inspect}-" }
        System.run_bool('date >> /tmp/cp')
        System.run_bool("ls -l #{dir_diplomas} >> /tmp/cp")
        outfiles = []
        dir = File::dirname(list.first)
        @only_psnup and list = []
        list.sort.each { |p|
          dputs(3) { "Started thread for file #{p} in directory #{dir}" }
          student_name = p.sub(/.*[0-9]+-/, '').sub(/\.odt/, '')
          dputs(3) { "Student name is #{student_name}" }
          @make_pdfs_state[student_name][1] = 'working'

          if format == :certificate
            Docsplit.extract_pdf p, :output => dir
          else
            Docsplit.extract_images p, :output => dir,
                                    :density => 300, :format => :png
            FileUtils.mv(p.sub(/.odt$/, '_1.png'), p.sub(/.odt$/, '.png'))
          end
          dputs(5) { 'Finished docsplit' }
          FileUtils.rm(p)
          dputs(5) { 'Finished rm' }
          outfile = p.sub(/\.[^\.]*$/, format == :certificate ? '.pdf' : '.png')
          outfiles.push outfile
          @make_pdfs_state[student_name][1] = 'done'
          @make_pdfs_state[student_name][2] = outfile.sub(/^#{@proxy.dir_diplomas}./, '')
        }
        @make_pdfs_state['0'] = 'collecting'
        if format == :certificate
          dputs(3) { "Getting #{outfiles.inspect} out of #{dir}" }
          all = "#{dir}/000-all.pdf"
          psn = "#{dir}/000-4pp.pdf"
          #cmd = "pdftk #{outfiles.join( ' ' )} cat output #{all}"
          if outfiles.length > 1
            cmd = "pdfunite #{outfiles.join(' ')} #{all}"
            dputs(3) { "Putting it all in one file: #{cmd}" }
            System.run_bool(cmd)
          else
            dputs(3) { "#{outfiles.first} - #{all}" }
            FileUtils.cp(outfiles.first, all)
          end
          dputs(3) { "Putting 4 pages of #{all} into #{psn}" }
          pf = ctype.data_get(:page_format, true)[0]
          format = ['', '-f', '-l', '-r'][pf - 1]
          dputs(3) { "Page-format is #{pf.inspect}: #{format}" }
          System.run_bool("pdftops #{all} - | psnup -4 #{format} | ps2pdf -sPAPERSIZE=a4 - #{psn}.tmp")
          FileUtils.mv("#{psn}.tmp", psn)
          dputs(2) { 'Finished' }
        else
          dputs(3) { 'Making a zip-file' }
          Zip::File.open("#{dir}/all.zip", Zip::File::CREATE) { |z|
            Dir.glob("#{dir}/*").each { |image|
              z.get_output_stream(image.sub('.*/', '')) { |f|
                File.open(image) { |fi|
                  f.write fi.read
                }
              }
            }
          }
        end
      end
    rescue Exception => e
      dputs(0) { "Error in thread: #{e.message}" }
      dputs(0) { "#{e.inspect}" }
      dputs(0) { "#{e.to_s}" }
      puts e.backtrace
    end
    @make_pdfs_state['0'] = 'done'
  }
end
md5_exams() click to toggle source
# File Entities/Course.rb, line 1556
def md5_exams
  center_pre = ConfigBase.has_function?(:course_server) ?
      "#{center.login_name}_" : ''
  dputs(3) { "Fetching existing files with center -#{center_pre}-" }
  Hash[students.map { |s|
         [s.sub(/^#{center_pre}/, ''),
          Dir.glob("#{dir_exams}/#{s}/*").map { |exa_f|
            md5 = Digest::MD5.file(exa_f).hexdigest
            exa_rel = exa_f.sub(/^.*\//, '')
            dputs(3) { "Adding file #{exa_rel} with md5 #{md5}" }
            [exa_rel, md5]
          }]
       }]
end
move_payment(src, dst) click to toggle source
# File Entities/Course.rb, line 1523
def move_payment(src, dst)
  return if !students.index(src)
  return if !students.index(dst)

  log_msg :Course, "Transferring payments from #{src} to #{dst}"

  movs = entries.movements
  if archives = entries.get_archives
    movs.concat archives.collect { |a| a.movements }.flatten
  end

  p_dst = Persons.match_by_login_name(dst)
  p_src = Persons.match_by_login_name(src)
  src_dst = "#{p_src.login_name}-#{p_src.full_name} to " +
      "#{p_dst.login_name}-#{p_dst.full_name}"
  movs.select { |m|
    m.desc =~ / #{src}:/
  }.each { |m|
    other = m.get_other_account(entries)
    if other.get_path =~ /::Paid$/ ||
        other.get_path =~ /^Archive::/
      value = m.get_value(entries)
      m.desc = "Moved payment from #{src_dst}"
      Movements.create("Moved payment from #{src_dst}",
                       m.date, value, entries, other)
      Movements.create("For student #{dst}:#{p_dst.full_name}",
                       m.date, value, other, entries)
    else
      m.desc = "For student #{dst}:#{p_dst.full_name}"
    end
  }
end
payment(secretary, student, amount, date = Date.today, oldcash = false, remark: '') click to toggle source
# File Entities/Course.rb, line 1482
def payment(secretary, student, amount, date = Date.today, oldcash = false,
            remark: '')
  log_msg :course_payment, "#{secretary.full_login} got #{amount} " +
                             "of #{student.full_name} in #{name}"
  Movements.create("For student #{student.login_name}:" +
                       "#{student.full_name} -- #{remark}",
                   date.strftime('%Y-%m-%d'), amount.to_f / 1000,
                   secretary.account_due, entries)
  if secretary.has_permission?(:admin) && oldcash
    log_msg 'course-payment', 'Oldcash - doing reverse, too'
    Movements.create("old_cash for #{student.login_name}",
                     date.strftime('%Y-%m-%d'), amount.to_f / 1000,
                     entries, secretary.account_due)
  end
end
prepare_diplomas(convert = true) click to toggle source
# File Entities/Course.rb, line 977
def prepare_diplomas(convert = true)
  dputs(2) { "dir_diplomas is: #{dir_diplomas}" }
  if not File::directory? dir_diplomas
    FileUtils.mkdir(dir_diplomas)
  else
    if !@only_psnup
      FileUtils.rm_rf(Dir.glob(dir_diplomas + '/*'))
    end
  end
  #@make_pdfs_state = {}
  make_pdfs(convert)
end
print_exam_file(lp_cmd = nil) click to toggle source
print_presence(lp_cmd = nil) click to toggle source
rename(new_name) click to toggle source
# File Entities/Course.rb, line 1571
def rename(new_name)
  if new_name == name
    log_msg :Courses, "Renaming #{name} with #{new_name} is useless"
    return
  end
  log_msg :Courses, "Renaming #{name} to #{new_name}"
  dir = "#{@proxy.dir_exams}/#{name}"
  if File.exists?(dir)
    log_msg :Courses, "Moving files of #{name} to #{new_name}"
    FileUtils.mv dir, "#{@proxy.dir_exams}/#{new_name}"
  end
  self.name = new_name
  if entries
    entries.name = new_name
  end
end
report_list() click to toggle source
# File Entities/Course.rb, line 1443
def report_list
  entries or return []
  movs = entries.movements
  if archives = entries.get_archives
    movs.concat archives.collect { |a| a.movements }.flatten
  end

  students.sort { |a, b|
    Persons.match_by_login_name(a).full_name <=>
        Persons.match_by_login_name(b).full_name }.collect { |s|
    total = 0
    (movs.select { |e| e.desc =~ / #{s}:/ }.collect { |e|
      total += e.value
      remark = (e.desc =~ / -- /) ? e.desc.sub(/.*-- /, '') : ''
      [e.global_id,
       [e.date,
        remark,
        e.value_form,
        ''
       ]] } +
        [[nil,
          ['Reste',
           "#{Persons.match_by_login_name(s).full_name} (#{s})",
           Account.total_form(total),
           Account.total_form(cost_student.to_f / 1000 - total)
          ]]]).reverse
  }.flatten(1)
end
report_pdf() click to toggle source
# File Entities/Course.rb, line 1379
def report_pdf
  file = "/tmp/course_#{name}.pdf"
  Prawn::Document.generate(file,
                           :page_size => 'A4',
                           :page_layout => :portrait,
                           :bottom_margin => 2.cm) do |pdf|

    sum = 0
    pdf.text "Report for #{name} (#{ctype.name})",
             :align => :center, :size => 20
    pdf.font_size 10
    pdf.text "Duration: #{start}-#{self.end} - - Teacher: #{teacher.full_name}" +
                 " - - Hours: #{hours}"
    pdf.text "Cost per student: #{Account.total_form(cost_student.to_i / 1000)} - - " +
                 "Cost per teacher: #{Account.total_form(salary_teacher.to_i / 1000)}"
    pdf.text "Account: #{entries.path}"
    pdf.move_down 1.cm

    if students.length > 0
      pdf.table([['Description', 'Value', 'Sum'].collect { |ch|
                   {:content => ch, :align => :center} }] +
                    report_list.collect { |id, t|
                      [t[0] == 'Reste' ? t[1] : t[0],
                       t[2],
                       t[3]]
                    },
                :header => true, :column_widths => [300, 75, 75])
      pdf.move_down(2.cm)
    end

    pdf.repeat(:all, :dynamic => true) do
      pdf.draw_text "#{Date.today} - #{entries.path}",
                    :at => [0, -20], :size => 10
      pdf.draw_text pdf.page_number, :at => [18.cm, -20]
    end
  end
  file
end
setup_instance() click to toggle source
# File Entities/Course.rb, line 418
def setup_instance
  if not self.students.class == Array
    self.students = []
  end

  check_students_dir
  @make_pdfs_state = {'0' => 'done'}
  @only_psnup = false
end
sort_md5s(m) click to toggle source
# File Entities/Course.rb, line 1276
def sort_md5s(m)
  m.map { |k, v| {k => v.sort { |a, b| a[0] <=> b[0] }} }
end
student_paid(student) click to toggle source
# File Entities/Course.rb, line 1418
def student_paid(student)
  movs = entries.movements
  if archives = entries.get_archives
    movs.concat archives.collect { |a| a.movements }.flatten
  end

  movs.select { |e| e.desc =~ / #{student}:/ }
end
student_payments(student) click to toggle source
# File Entities/Course.rb, line 1427
def student_payments(student)
  total = 0
  movs = entries.movements
  if archives = entries.get_archives
    movs.concat archives.collect { |a| a.movements }.flatten
  end

  movs.reverse.select { |e| e.desc =~ / #{student}:/ }.collect { |e|
    total += e.value
    [e.global_id,
     [e.date, e.value_form, '']]
  } + [[nil,
        ['Reste', Account.total_form(total),
         Account.total_form(cost_student.to_f / 1000 - total)]]]
end
students=(s) click to toggle source
# File Entities/Course.rb, line 1358
def students=(s)
  self._students = s
  @pre_init or log_msg :course, "Students for #{name} are: #{students.inspect}"
end
students_add(studs) click to toggle source
# File Entities/Course.rb, line 1363
def students_add(studs)
  [studs].flatten.each { |s|
    s.class == Person and s = s.login_name
    log_msg :course, "Adding student #{s} to course #{name}"
    self.students = (students || []) + [s]
  }
end
students_del(studs) click to toggle source
# File Entities/Course.rb, line 1371
def students_del(studs)
  [studs].flatten.each { |s|
    s.class == Person and s = s.login_name
    log_msg :course, "Deleting student #{s} to course #{name}"
    self.students = (students || []) - [s]
  }
end
sync_do() click to toggle source
# File Entities/Course.rb, line 1165
def sync_do
  @sync_state = sync_s = ''
  dputs(3) { @sync_state }

  dputs(4) { 'Responsibles' }
  @sync_state = sync_s += '<li>Transferring responsibles: '
  users = [teacher, responsible, center, assistant].compact.collect { |n| n.login_name }
  ret = sync_transfer(:users, users.collect { |s|
                              Persons.match_by_login_name(s)
                            }.compact)
  if ret._code == 'Error'
    @sync_state += "Error: #{ret._msg}"
    dputs(2) { "Error is #{ret._msg}" }
    return false
  end
  @sync_state = sync_s += 'OK</li>'

  dputs(4) { 'Students' }
  if students.length > 0
    dputs(4) { 'Students - go' }
    @sync_state = sync_s += '<li>Transferring users: '
    users = students + [teacher.login_name, responsible.login_name]
    ret = sync_transfer(:users, users.collect { |s|
                                Persons.match_by_login_name(s)
                              })
    if ret._code == 'Error'
      @sync_state += "Error: #{ret._msg}"
      return false
    end
    @sync_state = sync_s += 'OK</li>'
  end

  dputs(4) { 'Courses' }
  @sync_state = sync_s += '<li>Transferring course: '
  myself = self.to_hash(true)
  myself._students = students
  ret = sync_transfer(:course, myself)
  if ret._code == 'Error'
    @sync_state += "Error: #{ret._msg}"
    return false
  end
  @sync_state = sync_s += 'OK</li>'

  dputs(4) { 'Exams' }
  remote_exams = {}
  if true
    dputs(4) { 'Fetching remote exams' }
    @sync_state = sync_s += '<li>Demander ce qui existe déjà: '
    ret = sync_transfer(:exams_here, self.name)
    if ret._code == 'Error'
      @sync_state += "Error: #{ret._msg}"
      return false
    end
    remote_exams = ret._msg
    @sync_state = sync_s += 'OK</li>'
  end

  local_exams = md5_exams
  files = zip_create_chunks(local_exams, remote_exams)
  files.each { |file|
    dputs(4) { 'Exams - go' }
    @sync_state = sync_s + '<li>Transferring exams ' +
        "#{files.index(file) + 1}/#{files.count}: "
    file = "/tmp/#{file}"
    dputs(3) { "Exa-file is #{file}" }
    file_64 = Base64::encode64(File.open(file) { |f| f.read }.
                                   force_encoding(Encoding::ASCII_8BIT))
    ret = sync_transfer(:exams, {zip: file_64, course: name})
    if ret._code == 'Error'
      @sync_state += "Error: #{ret._msg}"
      return false
    end
  }
  @sync_state = sync_s += '<li>Transferring exams: OK</li>'

  dputs(4) { 'Grades' }
  if (grades = Grades.matches_by_course(self.course_id)).length > 0
    dputs(4) { 'Grades - go' }
    @sync_state = sync_s += '<li>Transferring grades: '
    ret = sync_transfer(:grades, grades.select { |g|
                                 g.course and g.student
                               }.collect { |g|
                                 dputs(4) { "Found grade with #{g.course.inspect} and #{g.student.inspect}" }
                                 g.to_hash(true).merge(:course => g.course.name,
                                                       :person => g.student.login_name)
                               })
    if ret._code == 'Error'
      @sync_state += "Error: #{ret._msg}"
      return false
    end
    grades = ret._msg
    #grades = JSON.parse(ret.sub(/^OK: /, ''))
    dputs(3) { "Return is #{grades.inspect}" }
    grades.each { |g|
      course_name, student, random = g
      course = Courses.match_by_name(course_name)
      if grade = Grades.match_by_course_person(course, student)
        dputs(4) { "Setting grade-random of #{grade.grade_id} to #{random}" }
        grade.random = random
      else
        dputs(0) { "Error: Can't find grade for #{course}-#{student}!" }
      end
    }
    @sync_state = sync_s += 'OK</li>'
  end

  @sync_state = sync_s += 'It is finished!'
  dputs(3) { @sync_state }
  return true
end
sync_start() click to toggle source
# File Entities/Course.rb, line 1303
def sync_start
  if @thread
    dputs(2) { 'Thread is here, killing' }
    begin
      abort_pdfs
    rescue Exception => e
      dputs(0) { "Error while killing: #{e.message}" }
      dputs(0) { "#{e.inspect}" }
      dputs(0) { "#{e.to_s}" }
      puts e.backtrace
    end
  end
  dputs(2) { 'Starting new thread' }
  @sync_state = 'Starting'
  @thread = Thread.new {
    begin
      sync_do
    rescue Exception => e
      dputs(0) { "Error in thread: #{e.message}" }
      dputs(0) { "#{e.inspect}" }
      dputs(0) { "#{e.to_s}" }
      puts e.backtrace
      @sync_state += "Error: thread reported #{e.to_s}"
    end
  }
end
sync_transfer(field, transfer = '', json = true) click to toggle source
# File Entities/Course.rb, line 1156
def sync_transfer(field, transfer = '', json = true)
  ss = @sync_state
  ret = ICC.transfer(Persons.center, "Courses.#{field}", transfer,
                     url: ConfigBase.get_url(:server_url), json: json) { |s|
    @sync_state = "#{ss} #{s}" }
  @sync_state = ss
  ret
end
to_hash(unique_ids = false) click to toggle source
Calls superclass method
# File Entities/Course.rb, line 493
def to_hash(unique_ids = false)
  ret = super(unique_ids).clone
  ret.delete :students
  ret.merge :students => list_students
end
transfer_student(student, new_course) click to toggle source
# File Entities/Course.rb, line 1498
def transfer_student(student, new_course)
  return if !students.index(student)
  return if !new_course.entries
  return if new_course.students.index(student)

  log_msg 'course', "Transferring #{student} from #{name} to #{new_course.name}"
  self.students = students - [student]
  new_course.students_add student

  entries.movements.select { |m|
    m.desc =~ / #{student}:/
  }.each { |m|
    other = m.get_other_account(entries)
    if other.get_path =~ /::Paid$/
      Movements.create("Transfert of student #{student}:",
                       m.date, m.get_value(entries), entries, new_course.entries)
    else
      value = m.get_value(entries)
      m.value = 0
      m.account_dst_id = new_course.entries
      m.value = value
    end
  }
end
update_exam_file() click to toggle source
# File Entities/Course.rb, line 428
def update_exam_file
  if ctype.file_exam.class == Array
    @print_exam_file = OpenPrint.new("#{ConfigBase.template_dir}/#{ctype.file_exam.first}")
  end
end
update_state(force = false) click to toggle source
# File Entities/Course.rb, line 441
def update_state(force = false)
  if @make_pdfs_state[0] == 'done' || force
    students.collect { |s|
      dputs(3) { "Working on #{s}" }
      Persons.match_by_login_name(s)
    }.compact.each { |s|
      get_grade_args(s, true)
    }
  end
end
update_student_diploma(file, student) click to toggle source
# File Entities/Course.rb, line 751
def update_student_diploma(file, student)
  # dputs_func
  grade, state = get_grade_args(student)

  #if grade and grade.to_s != "NP" and
  #    ( ( ctype.diploma_type[0] == "simple" ) or
  #      ( exam_files( student ).count >= ctype.files_nbr.to_i ) ) and
  #    ( ( ctype.diploma_type[0] == "accredited" ) and grade.random )
  #  @make_pdfs_state[student.login_name] = [ grade.mean, "queued" ]
  if state != 'queued'
    dputs(2) {"File #{file} is not queued, but #{state} - removing"}
    FileUtils.rm(file)
  else
    dputs(3) { "New diploma for: #{course_id} - #{student.login_name} - #{grade.to_hash.inspect}" }
    Zip::File.open(file) { |z|
      dputs(5) { "Cours is #{self.inspect}" }
      doc = z.read('content.xml')
      doc.force_encoding(Encoding::UTF_8)
      dputs(5) { doc.inspect }
      dputs(5) { "Contents is: #{contents.inspect}" }
      if qrcode = /draw:image.*xlink:href="([^"]*).*QRcode.*\/draw:frame/.match(doc)
        dputs(2) { "QRcode-image is #{qrcode[1]}" }
        qr = RQRCode::QRCode.new(grade.get_url_label)
        png = qr.as_png(:border_modules => 0)
        z.get_output_stream(qrcode[1]) { |f|
          png.write(f)
        }
      end

      cont = contents +
          (grade.remark.to_s.length > 0 ? "\n#{grade.remark}" : '')
      if desc_p_match = /-DESC1-(.*)-DESC2-/.match(doc)
        desc_p = desc_p_match[1]
        dputs(3) { "desc_p is #{desc_p}" }
        doc.gsub!(/-DESC1-.*-DESC2-/,
                  cont.split("\n").join(desc_p))
      end
      doc.gsub!(/-TEACHER-/, teacher.full_name)
      role_diploma = 'Enseignant informatique'
      if teacher.role_diploma.to_s.length > 0
        role_diploma = teacher.role_diploma
      end
      doc.gsub!(/-TEACHER_ROLE-/, role_diploma)
      role_diploma = 'Responsable informatique'
      if responsible.role_diploma.to_s.length > 0
        role_diploma = responsible.role_diploma
      end
      show_year = start.gsub(/.*\./, '') != self.end.gsub(/.*\./, '')
      c = center
      OpenPrint.replace(
          doc,
          [[/-RESP_ROLE-/, role_diploma],
           [/-RESP-/, responsible.full_name],
           [/-NAME-/, student.full_name],
           [/-DURATION-/, duration.to_s],
           [/-COURSE-/, description],
           [/-COURSE_ID-/, name],
           [/-SPECIAL-/, ''],
           [/-GENDER-/, student.gender_i18n(ctype.diploma_lang.first)],
           [/-GRADE-/, grade.mention],
           [/-DATE-/, date_i18n(sign)],
           [/-FROM-/, date_i18n(start, show_year)],
           [/-TO-/, date_i18n(self.end)],
           [/-COURSE_TYPE-/, ctype.name],
           [/-URL_LABEL-/, grade.get_url_label],
           [/-CENTER_NAME-/, c.full_name],
           [/-CENTER_ADDRESS-/, c.address || ''],
           [/-CENTER_PLACE-/, c.town || ''],
           [/-CENTER_PHONE-/, c.phone || ''],
           [/-CENTER_EMAIL-/, c.email || ''],
           [/-MEAN-/, sprintf('%2.2f', grade.mean)]])

      dputs(3) { "ctype is #{ctype.inspect}" }
      if ctype.diploma_type.first =~ /report/
        dputs(3) { 'Adding report-lines' }
        if test_p_match = /-TEST1-(.*)-MEAN1-(.*)-TEST2-/.match(doc)
          test_p = test_p_match[1..2]
          dputs(3) { "test_p is #{test_p.inspect}" }
          if grade.means.count == ctype.tests_arr.count
            tests = ctype.tests_arr.zip(grade.means).collect { |t, m|
              t + test_p[0] + sprintf('%2.1f', m)
            }.join(test_p[1])
            doc.gsub!(/-TEST1-.*-MEAN2-/, tests)
          else
            dputs(1) { "Incomplete tests for #{student.login_name}" }
          end
        end
      end
      doc.gsub!(/(number-rows-spanned=)\"3\"/, "\\1\"#{ctype.tests_nbr.to_i+1}\"")
      z.get_output_stream('content.xml') { |f|
        f.write(doc)
      }
      z.commit
    }
  end
end
zip_create(for_server: true, include_files: true, md5sums: {}, size_exams: -1, files_added: nil) click to toggle source

This prepares a zip-file as a skeleton for the center to copy the files over. The name of the zip-file is different from the directory-name, so that the upload is less error-prone. @param [Object] for_server @param [Object] include_files @param [Object] md5sums @param [Object] users

# File Entities/Course.rb, line 998
def zip_create(for_server: true, include_files: true, md5sums: {},
               size_exams: -1, files_added: nil)
  pre = for_server ? center.login_name + '_' : ''
  dir = "exa-#{pre}#{name}"
  file = "#{pre}#{name}.zip"
  tmp_file = "/tmp/#{file}"
  dputs(2) { "for_server:#{for_server} - include_files:#{include_files} " +
      "md5sums:#{md5sums.inspect} - size_exams:#{size_exams.inspect}" }

  if students and students.size > 0
    File.exists?(tmp_file) and FileUtils.rm(tmp_file)
    Zip::File.open(tmp_file, Zip::File::CREATE) { |z|
      z.mkdir dir
      dputs(3) { "Students is #{students.inspect}" }
      files_excluded = []
      students.sort.each { |s|
        p = "#{dir}/#{pre}#{s}"
        dputs(3) { "Creating #{p}" }
        z.mkdir(p)
        if include_files
          dputs(3) { "Searching in #{dir_exams}/#{s}" }
          Dir.glob("#{dir_exams}/#{s}/*").sort.each { |exa_f|
            exa_md5 = Digest::MD5.file(exa_f).hexdigest
            file_add = true
            filename = exa_f.sub(/.*\//, '')
            if md5sums.has_key?(s)
              md5sums[s].each { |f, md5|
                if (f == filename) && (md5 == exa_md5)
                  dputs(3) { "Found file #{filename} to be excluded" }
                  file_add = false
                  files_excluded.push exa_f.sub(/^#{dir_exams}\//, '')
                end
              }
            end
            if file_add and size_exams != 0
              dputs(3) { "Adding file #{exa_f} with size #{size_exams}" }
              files_added and files_added.push [s, File.basename(exa_f), exa_md5]
              z.file.open("#{p}/#{exa_f.sub(/.*\//, '')}", 'w') { |f|
                f.write File.open(exa_f) { |ef|
                          content = ef.read
                          dputs(3) { "Size of file is #{content.size}" }
                          size_exams -= [content.size, size_exams.abs].min
                          content
                        }
              }
            end
          }
        end
      }
      dputs(3) { "Files_excluded are #{files_excluded.inspect}" }
      z.file.open("#{dir}/files_excluded", 'w') { |f|
        f.write files_excluded.to_json
      }
    }
    return file
  end
  return nil
end
zip_create_chunks(local, remote) click to toggle source
# File Entities/Course.rb, line 1280
def zip_create_chunks(local, remote)
  files = []
  loop {
    fa = []
    dputs(3) { "Remote: #{remote.inspect}" }
    dputs(3) { "Local: #{local.inspect}" }
    zipfile = zip_create(md5sums: remote, size_exams: ConfigBase.max_upload_size.to_i,
                         files_added: fa)
    fa.length == 0 and return files

    zipfile_cnt = "#{zipfile.chomp('.zip')}-#{files.size}.zip"
    FileUtils.mv "/tmp/#{zipfile}", "/tmp/#{zipfile_cnt}"
    files.push zipfile_cnt
    dputs(3) { "Zip-file #{files.last} has files added #{fa.inspect}" }
    fa.each { |s, f, md5|
      dputs(3) { "Found student #{s} with file #{f} and md5 #{md5} in zip" }
      remote[s] ||= []
      remote[s].push [f, md5]
    }
  }
  []
end
zip_read(f = nil) click to toggle source
# File Entities/Course.rb, line 1057
def zip_read(f = nil)
  name.length == 0 and return

  dir_zip = "exa-#{name.sub(/^#{center}_/, '')}"
  dir_exams = @proxy.dir_exams + "/#{name}"
  dir_exams_tmp = "/tmp/#{name}"
  file = f || "/tmp/#{dir_zip}.zip"
  dputs(3) { "dir_zip: #{dir_zip}, dir_exams: #{dir_exams}, dir_exams_tmp: #{dir_exams_tmp}, " +
      "file: #{file}" }

  if File.exists?(file) && students
    # Save existing exams in /tmp
    FileUtils.rm_rf dir_exams_tmp
    if File.exists? dir_exams
      dputs(3) { "Moving #{dir_exams} to /tmp" }
      dputs(3) { "#{dir_exams} is " + Dir.glob("#{dir_exams}/**/*").join(' ') }
      FileUtils.mv dir_exams, dir_exams_tmp
    end
    FileUtils.mkdir dir_exams

    dputs(3) { "Opening zip-file #{file}" }
    Zip::File.open(file) { |z|
      students.each { |s|
        dir_zip_student = "#{dir_zip}/#{s}"
        dir_exams_student = "#{dir_exams}/#{s}"

        begin
          FileUtils.mkdir(dir_exams_student)
          if (files_student = z.dir.entries(dir_zip_student)).size > 0
            files_student.each { |fs|
              dputs(3) { "Extracting #{dir_exams_student}/#{fs}" }
              z.extract("#{dir_zip_student}/#{fs}", "#{dir_exams_student}/#{fs}")
            }
          end
        rescue Errno::ENOENT => e
          dputs(3) { "Directory for student #{s} doesn't exist" }
        end
      }
      begin
        center_pre = ConfigBase.has_function?(:course_server) ?
            "#{center.login_name}_" : ''
        JSON.parse(z.read("#{dir_zip}/files_excluded")).each { |f|
          dputs(3) { "Transferring file #{f} from old to new directory" }
          FileUtils.cp "#{dir_exams_tmp}/#{center_pre + f}",
                       "#{dir_exams}/#{center_pre + f}"
        }
      rescue Errno::ENOENT => e
        dputs(3) { 'No files_excluded here' }
      end
    }
    FileUtils.rm file
  end
end