diff --git a/app/assets/javascripts/admins/competitions/index.js b/app/assets/javascripts/admins/competitions/index.js new file mode 100644 index 000000000..ae4593d33 --- /dev/null +++ b/app/assets/javascripts/admins/competitions/index.js @@ -0,0 +1,11 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-competitions-index-page').length > 0) { + $('.modal.admin-upload-file-modal').on('upload:success', function(e, data){ + var $imageElement = $('.competition-image-' + data.source_id); + $imageElement.attr('src', data.url); + $imageElement.show(); + $imageElement.next().html('重新上传'); + }) + } +}); + diff --git a/app/controllers/admins/competitions_controller.rb b/app/controllers/admins/competitions_controller.rb index 3b9b63243..f501564ab 100644 --- a/app/controllers/admins/competitions_controller.rb +++ b/app/controllers/admins/competitions_controller.rb @@ -11,6 +11,7 @@ class Admins::CompetitionsController < Admins::BaseController ids = @competitions.map(&:id) @member_count_map = TeamMember.where(competition_id: ids).group(:competition_id).count + @competition_hot = ModuleSetting.exists?(module_type: "Competition", property: "hot") respond_to do |format| format.js format.html diff --git a/app/controllers/competitions/competition_teams_controller.rb b/app/controllers/competitions/competition_teams_controller.rb index e03810b61..f504b226e 100644 --- a/app/controllers/competitions/competition_teams_controller.rb +++ b/app/controllers/competitions/competition_teams_controller.rb @@ -1,4 +1,63 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController + before_action :tech_mode, only: [:shixun_detail, :course_detail] + + def shixun_detail + start_time = current_competition.competition_mode_setting&.start_time || current_competition.start_time + end_time = current_competition.competition_mode_setting&.end_time || current_competition.end_time + + team_user_ids = @team.team_members.pluck(:user_id) + shixuns = Shixun.where(user_id: team_user_ids, status: 2) + .where('shixuns.created_at > ? && shixuns.created_at <= ?', start_time, end_time) + shixuns = shixuns.joins('left join shixuns forked_shixuns on forked_shixuns.fork_from = shixuns.id and forked_shixuns.status = 2') + shixuns = shixuns.select('shixuns.id, shixuns.identifier, shixuns.user_id, shixuns.myshixuns_count, shixuns.name, shixuns.fork_from, sum(forked_shixuns.myshixuns_count) forked_myshixun_count') + @shixuns = shixuns.group('shixuns.id').order('shixuns.myshixuns_count desc').includes(:user) + + shixun_ids = @shixuns.map(&:id) + @myshixun_count_map = get_valid_myshixun_count(shixun_ids) + @original_myshixun_count_map = @myshixun_count_map.clone + # forked shixun valid myshixun count + forked_shixun_map = Shixun.where(status: 2, fork_from: shixun_ids).select('id, fork_from') + forked_shixun_map = forked_shixun_map.each_with_object({}) { |sx, obj| obj[sx.id] = sx.fork_from } + @forked_myshixun_count_map = get_valid_myshixun_count(forked_shixun_map.keys) + @forked_myshixun_count_map.each { |k, v| @myshixun_count_map[forked_shixun_map[k]] += v } + + @course_count_map = get_valid_course_count(shixun_ids) + @forked_map = get_valid_course_count(forked_shixun_map.keys) + @forked_course_count_map = {} + @forked_map.each do |forked_id, course_count| + @forked_course_count_map[forked_shixun_map[forked_id]] ||= 0 + @forked_course_count_map[forked_shixun_map[forked_id]] += course_count + end + @forked_shixun_map = forked_shixun_map + end + + def course_detail + start_time = current_competition.competition_mode_setting&.start_time || current_competition.start_time + end_time = current_competition.competition_mode_setting&.end_time || current_competition.end_time + @team_user_ids = @team.team_members.pluck(:user_id) + + student_count_subquery = CourseMember.where('course_id = courses.id AND role = 4').select('count(*)').to_sql + subquery = StudentWork.where('homework_common_id = hcs.id') + .select('sum(compelete_status !=0 ) as finish, count(*) as total') + .having('total != 0 and finish >= (total / 2)').to_sql + course_ids = Course.where('courses.created_at > ?', start_time) + .where('courses.created_at <= ?', end_time) + .where("(#{student_count_subquery}) >= 3") + .where("exists(select 1 from homework_commons hcs where hcs.course_id = courses.id and hcs.publish_time is not null and hcs.publish_time < NOW() and hcs.homework_type = 4 and exists(#{subquery}))") + .joins('join course_members on course_members.course_id = courses.id and course_members.role in (1,2,3)') + .where(course_members: { user_id: @team_user_ids }).pluck(:id) + courses = Course.where(id: course_ids).joins(:practice_homeworks).where('homework_commons.publish_time < now()') + @courses = courses.select('courses.id, courses.name, courses.members_count, count(*) shixun_homework_count') + .group('courses.id').order('shixun_homework_count desc').having('shixun_homework_count > 0') + + course_ids = @courses.map(&:id) + @course_myshixun_map = Myshixun.joins(student_works: :homework_common) + .where(homework_commons: { course_id: course_ids }) + .where('exists(select 1 from games where games.myshixun_id = myshixuns.id and games.status = 2)') + .group('homework_commons.course_id').count + @course_shixun_count_map = get_valid_shixun_count(course_ids) + end + def index admin_or_business? ? all_competition_teams : user_competition_teams end @@ -67,4 +126,37 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController def save_params params.permit(:name, teacher_ids: [], member_ids: []) end + + def tech_mode + # render_not_found if current_competition.mode != 3 + @team = current_competition.competition_teams.find_by(id: params[:id]) + end + + def get_valid_myshixun_count(ids) + Myshixun.where(shixun_id: ids) + .where('exists(select 1 from games where games.myshixun_id = myshixuns.id and games.status = 2)') + .group('shixun_id').count + end + + def get_valid_course_count(ids) + percentage_sql = StudentWork.where('homework_common_id = homework_commons.id and homework_commons.publish_time is not null and homework_commons.publish_time < NOW()') + .select('sum(compelete_status !=0 ) as finish, count(*) as total') + .having('total != 0 and finish >= (total / 2)').to_sql + + Course.joins(practice_homeworks: :homework_commons_shixun) + .where('shixun_id in (?)', ids) + .where("exists (#{percentage_sql})") + .group('shixun_id').count + end + + def get_valid_shixun_count(ids) + percentage_sql = StudentWork.where('homework_common_id = homework_commons.id and homework_commons.publish_time is not null and homework_commons.publish_time < NOW()') + .select('sum(compelete_status !=0 ) as finish, count(*) as total') + .having('total != 0 and finish >= (total / 2)').to_sql + Shixun.joins(homework_commons_shixuns: :homework_common) + .where(homework_commons: { homework_type: 4 }) + .where('course_id in (?)', ids) + .where("exists (#{percentage_sql})") + .group('course_id').count + end end diff --git a/app/controllers/competitions/competitions_controller.rb b/app/controllers/competitions/competitions_controller.rb index 77159fdaf..03a276952 100644 --- a/app/controllers/competitions/competitions_controller.rb +++ b/app/controllers/competitions/competitions_controller.rb @@ -1,6 +1,10 @@ class Competitions::CompetitionsController < Competitions::BaseController + include CompetitionsHelper + skip_before_action :require_login before_action :allow_visit, except: [:index] + before_action :require_admin, only: [:update, :update_inform] + before_action :chart_visible, only: [:charts, :chart_rules] def index # 已上架 或者 即将上架 @@ -25,10 +29,108 @@ class Competitions::CompetitionsController < Competitions::BaseController end def show + @competition = current_competition + end + + def update + @competition.update_attributes!(introduction: params[:introduction]) + normal_status("更新成功") end def common_header + @competition = current_competition + @competition_modules = @competition.unhidden_competition_modules + @user = current_user + end + + def informs + status = params[:status] || 1 + @informs = current_competition.informs.where(status: status) + end + + def update_inform + tip_exception("标题和内容不能为空") if params[:name].blank? || params[:description].blank? + tip_exception("status参数有误") if params[:status].blank? || ![1, 2].include?(params[:status].to_i) + ActiveRecord::Base.transaction do + if params[:inform_id] + inform = current_competition.informs.find_by!(id: params[:inform_id]) + inform.update_attributes!(name: params[:name], description: params[:description]) + else + inform = current_competition.informs.create!(name: params[:name], description: params[:description], status: params[:status]) + end + Attachment.associate_container(params[:attachment_ids], inform.id, inform.class) if params[:attachment_ids] + normal_status("更新成功") + end + end + + def md_content + @md_content = CompetitionModuleMdContent.find_by!(id: params[:md_content_id]) + end + + def update_md_content + tip_exception("标题和内容不能为空") if params[:name].blank? || params[:content].blank? + ActiveRecord::Base.transaction do + if params[:md_content_id] + md_content = CompetitionModuleMdContent.find_by!(id: params[:md_content_id]) + md_content.update_attributes!(name: params[:name], content: params[:content]) + else + md_content = CompetitionModuleMdContent.create!(name: params[:name], content: params[:content]) + end + Attachment.associate_container(params[:attachment_ids], md_content.id, md_content.class) if params[:attachment_ids] + normal_status("更新成功") + end + end + + def chart_rules + @competition = current_competition + @stages = @competition.competition_stages + @rule_contents = @competition.chart_rules + end + + def update_chart_rules + tip_exception("内容不能为空") if params[:content].blank? + @competition = current_competition + @stage = @competition.competition_stages.find_by!(id: params[:stage_id]) if params[:stage_id] + chart_rule = @competition.chart_rules.where(competition_stage_id: @stage&.id).first + if chart_rule + chart_rule.update_attributes!(content: params[:content]) + else + @competition.chart_rules.create!(competition_stage_id: @stage&.id, content: params[:content]) + end + normal_status("更新成功") + end + def charts + @competition = current_competition + if params[:stage_id] + @stage = @competition.competition_stages.find_by(id: params[:stage_id]) + end + + if @competition.identifier == "gcc-annotation-2018" + @records = @competition.competition_teams.joins(:competition_scores) + .select("competition_teams.*, score, cost_time").order("score desc, cost_time desc") + else + @records = @competition.competition_teams.joins(:competition_scores).where(competition_scores: {competition_stage_id: @stage&.id.to_i}) + .select("competition_teams.*, score, cost_time").order("score desc, cost_time desc") + end + + current_team_ids = @competition.team_members.where(user_id: current_user.id).pluck(:competition_team_id).uniq + @user_ranks = @records.select{|com_team| current_team_ids.include?(com_team.id)} + @records = @records.where("score > 0") + if params[:format] == "xlsx" + @records = @records.includes(user: [user_extension: :school], team_members: :user) + respond_to do |format| + format.xlsx{ + set_export_cookies + chart_to_xlsx(@records, @competition) + exercise_export_name = "#{@competition.name}比赛成绩" + render xlsx: "#{exercise_export_name.strip}",template: "competitions/competitions/chart_list.xlsx.axlsx",locals: + {table_columns: @competition_head_cells, chart_lists: @competition_cells_column} + } + end + else + @records = @records.includes(:user, :team_members).limit(@competition.awards_count) + end end private @@ -38,9 +140,60 @@ class Competitions::CompetitionsController < Competitions::BaseController end def allow_visit - unless current_competition.published? || admin_or_business? - render_forbidden - return + render_forbidden unless current_competition.published? || admin_or_business? + end + + def chart_visible + chart_module = current_competition.competition_modules.find_by(name: "排行榜") + render_forbidden unless (chart_module.present? && !chart_module.hidden) || admin_or_business? + end + + # 竞赛成绩导出 + def chart_to_xlsx records, competition + @competition_head_cells = [] + @competition_cells_column = [] + + max_staff = competition.competition_staffs.sum(:maximum) + if max_staff < 2 # 个人赛 + @competition_head_cells = %w(序号 姓名 学校 学号) + else # 战队赛 + @competition_head_cells = %w(序号 战队名 指导老师 队员 学校) + end + + statistic_stages = competition.competition_stages.where("rate > 0") + statistic_stages.each do |stage| + @competition_head_cells += ["#{stage.name}得分", "#{stage.name}用时"] + end + @competition_head_cells += ["总得分", "总用时"] if statistic_stages.size > 1 + competition_scores = competition.competition_scores + + records.each_with_index do |record, index| + row_cells_column = [] + row_cells_column << index + 1 + record_user = record.user + if max_staff < 2 + row_cells_column << record_user.real_name + row_cells_column << record_user.school_name + row_cells_column << record_user.student_id + else + row_cells_column << record.name + row_cells_column << record.teachers_name + row_cells_column << record.members_name + row_cells_column << chart_school_str(record.team_members.select{|member| !member.is_teacher}.pluck(:user_id)) + end + + statistic_stages.each do |stage| + stage_score = competition_scores.select{|score| score.competition_stage_id == stage.id && score.competition_team_id == record.id}.first + row_cells_column << stage_score&.score.to_f.round(2) + row_cells_column << com_spend_time(stage_score&.cost_time.to_i) + end + + if statistic_stages.size > 1 + row_cells_column << record&.score.to_f.round(2) + row_cells_column << com_spend_time(record&.cost_time.to_i) + end + + @competition_cells_column.push(row_cells_column) end end end \ No newline at end of file diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index 60cf2d6c5..168d05299 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -1058,8 +1058,8 @@ class HomeworkCommonsController < ApplicationController tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day) else + tip_exception("缺少分班截止时间参数") if params[:group_end_times].blank? group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time} - tip_exception("缺少截止时间参数") if group_end_times.blank? tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length group_end_times.each do |time| tip_exception("分班截止时间不能早于当前时间") if time <= Time.now diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index dbdcaea40..1d684350c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -142,6 +142,14 @@ module ApplicationHelper end end + # 主页banner图 + def banner_img(source_type) + if File.exist?(disk_filename(source_type, "banner")) + ctime = File.ctime(disk_filename(source_type, "banner")).to_i + File.join("images/avatars", ["#{source_type}", "banner"]) + "?t=#{ctime}" + end + end + def disk_filename(source_type,source_id,image_file=nil) File.join(storage_path, "#{source_type}", "#{source_id}") end diff --git a/app/helpers/competitions_helper.rb b/app/helpers/competitions_helper.rb new file mode 100644 index 000000000..66c7eec41 --- /dev/null +++ b/app/helpers/competitions_helper.rb @@ -0,0 +1,54 @@ +module CompetitionsHelper + def chart_school_str user_ids + chart_school_name = "" + chart_school_name = School.where(id: UserExtension.where(user_id: user_ids).pluck(:school_id).uniq).pluck(:name).join("、") + end + + # 耗时:小时、分、秒 00:00:00 + # 小于1秒钟则不显示 + def com_spend_time time + hour = time / (60*60) + min = time % (60*60) / 60 + sec = time % (60*60) % 60 + hour_str = "00" + min_str = "00" + sec_str = "00" + if hour >= 1 && hour < 10 + hour_str = "0#{hour}" + elsif hour >= 10 + hour_str = "#{hour}" + end + + if min >= 1 && min < 10 + min_str = "0#{min}" + elsif min >= 10 + min_str = "#{min}" + end + + if sec >= 1 && sec < 10 + sec_str = "0#{sec}" + elsif sec >= 10 + sec_str = "#{sec}" + end + + time = "#{hour_str} : #{min_str} : #{sec_str}" + return time + end + + def chart_stages competition + stages = [] + statistic_stages = competition.competition_stages.where("rate > 0") + if competition.end_time && competition.end_time < Time.now && statistic_stages.size > 1 + stages << {id: nil, name: "总排行榜", rate: 1.0, start_time: competition.start_time, end_time: competition.end_time} + end + + statistic_stages.each do |stage| + if stage.max_end_time && stage.max_end_time < Time.now + stages << {id: stage.id, name: "#{stage.name}排行榜", rate: stage.rate, start_time: stage.min_start_time, end_time: stage.max_end_time} + end + end + + stages = stages.sort { |a, b| b[:end_time] <=> a[:end_time] } if stages.size > 0 + return stages + end +end \ No newline at end of file diff --git a/app/models/chart_rule.rb b/app/models/chart_rule.rb new file mode 100644 index 000000000..de8fceaf1 --- /dev/null +++ b/app/models/chart_rule.rb @@ -0,0 +1,6 @@ +class ChartRule < ApplicationRecord + belongs_to :competition + belongs_to :competition_stage, optional: true + + validates :content, length: { maximum: 1000 } +end diff --git a/app/models/competition.rb b/app/models/competition.rb index 9055adefc..23b1e3c81 100644 --- a/app/models/competition.rb +++ b/app/models/competition.rb @@ -9,12 +9,19 @@ class Competition < ApplicationRecord has_many :competition_teams, dependent: :destroy has_many :team_members, dependent: :destroy + has_many :chart_rules, dependent: :destroy + + has_many :competition_scores, dependent: :destroy has_many :competition_staffs, dependent: :destroy has_one :teacher_staff, -> { where(category: :teacher) }, class_name: 'CompetitionStaff' has_one :member_staff, -> { where.not(category: :teacher) }, class_name: 'CompetitionStaff' + has_one :competition_mode_setting, dependent: :destroy + + has_many :informs, as: :container, dependent: :destroy + has_many :attachments, as: :container, dependent: :destroy - has_many :attachments, as: :container + has_many :competition_awards, dependent: :destroy after_create :create_competition_modules @@ -81,6 +88,16 @@ class Competition < ApplicationRecord member_staff.mutiple_limited? end + def max_min_stage_time + CompetitionStageSection.find_by_sql("SELECT MAX(end_time) as max_end_time, MIN(start_time) as min_start_time, + competition_stage_id FROM competition_stage_sections WHERE competition_id = #{id} + GROUP BY competition_stage_id order by competition_stage_id") + end + + def awards_count + competition_awards.pluck(:num)&.sum > 0 ? competition_awards.pluck(:num)&.sum : 20 + end + private def create_competition_modules diff --git a/app/models/competition_award.rb b/app/models/competition_award.rb new file mode 100644 index 000000000..715d82ee2 --- /dev/null +++ b/app/models/competition_award.rb @@ -0,0 +1,3 @@ +class CompetitionAward < ApplicationRecord + belongs_to :competition +end diff --git a/app/models/competition_mode_setting.rb b/app/models/competition_mode_setting.rb index b6bafa7c3..e8d6c17d7 100644 --- a/app/models/competition_mode_setting.rb +++ b/app/models/competition_mode_setting.rb @@ -1,3 +1,4 @@ class CompetitionModeSetting < ApplicationRecord - belongs_to :course + belongs_to :course, optional: true + belongs_to :competition end diff --git a/app/models/competition_module.rb b/app/models/competition_module.rb index be73bf3c1..9579533da 100644 --- a/app/models/competition_module.rb +++ b/app/models/competition_module.rb @@ -4,4 +4,21 @@ class CompetitionModule < ApplicationRecord belongs_to :competition has_one :competition_module_md_content, dependent: :destroy + + def module_url + case name + when "首页", "赛制介绍" + "/competitions/#{competition.identifier}" + when "通知公告" + "/competitions/#{competition.identifier}/informs?status=1" + when "参赛手册" + "/competitions/#{competition.identifier}/informs?status=2" + when "排行榜" + "/competitions/#{competition.identifier}/charts" + when "报名" + "/competitions/#{competition.identifier}/competition_teams" + else + url || "/competitions/#{competition.identifier}/md_content?md_content_id=#{competition_module_md_content&.id}" + end + end end diff --git a/app/models/competition_score.rb b/app/models/competition_score.rb new file mode 100644 index 000000000..ce7bad427 --- /dev/null +++ b/app/models/competition_score.rb @@ -0,0 +1,6 @@ +class CompetitionScore < ApplicationRecord + belongs_to :user + belongs_to :competition + belongs_to :competition_stage, optional: true + belongs_to :competition_team, optional: true +end diff --git a/app/models/competition_stage.rb b/app/models/competition_stage.rb index 60d4b1644..dec777b39 100644 --- a/app/models/competition_stage.rb +++ b/app/models/competition_stage.rb @@ -3,5 +3,15 @@ class CompetitionStage < ApplicationRecord has_many :competition_stage_sections, dependent: :destroy has_many :competition_entries, dependent: :destroy + has_many :competition_scores, dependent: :destroy + has_one :chart_rule, dependent: :destroy + + def min_start_time + competition_stage_sections.where("start_time is not null").pluck(:start_time).min + end + + def max_end_time + competition_stage_sections.where("end_time is not null").pluck(:end_time).max + end end \ No newline at end of file diff --git a/app/models/competition_team.rb b/app/models/competition_team.rb index 625b29421..878f58882 100644 --- a/app/models/competition_team.rb +++ b/app/models/competition_team.rb @@ -7,6 +7,7 @@ class CompetitionTeam < ApplicationRecord has_many :team_members, dependent: :destroy has_many :users, through: :team_members, source: :user + has_many :competition_scores, dependent: :destroy has_many :members, -> { without_teachers }, class_name: 'TeamMember' has_many :teachers, -> { only_teachers }, class_name: 'TeamMember' @@ -30,4 +31,12 @@ class CompetitionTeam < ApplicationRecord self.code = code code end + + def teachers_name + teachers.map{|teacher| teacher.user.real_name}.join(",") + end + + def members_name + members.map{|member| member.user.real_name}.join(",") + end end \ No newline at end of file diff --git a/app/models/inform.rb b/app/models/inform.rb index 5caf80c5f..d486b6f11 100644 --- a/app/models/inform.rb +++ b/app/models/inform.rb @@ -3,4 +3,6 @@ class Inform < ApplicationRecord validates :name, length: { maximum: 60 } validates :description, length: { maximum: 5000 } + + has_many :attachments, as: :container, dependent: :destroy end diff --git a/app/models/laboratory_setting.rb b/app/models/laboratory_setting.rb index 32848dca2..ca62f5c91 100644 --- a/app/models/laboratory_setting.rb +++ b/app/models/laboratory_setting.rb @@ -43,7 +43,7 @@ class LaboratorySetting < ApplicationRecord navbar: [ { 'name' => '实践课程', 'link' => '/paths', 'hidden' => false }, { 'name' => '翻转课堂', 'link' => '/courses', 'hidden' => false }, - { 'name' => '实现项目', 'link' => '/shixuns', 'hidden' => false }, + { 'name' => '实训项目', 'link' => '/shixuns', 'hidden' => false }, { 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false }, { 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false }, { 'name' => '交流问答', 'link' => '/forums', 'hidden' => false }, diff --git a/app/models/module_setting.rb b/app/models/module_setting.rb new file mode 100644 index 000000000..36fc45e6f --- /dev/null +++ b/app/models/module_setting.rb @@ -0,0 +1,2 @@ +class ModuleSetting < ApplicationRecord +end diff --git a/app/models/shixun.rb b/app/models/shixun.rb index e0feb80ce..38df5f0d4 100644 --- a/app/models/shixun.rb +++ b/app/models/shixun.rb @@ -27,6 +27,7 @@ class Shixun < ApplicationRecord has_one :first_shixun_tag_repertoire, class_name: 'ShixunTagRepertoire' has_one :first_tag_repertoire, through: :first_shixun_tag_repertoire, source: :tag_repertoire + has_many :homework_commons_shixuns, class_name: 'HomeworkCommonsShixun' #实训的关卡 has_many :exercise_shixun_challenges, :dependent => :destroy diff --git a/app/views/admins/competition_settings/show.html.erb b/app/views/admins/competition_settings/show.html.erb index e69de29bb..6272687e4 100644 --- a/app/views/admins/competition_settings/show.html.erb +++ b/app/views/admins/competition_settings/show.html.erb @@ -0,0 +1,16 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('竞赛列表', admins_competitions_path) + add_admin_breadcrumb(@competition.name) + end +%> + +