Mercurial > hg > soundsoftware-site
changeset 1025:02ee54197879 luisf
Merge
author | luisf <luis.figueira@eecs.qmul.ac.uk> |
---|---|
date | Tue, 13 Nov 2012 12:10:15 +0000 |
parents | 503ed68a4e0f (current diff) 52be96e83080 (diff) |
children | b42553f6df71 b0e0ffb43fa1 beea10a56132 |
files | .hgignore |
diffstat | 37 files changed, 700 insertions(+), 126 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Tue Nov 13 11:22:53 2012 +0000 +++ b/.hgignore Tue Nov 13 12:10:15 2012 +0000 @@ -29,7 +29,7 @@ .svn/ .git/ *~ - +public/themes/soundsoftware/stylesheets/fonts/* .bundle Gemfile.lock
--- a/app/controllers/activities_controller.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/controllers/activities_controller.rb Tue Nov 13 12:10:15 2012 +0000 @@ -40,7 +40,22 @@ events = @activity.events(@date_from, @date_to) - if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, User.current, current_language]) + @institution_name = params[:institution] + if !@institution_name.blank? + events = events.select do |e| + e.respond_to?(:event_author) and e.event_author and + e.event_author.respond_to?(:ssamr_user_detail) and + !e.event_author.ssamr_user_detail.nil? and + e.event_author.ssamr_user_detail.institution_name == @institution_name + end + if events.empty? + # We don't want to dump into the output any arbitrary string + # from the URL that has no matching events + @institution_name = "" + end + end + + if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, @institution_name, events.first, User.current, current_language]) respond_to do |format| format.html { @events_by_day = events.group_by(&:event_date)
--- a/app/controllers/attachments_controller.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/controllers/attachments_controller.rb Tue Nov 13 12:10:15 2012 +0000 @@ -17,6 +17,9 @@ class AttachmentsController < ApplicationController + include AttachmentsHelper + helper :attachments + before_filter :find_project before_filter :file_readable, :read_authorize, :except => :destroy before_filter :delete_authorize, :only => :destroy @@ -49,7 +52,9 @@ end def download - if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project) + # cc: formerly this happened only if "@attachment.container.is_a?(Version)" + # or Project. Not good for us, we want to tally all downloads [by humans] + if not user_is_search_bot? @attachment.increment_download end
--- a/app/controllers/projects_controller.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/controllers/projects_controller.rb Tue Nov 13 12:10:15 2012 +0000 @@ -20,8 +20,8 @@ menu_item :roadmap, :only => :roadmap menu_item :settings, :only => :settings - before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ] - before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy] + before_filter :find_project, :except => [ :index, :list, :explore, :new, :create, :copy ] + before_filter :authorize, :except => [ :index, :list, :explore, :new, :create, :copy, :archive, :unarchive, :destroy] before_filter :authorize_global, :only => [:new, :create] before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ] accept_rss_auth :index @@ -43,6 +43,8 @@ helper :repositories include RepositoriesHelper include ProjectsHelper + include ActivitiesHelper + helper :activities # Lists visible projects. Paginator is for top-level projects only # (subprojects belong to them) @@ -76,6 +78,16 @@ end end + # A different view of projects using explore boxes + def explore + respond_to do |format| + format.html { + @projects = Project.visible + render :template => 'projects/explore.html.erb', :layout => !request.xhr? + } + end + end + def new @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position") @trackers = Tracker.all
--- a/app/controllers/sys_controller.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/controllers/sys_controller.rb Tue Nov 13 12:10:15 2012 +0000 @@ -21,7 +21,7 @@ def projects p = Project.active.has_module(:repository).find(:all, :include => :repository, :order => 'identifier') # extra_info attribute from repository breaks activeresource client - render :xml => p.to_xml(:only => [:id, :identifier, :name, :is_public, :status], :include => {:repository => {:only => [:id, :url]}}) + render :xml => p.to_xml(:only => [:id, :identifier, :name, :is_public, :status], :include => {:repository => {:only => [:id, :url, :is_external, :external_url]}}) end def create_project_repository
--- a/app/controllers/welcome_controller.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/controllers/welcome_controller.rb Tue Nov 13 12:10:15 2012 +0000 @@ -25,7 +25,6 @@ @site_project = Project.find_by_identifier "soundsoftware-site" @site_news = [] @site_news = News.latest_for @site_project if @site_project - @projects = Project.latest User.current # tests if user is logged in to generate the tips of the day list if User.current.logged?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/helpers/activities_helper.rb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,30 @@ + +module ActivitiesHelper + + def busy_projects(events, count) + # Transform events list into hash from project id to number of + # occurrences of project in list (there is surely a tidier way + # to do this, e.g. chunk() in Ruby 1.9 but not in 1.8) + phash = events.map { |e| e.project unless !e.respond_to?(:project) }.sort.group_by { |p| p.id } + phash = phash.merge(phash) { |k,v| v.length } + threshold = phash.values.sort.last(count).first + busy = phash.keys.select { |k| phash[k] >= threshold }.sample(count) + busy.map { |pid| Project.find(pid) } + end + + def busy_institutions(events, count) + authors = events.map do |e| + e.event_author unless !e.respond_to?(:event_author) + end.compact + institutions = authors.map do |a| + if a.respond_to?(:ssamr_user_detail) and !a.ssamr_user_detail.nil? + a.ssamr_user_detail.institution_name + end + end + insthash = institutions.compact.sort.group_by { |i| i } + insthash = insthash.merge(insthash) { |k,v| v.length } + threshold = insthash.values.sort.last(count).first + insthash.keys.select { |k| insthash[k] >= threshold }.sample(count) + end + +end
--- a/app/helpers/attachments_helper.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/helpers/attachments_helper.rb Tue Nov 13 12:10:15 2012 +0000 @@ -42,4 +42,11 @@ api.created_on attachment.created_on end end + + # Returns true if user agent appears (approximately) to be a search + # bot or crawler + def user_is_search_bot? + agent = request.env['HTTP_USER_AGENT'] + agent and agent =~ /(bot|slurp|crawler|spider)\b/i + end end
--- a/app/helpers/projects_helper.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/helpers/projects_helper.rb Tue Nov 13 12:10:15 2012 +0000 @@ -162,14 +162,12 @@ end - # Renders a tree of projects that the current user does not belong - # to, or of all projects if the current user is not logged in. The - # given collection may be a subset of the whole project tree - # (eg. some intermediate nodes are private and can not be seen). We - # are potentially interested in various things: the project name, - # description, manager(s), creation date, last activity date, - # general activity level, whether there is anything actually hosted - # here for the project, etc. + # Renders a tree of projects. The given collection may be a subset + # of the whole project tree (eg. some intermediate nodes are private + # and can not be seen). We are potentially interested in various + # things: the project name, description, manager(s), creation date, + # last activity date, general activity level, whether there is + # anything actually hosted here for the project, etc. def render_project_table(projects) s = ""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/views/activities/_busy.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,48 @@ +<% events = @events_by_day %> +<% if (events.nil?) + activity = Redmine::Activity::Fetcher.new(User.current) + events = activity.events(Date.today - 14, Date.today + 1) + end +%> + +<% if events.empty? %> + +<% else %> + + <ul> + + <% + for project in busy_projects(events, 5) + %> + + <li class="busy"> + <span class="title"> + <% if !project.root? %> + <% project.ancestors.each do |p| %> + <%= h(p) %> » + <% end %> + <% end %> + <%= link_to_project project %> + </span> + <% if !project.is_public? %> + <span class="private"><%= l(:field_is_private) %></span> + <% end %> + <span class='managers'> + <% + u = project.users_by_role + if ! u.empty? %> + (<%= + mgmt_roles = u.keys.select{ |r| r.allowed_to?(:edit_project) } + managers = mgmt_roles.map{ |r| u[r] }.flatten.sort.uniq + managers.map{ |m| m.name }.join(', ') + %>)<% + end + %> + </span> + + <%= render_project_short_description project %> + </li> + + <% end %> + </ul> +<% end %>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/views/activities/_busy_institution.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,26 @@ +<% events = @events_by_day %> +<% if (events.nil?) + activity = Redmine::Activity::Fetcher.new(User.current) + events = activity.events(Date.today - 14, Date.today + 1) + end +%> + +<% if events.empty? %> + +<% else %> + + <ul> + + <% + for institution in busy_institutions(events, 5) + %> + + <li class="busy"> + <span class="title"> + <%= link_to h(institution), { :controller => 'activities', :institution => institution } %> + </span> + </li> + + <% end %> + </ul> +<% end %>
--- a/app/views/activities/index.html.erb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/views/activities/index.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -1,4 +1,14 @@ -<h2><%= @author.nil? ? l(:label_activity) : l(:label_user_activity, link_to_user(@author)) %></h2> +<h2><%= + if @author.nil? + if @institution_name.blank? + l(:label_activity) + else + l(:label_institution_activity, h(@institution_name)) + end + else + l(:label_user_activity, link_to_user(@author)) + end + %></h2> <p class="subtitle"><%= l(:label_date_from_to, :start => format_date(@date_to - @days), :end => format_date(@date_to-1)) %></p> <div id="activity">
--- a/app/views/attachments/_form.html.erb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/views/attachments/_form.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -2,10 +2,10 @@ <span> <%= file_field_tag 'attachments[1][file]', :size => 30, :id => nil, :class => 'file', :onchange => "checkFileSize(this, #{Setting.attachment_max_size.to_i.kilobytes}, '#{escape_javascript(l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)))}');" -%> - <label class="inline"><%= l(:label_optional_description) %><%= text_field_tag 'attachments[1][description]', '', :size => 60, :id => nil, :class => 'description' %></label> - <%= link_to_function(image_tag('delete.png'), 'removeFileField(this)', :title => (l(:button_delete))) %> + <nobr><label class="inline"><%= l(:label_optional_description) %><%= text_field_tag 'attachments[1][description]', '', :size => 60, :id => nil, :class => 'description' %></label> + <%= link_to_function(image_tag('delete.png'), 'removeFileField(this)', :title => (l(:button_delete))) %></nobr> </span> </span> -<small><%= link_to l(:label_add_another_file), '#', :onclick => 'addFileField(); return false;' %> +<br><small><%= link_to l(:label_add_another_file), '#', :onclick => 'addFileField(); return false;' %> (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>) </small>
--- a/app/views/attachments/_links.html.erb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/views/attachments/_links.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -2,7 +2,7 @@ <% for attachment in attachments %> <p><%= link_to_attachment attachment, :class => 'icon icon-attachment' -%> <%= h(" - #{attachment.description}") unless attachment.description.blank? %> - <span class="size">(<%= number_to_human_size attachment.filesize %>)</span> + <span class="size_and_count"><%= number_to_human_size attachment.filesize %><%= ", " + l(:label_x_downloads, :count => attachment.downloads) unless attachment.downloads == 0 %></span> <% if options[:deletable] %> <%= link_to image_tag('delete.png'), attachment_path(attachment), :confirm => l(:text_are_you_sure),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/views/projects/_latest.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,19 @@ + <ul> + <% for project in Project.latest(User.current) %> + <li class="latest"> + <span class="title"> + <% if !project.root? %> + <% project.ancestors.each do |p| %> + <%= h(p) %> » + <% end %> + <% end %> + <%= link_to_project project %> + </span> + <% if !project.is_public? %> + <span class="private"><%= l(:field_is_private) %></span> + <% end %> + <span class="time"><%= format_time(project.created_on)%></span> + <%= render_project_short_description project %> + </li> + <% end %> + </ul>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/views/projects/explore.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,23 @@ + +<h2><%= l(:label_explore_projects) %></h2> + + <div class="tags box"> + <h3><%=l(:label_project_tags_all)%></h3> + <%= render :partial => 'projects/tagcloud' %> + </div> +<div class="splitcontentleft"> + <div class="institutions box"> + <h3><%=l(:label_institutions_busy)%></h3> + <%= render :partial => 'activities/busy_institution' %> + </div> + <div class="projects box"> + <h3><%=l(:label_project_latest)%></h3> + <%= render :partial => 'projects/latest' %> + </div> +</div> +<div class="splitcontentright"> + <div class="projects box"> + <h3><%=l(:label_projects_busy)%></h3> + <%= render :partial => 'activities/busy' %> + </div> +</div>
--- a/app/views/welcome/index.html.erb Tue Nov 13 11:22:53 2012 +0000 +++ b/app/views/welcome/index.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -14,40 +14,17 @@ <div class="splitcontentright"> <% if @site_news.any? %> - <div class="news box"> - <h3><%=l(:label_news_site_latest)%></h3> + <div class="news box"> + <h3><%=l(:label_news_site_latest)%></h3> <%= render :partial => 'news/news', :locals => { :project => @site_project }, :collection => @site_news %> - <%= link_to l(:label_news_more), { :controller => 'projects', :action => @site_project.identifier, :id => 'news' } %> </div> <% end %> - <% if @projects.any? %> <div class="projects box"> <h3><%=l(:label_project_latest)%></h3> - <ul> - <% for project in @projects %> - <% @project = project %> - <li class="latest"> - <span class="title"> - <% if !project.root? %> - <% project.ancestors.each do |p| %> - <%= h(p) %> » - <% end %> - <% end %> - <%= link_to_project project %> - </span> - <% if !project.is_public? %> - <span class="private"><%= l(:field_is_private) %></span> - <% end %> - <span class="time"><%= format_time(project.created_on)%></span> - <%= render_project_short_description project %> - </li> - <% end %> - <% @project = nil %> - </ul> - <%= link_to l(:label_projects_more), :controller => 'projects' %> + <%= render :partial => 'projects/latest' %> + <%= link_to l(:label_projects_more), :controller => 'projects' %> </div> - <% end %> <%= call_hook(:view_welcome_index_right, :projects => @projects) %> </div>
--- a/config/locales/en.yml Tue Nov 13 11:22:53 2012 +0000 +++ b/config/locales/en.yml Tue Nov 13 12:10:15 2012 +0000 @@ -487,9 +487,16 @@ zero: no projects one: 1 project other: "%{count} projects" + label_x_downloads: + zero: never downloaded + one: downloaded once + other: "downloaded %{count} times" label_project_all: All Projects label_project_latest: Latest projects label_projects_more: More projects + label_project_tags_all: Popular tags + label_projects_busy: Busy projects + label_institutions_busy: Active institutions label_managers: Managed by label_issue: Issue label_issue_new: New issue @@ -556,6 +563,7 @@ label_activity_my_recent_none: No recent activity label_overall_activity: Overall activity label_user_activity: "%{value}'s activity" + label_institution_activity: "Activity from %{value}" label_new: New label_logged_as: Logged in as label_environment: Environment @@ -685,7 +693,7 @@ label_repository: Repository label_is_external_repository: Track an external repository label_repository_plural: Repositories - label_browse: Browse + label_explore_projects: Explore projects label_modification: "%{count} change" label_modification_plural: "%{count} changes" label_branch: Branch
--- a/config/routes.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/config/routes.rb Tue Nov 13 12:10:15 2012 +0000 @@ -32,6 +32,8 @@ # TODO: wasteful since this is also nested under issues, projects, and projects/issues map.resources :time_entries, :controller => 'timelog' + map.connect 'explore', :controller => 'projects', :action => 'explore' + map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post} map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get} map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
--- a/extra/soundsoftware/extract-javadoc.sh Tue Nov 13 11:22:53 2012 +0000 +++ b/extra/soundsoftware/extract-javadoc.sh Tue Nov 13 12:10:15 2012 +0000 @@ -35,8 +35,8 @@ # package declarations find "$projectdir" -type f -name \*.java \ - -exec grep '^ *package [a-zA-Z][a-zA-Z0-9\._-]*; *$' \{\} /dev/null \; | - sed -e 's/\/[^\/]*: *package */:/' -e 's/; *$//' | + -exec egrep '^ *package +[a-zA-Z][a-zA-Z0-9\._-]*;.*$' \{\} /dev/null \; | + sed -e 's/\/[^\/]*: *package */:/' -e 's/;.*$//' | sort | uniq | ( current_prefix= current_packages=
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/soundsoftware/get-apache-log-stats.rb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,179 @@ + +# Read an Apache log file in SoundSoftware site format from stdin and +# produce some per-project stats. +# +# Invoke with e.g. +# +# cat /var/log/apache2/code-access.log | \ +# script/runner -e production extra/soundsoftware/get-apache-log-stats.rb + + +# Use the ApacheLogRegex parser, a neat thing +# See http://www.simonecarletti.com/blog/2009/02/apache-log-regex-a-lightweight-ruby-apache-log-parser/ +require 'apachelogregex' + +# This is the format defined in our httpd.conf +vhost_combined_format = '%v:%p %h %{X-Forwarded-For}i %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"' + +parser = ApacheLogRegex.new(vhost_combined_format) + +# project name -> count of hg clones +clones = Hash.new(0) + +# project name -> count of hg pulls +pulls = Hash.new(0) + +# project name -> count of hg pushes +pushes = Hash.new(0) + +# project name -> count of hg archive requests (i.e. Download as Zip) +zips = Hash.new(0) + +# project name -> count of hits to pages under /projects/projectname +hits = Hash.new(0) + +# project name -> Project object +@projects = Hash.new + +parseable = 0 +unparseable = 0 + +def is_public_project?(project) + if !project + false + elsif project =~ /^\d+$/ + # ignore numerical project ids, they are only used when editing projects + false + elsif @projects.key?(project) + @projects[project].is_public? + else + pobj = Project.find_by_identifier(project) + if pobj + @projects[project] = pobj + pobj.is_public? + else + print "Project not found: ", project, "\n" + false + end + end +end + +def print_stats(h) + h.keys.sort { |a,b| h[b] <=> h[a] }.each do |p| + if h[p] > 0 + print h[p], " ", @projects[p].name, " [", p, "]\n" + end + end +end + +STDIN.each do |line| + + record = parser.parse(line) + + # most annoyingly, the parser can't handle the comma-separated list + # in X-Forwarded-For where it has more than one element. If it has + # failed, remove any IP addresses or the word "unknown" with + # trailing commas and try again + if not record + filtered = line.gsub(/(unknown|([0-9]+\.){3}[0-9]+),\s*/, "") + record = parser.parse(filtered) + end + + # discard, but count, unparseable lines + if not record + print "Line not parseable: ", line, "\n" + unparseable += 1 + next + end + + # discard everything that isn't a 200 OK response + next if record["%>s"] != "200" + + # discard anything apparently requested by a crawler + next if record["%{User-Agent}i"] =~ /(bot|slurp|crawler|spider|Redmine)\b/i + + # pull out request e.g. GET / HTTP/1.0 + request = record["%r"] + + # split into method, path, protocol + if not request =~ /^[^\s]+ ([^\s]+) [^\s]+$/ + print "Line not parseable (bad method, path, protocol): ", line, "\n" + unparseable += 1 + next + end + + # get the path e.g. /projects/weevilmatic and split on / + path = $~[1] + components = path.split("/") + + # should have at least two elements unless path is "/"; first should + # be empty (begins with /) + if path != "/" and (components.size < 2 or components[0] != "") + print "Line not parseable (degenerate path): ", line, "\n" + unparseable += 1 + next + end + + if components[1] == "hg" + + # path is /hg/project?something or /hg/project/something + + project = components[2].split("?")[0] + if not is_public_project?(project) + next + end + + if components[2] =~ /&roots=00*$/ + clones[project] += 1 + elsif components[2] =~ /cmd=capabilities/ + pulls[project] += 1 + elsif components[2] =~ /cmd=unbundle/ + pushes[project] += 1 + elsif components[3] == "archive" + zips[project] += 1 + end + + elsif components[1] == "projects" + + # path is /projects/project or /projects/project/something + + project = components[2] + project = project.split("?")[0] if project + if not is_public_project?(project) + next + end + + hits[project] += 1 + + end + + parseable += 1 +end + +# Each clone is also a pull; deduct it from the pulls hash, because we +# want that to contain only non-clone pulls + +clones.keys.each do |project| + pulls[project] -= 1 +end + +print parseable, " parseable\n" +print unparseable, " unparseable\n" + + +print "\nMercurial clones:\n" +print_stats clones + +print "\nMercurial pulls (excluding clones):\n" +print_stats pulls + +print "\nMercurial pushes:\n" +print_stats pushes + +print "\nMercurial archive (zip file) downloads:\n" +print_stats zips + +print "\nProject page hits (excluding crawlers):\n" +print_stats hits + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/soundsoftware/get-statistics.rb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,147 @@ +# this script will get stats from the repo and print them to stdout + +# USAGE: + +# ./script/runner -e production extra/soundsoftware/get-statistics.rb +# + +d1 = Date.parse("20100701") # => 1 Jul 2010 +d2 = Date.today + +def delta_array (iarray) + # returns an array with the deltas + ## prepends a zero and drops the last element + deltas = [0] + iarray + deltas = deltas.first(deltas.size - 1) + + return iarray.zip(deltas).map { |x, y| x - y } + +end + +def months_between(d1, d2) + months = [] + start_date = Date.civil(d1.year, d1.month, 1) + end_date = Date.civil(d2.year, d2.month, 1) + + raise ArgumentError unless d1 <= d2 + + while (start_date < end_date) + months << start_date + start_date = start_date >>1 + end + + months << end_date +end + +def weeks_between(d1, d2) + weeks = [] + start_date = Date.civil(d1.year, d1.month, d1.day) + end_date = Date.civil(d2.year, d2.month, d2.day) + + raise ArgumentError unless d1 <= d2 + + while (start_date < end_date) + weeks << start_date + start_date = start_date + 2.week + end + + weeks << end_date +end + +def get_user_project_evol_stats() + # dates = months_between(d1, d2) + dates = months_between(d1, d2) + + # number of users + n_users = [] + n_projects = [] + qm_users = [] + + dates.each do |date| + users = User.find_by_sql ["SELECT * FROM users WHERE users.status = '1' AND users.created_on <= ?;", date] + projects = Project.find_by_sql ["SELECT * FROM projects WHERE projects.created_on <= ?;", date] + + qm_users_list = User.find_by_sql ["SELECT * FROM users,ssamr_user_details WHERE users.status = '1' AND ssamr_user_details.user_id = users.id AND (users.mail like '%qmul%' OR ssamr_user_details.institution_id = '99') AND users.created_on <= ?;", date ] + + qm_users << qm_users_list.count + n_users << users.count + n_projects << projects.count + + # private_projects = Project.find(:all, :conditions => {:created_on => d1..date, is_public => false}) + end + + user_deltas = delta_array(n_users) + proj_deltas = delta_array(n_projects) + qm_user_deltas = delta_array(qm_users) + + puts "Date Users D_Users QM_Users D_QM_users Projects D_Projects" + + dates.zip(n_users, user_deltas, qm_users, qm_user_deltas, n_projects, proj_deltas).each do |a, b, c, d, e, f, g| + puts "#{a} #{b} #{c} #{d} #{e} #{f} #{g}" + end + +end + + +def get_project_status() + date = "20121101" + + all_projects = Project.find(:all, :conditions => ["created_on < ?", date]) + # all_projects = Project.find(:all, :conditions => ["is_public = ? AND created_on < ?", true, date]) +# all_projects = Project.find(:all, :conditions => ["is_public = ? AND created_on < ?", false, date]) + + collab = [] + users_per_proj = [] + + # puts "Public Users Institutions" + + all_projects.each do |proj| + insts = [] + + proj.users.each do |u| + if u.institution == "" || u.institution == "No Institution Set" + if u.mail.include?("qmul.ac.uk") || u.mail.include?("andrewrobertson77") + insts << "Queen Mary, University of London" + else + insts << u.mail + end + else + insts << u.institution + end + end + + users_per_proj << proj.users.count + collab << insts.uniq.count + end + + + # freq = collab.inject(Hash.new(0)) { |h,v| h[v] += 1; h } + # freq = freq.sort_by {|key, value| value} + # puts freq.inspect.sort + + puts "Projects: #{all_projects.count} UpP: #{users_per_proj.sum / users_per_proj.size.to_f} Users1+: #{users_per_proj.count{|x| x> 1}} Users2+: #{users_per_proj.count{|x| x> 2}} Collab1+: #{collab.count{|x| x > 1}} Collab2+: #{collab.count{|x| x > 2}} IpP: #{collab.sum / collab.size.to_f}" +end + +def get_user_projects_ratios() + user_projects = User.find(:all, :conditions=> {:status => 1}) + pub_proj_user = user_projects.map{|u| u.projects.find(:all, :conditions=>{:is_public => true}).count} + + user_projects.zip(pub_proj_user).each do |u, pub| + puts "#{u.projects.count} #{pub}" + end + +end + +def get_inst_list() + users = User.find(:all, :conditions => {:status => 1}) + inst_list = users.map{|user| user.institution} + + freq = inst_list.inject(Hash.new(0)) { |h,v| h[v] += 1; h } + +end + + +# get_user_projects_ratios() +# get_user_project_evol_stats() + +get_project_status()
--- a/public/help/wiki_syntax.html Tue Nov 13 11:22:53 2012 +0000 +++ b/public/help/wiki_syntax.html Tue Nov 13 12:10:15 2012 +0000 @@ -44,11 +44,15 @@ <tr><th><img src="../images/jstoolbar/bt_h2.png" style="border: 1px solid #bbb;" alt="Heading 2" /></th><td>h2. Title 2</td><td><h2>Title 2</h2></td></tr> <tr><th><img src="../images/jstoolbar/bt_h3.png" style="border: 1px solid #bbb;" alt="Heading 3" /></th><td>h3. Title 3</td><td><h3>Title 3</h3></td></tr> +<tr><th colspan="3">Table of contents</th></tr> +<tr><th></th><td><pre>{{toc}}</pre></td><td>Left-aligned table of contents</td></tr> +<tr><th></th><td><pre>{{>toc}}</pre></td><td>Right-aligned table of contents</td></tr> + <tr><th colspan="3">Links</th></tr> <tr><th></th><td>http://foo.bar</td><td><a href="#">http://foo.bar</a></td></tr> <tr><th></th><td>"Foo":http://foo.bar</td><td><a href="#">Foo</a></td></tr> -<tr><th colspan="3">Redmine links</th></tr> +<tr><th colspan="3">Magic links</th></tr> <tr><th><img src="../images/jstoolbar/bt_link.png" style="border: 1px solid #bbb;" alt="Link to a Wiki page" /></th><td>[[Wiki page]]</td><td><a href="#">Wiki page</a></td></tr> <tr><th></th><td>Issue #12</td><td>Issue <a href="#">#12</a></td></tr> <tr><th></th><td>Revision r43</td><td>Revision <a href="#">r43</a></td></tr>
--- a/public/stylesheets/application.css Tue Nov 13 11:22:53 2012 +0000 +++ b/public/stylesheets/application.css Tue Nov 13 12:10:15 2012 +0000 @@ -5,7 +5,7 @@ h1 {margin:0; padding:0; font-size: 24px;} h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;} h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;} -h4, .wiki h3 {font-size: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;} +h4, .wiki h3 {font-size: 14px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;} /***** Layout *****/ #wrapper {background: white;} @@ -24,14 +24,14 @@ #account {float:right;} -#header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;} +#header {height:68px;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;} #header a {color:#f8f8f8;} #header h1 a.ancestor { font-size: 80%; } #project-search-jump {float:right; } -#main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;} +#main-menu {position: absolute; bottom: 0px; left:8px; margin-right: -500px;} #main-menu ul {margin: 0; padding: 0;} #main-menu li { float:left; @@ -240,6 +240,8 @@ div.members h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; } div.news h3 { background: url(../images/news.png) no-repeat 0% 50%; padding-left: 20px; } div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padding-left: 20px; } +div.tags h3 { background: url(../images/ticket_note.png) no-repeat 0% 50%; padding-left: 20px; } +div.institutions h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; } #watchers ul {margin: 0; padding: 0;} #watchers li {list-style-type:none;margin: 0px 2px 0px 0px; padding: 0px 0px 0px 0px;} @@ -500,13 +502,14 @@ .summary {font-style: italic;} #attachments_fields input[type=text] {margin-left: 8px; } -#attachments_fields span {display:block; white-space:nowrap;} +/*#attachments_fields span {display:block; white-space:nowrap;} */ #attachments_fields img {vertical-align: middle;} div.attachments { margin-top: 12px; } div.attachments p { margin:4px 0 2px 0; } div.attachments img { vertical-align: middle; } -div.attachments span.author { font-size: 0.9em; color: #888; } +div.attachments span.author { font-size: 0.9em; color: #888; font-style: italic; padding-left: 4px } +div.attachments span.size_and_count { font-size: 0.9em; color: #888; padding-left: 4px; } p.other-formats { text-align: right; font-size:0.9em; color: #666; } .other-formats span + span:before { content: "| "; }
--- a/public/themes/soundsoftware/stylesheets/application.css Tue Nov 13 11:22:53 2012 +0000 +++ b/public/themes/soundsoftware/stylesheets/application.css Tue Nov 13 12:10:15 2012 +0000 @@ -34,7 +34,7 @@ h2,h3,h4,.wiki h1 { color: #3e442c; - font-weight: bold; +/* font-weight: bold; */ } .wiki h2,.wiki h3,.wiki h4 { @@ -119,14 +119,14 @@ #quick-search { margin-right: 6px; margin-top: 1em; color: #000; } #project-jump-box { float: right; margin-right: 6px; margin-top: 5px; color: #000; } #project-ancestors-title { - margin-bottom: 0px; + margin-bottom: -6px; margin-left: 12px; margin-top: 6px; } -#main-menu { position: absolute; top: 100px; /* background-color: #be5700; */ left: 0; border-top: 0; width: 100%;/* height: 1.82em; */ padding: 0; margin: 0; border: 0; } +#main-menu { position: absolute; top: 100px; /* background-color: #be5700; */ left: 2px; border-top: 0; width: 100%;/* height: 1.82em; */ padding: 0; margin: 0; border: 0; } #main-menu li { margin: 0; padding: 0; } -#main-menu li a { background-color: #fdfbf5; color: #be5700; border-right: 1px solid #a9b680; font-size: 97%; padding: 0em 8px 0.2em 10px; font-weight: normal; } +#main-menu li a { background-color: #fdfbf5; color: #be5700; border-right: 1px solid #a9b680; font-size: 97%; padding: 0em 8px 0em 10px; font-weight: normal; } #main-menu li:last-child a { border-right: 0; } #main-menu li a:hover { background-color: #fdfbf5; color: #be5700; text-decoration: underline; } #main-menu li a.selected, #main-menu li a.selected:hover { background-color: #fdfbf5; color: #3e442c; } @@ -145,7 +145,9 @@ h2, h3, h4, .wiki h1, .wiki h2, .wiki h3 { border-bottom: 0px; } /*h2, .wiki h1 { letter-spacing:-1px; } */ -h4 { border-bottom: dotted 1px #c0c0c0; } +/* h4 { border-bottom: dotted 1px #c0c0c0; } */ + +.wiki p { margin-left: 3em; margin-right: 3em; } div.issue { background: #fdfaf0; border: 1px solid #a9b680; border-left: 4px solid #a9b680; }
--- a/public/themes/soundsoftware/stylesheets/fonts-generic.css Tue Nov 13 11:22:53 2012 +0000 +++ b/public/themes/soundsoftware/stylesheets/fonts-generic.css Tue Nov 13 12:10:15 2012 +0000 @@ -1,12 +1,24 @@ @import url(fonts.css); -h1, #project-ancestors-title { - font-family: GilliusADFNo2, 'Gill Sans', Tahoma, sans-serif; - font-weight: normal; +h1, #project-ancestors-title, #top-menu a { + font-family: Insider, 'Gill Sans', Tahoma, sans-serif; + font-weight: bold; } -body,p,h2,h3,h4,li,table,.wiki h1,.embedded h1 { - font-family: DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; +#top-menu div, #top-menu li { + font-size: 12px; +} + +body,p,li,table { + font-family: Insider, DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; + font-size: 14px; + line-height: 1.34; + font-weight: normal; +} + +h2,h3,h4,.wiki h1,.embedded h1 { + font-family: Insider, DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; + font-weight: bold; line-height: 1.34; }
--- a/public/themes/soundsoftware/stylesheets/fonts-mac.css Tue Nov 13 11:22:53 2012 +0000 +++ b/public/themes/soundsoftware/stylesheets/fonts-mac.css Tue Nov 13 12:10:15 2012 +0000 @@ -1,11 +1,24 @@ @import url(fonts.css); -h1, #project-ancestors-title { - font-family: GilliusADFNo2, 'Gill Sans', Tahoma, sans-serif; - font-weight: normal; +h1, #project-ancestors-title, #top-menu a { + font-family: Insider, "Lucida Grande", sans-serif; + font-weight: bold; } -body,p,h2,h3,h4,li,table,.wiki h1,.embedded h1 { - font-family: 'Lucida Grande', 'Lucida Sans Unicode', DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; +#top-menu div, #top-menu li { + font-size: 12px; +} + +body,p,li,table { + font-family: Insider, "Lucida Grande", sans-serif; + font-size: 14px; + line-height: 1.34; + font-weight: normal; +} + +h2,h3,h4,.wiki h1,.embedded h1 { + font-family: Insider, "Lucida Grande", sans-serif; + font-weight: bold; line-height: 1.34; } +
--- a/public/themes/soundsoftware/stylesheets/fonts-ms.css Tue Nov 13 11:22:53 2012 +0000 +++ b/public/themes/soundsoftware/stylesheets/fonts-ms.css Tue Nov 13 12:10:15 2012 +0000 @@ -1,11 +1,26 @@ @import url(fonts.css); -h1, #project-ancestors-title { - font-family: GilliusADFNo2, 'Gill Sans', Tahoma, sans-serif; +/* IE likes us to separate out normal & bold into different fonts + rather than use the selectors on the same font */ + +h1, #project-ancestors-title, #top-menu a { + font-family: Insider-Medium, Tahoma, sans-serif; font-weight: normal; } -body,p,h2,h3,h4,li,table,.wiki h1,.embedded h1 { - font-family: Calibri, DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; +#top-menu div, #top-menu li { + font-size: 12px; +} + +body,p,li,table { + font-family: Insider-Regular, tahoma, verdana, sans-serif; + font-size: 14px; + line-height: 1.34; + font-weight: normal; +} + +h2,h3,h4,.wiki h1,.embedded h1 { + font-family: Insider-Medium, tahoma, verdana, sans-serif; + font-weight: normal; line-height: 1.34; }
--- a/public/themes/soundsoftware/stylesheets/fonts.css Tue Nov 13 11:22:53 2012 +0000 +++ b/public/themes/soundsoftware/stylesheets/fonts.css Tue Nov 13 12:10:15 2012 +0000 @@ -1,51 +1,32 @@ - -/* Font pack generated by FontSquirrel */ @font-face { - font-family: 'GilliusADFNo2'; - src: url('fonts/gilliusadfno2-bolditalic-webfont.eot'); - src: local('☺'), url('fonts/gilliusadfno2-bolditalic-webfont.woff') format('woff'), url('fonts/gilliusadfno2-bolditalic-webfont.ttf') format('truetype'), url('fonts/GilliusADFNo2-BoldItalic.otf') format('opentype'), url('fonts/gilliusadfno2-bolditalic-webfont.svg#webfontLmhvPwzc') format('svg'); - font-weight: bold; - font-style: italic; + font-family: 'Insider'; + font-weight: bold; + font-style: normal; + src: url('fonts/24BC0E_0_0.eot'); + src: url('fonts/24BC0E_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC0E_0_0.woff') format('woff'), url('fonts/24BC0E_0_0.ttf') format('truetype'); +} + +@font-face { + font-family: 'Insider'; + font-weight: normal; + font-style: normal; + src: url('fonts/24BC35_0_0.eot'); + src: url('fonts/24BC35_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC35_0_0.woff') format('woff'), url('fonts/24BC35_0_0.ttf') format('truetype'); } @font-face { - font-family: 'GilliusADFNo2'; - src: url('fonts/gilliusadfno2-italic-webfont.eot'); - src: local('☺'), url('fonts/gilliusadfno2-italic-webfont.woff') format('woff'), url('fonts/gilliusadfno2-italic-webfont.ttf') format('truetype'), url('fonts/GilliusADFNo2-Italic.otf') format('opentype'), url('fonts/gilliusadfno2-italic-webfont.svg#webfonteHBtzgS0') format('svg'); - font-weight: normal; - font-style: italic; + font-family: 'Insider-Medium'; + font-weight: normal; + font-style: normal; + src: url('fonts/24BC0E_0_0.eot'); + src: url('fonts/24BC0E_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC0E_0_0.woff') format('woff'), url('fonts/24BC0E_0_0.ttf') format('truetype'); } - + @font-face { - font-family: 'GilliusADFNo2'; - src: url('fonts/gilliusadfno2-bold-webfont.eot'); - src: local('☺'), url('fonts/gilliusadfno2-bold-webfont.woff') format('woff'), url('fonts/gilliusadfno2-bold-webfont.ttf') format('truetype'), url('fonts/GilliusADFNo2-Bold.otf') format('opentype'), url('fonts/gilliusadfno2-bold-webfont.svg#webfontntXmQMqk') format('svg'); - font-weight: bold; - font-style: normal; + font-family: 'Insider-Regular'; + font-weight: normal; + font-style: normal; + src: url('fonts/24BC35_0_0.eot'); + src: url('fonts/24BC35_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC35_0_0.woff') format('woff'), url('fonts/24BC35_0_0.ttf') format('truetype'); } - -@font-face { - font-family: 'GilliusADFNo2'; - src: url('fonts/gilliusadfno2-regular-webfont.eot'); - src: local('☺'), url('fonts/gilliusadfno2-regular-webfont.woff') format('woff'), url('fonts/gilliusadfno2-regular-webfont.ttf') format('truetype'), url('fonts/GilliusADFNo2-Regular.otf') format('opentype'), url('fonts/gilliusadfno2-regular-webfont.svg#webfontvJUiAdi3') format('svg'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'DroidSans'; - src: url('fonts/DroidSans-webfont.eot'); - src: local('☺'), url('fonts/DroidSans-webfont.woff') format('woff'), url('fonts/DroidSans-webfont.ttf') format('truetype'), url('fonts/DroidSans-webfont.svg#webfontKYIQSBQk') format('svg'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'DroidSans'; - src: url('fonts/DroidSans-Bold-webfont.eot'); - src: local('☺'), url('fonts/DroidSans-Bold-webfont.woff') format('woff'), url('fonts/DroidSans-Bold-webfont.ttf') format('truetype'), url('fonts/DroidSans-Bold-webfont.svg#webfontljpTCDjw') format('svg'); - font-weight: bold; - font-style: normal; -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/redmine_tags/app/controllers/tags_controller.rb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,15 @@ +class TagsController < ApplicationController + + def index + respond_to do |format| + format.html { + render :template => 'tags/index.html.erb', :layout => !request.xhr? + } + format.api { + } + format.atom { + } + end + end + +end
--- a/vendor/plugins/redmine_tags/app/helpers/tags_helper.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/vendor/plugins/redmine_tags/app/helpers/tags_helper.rb Tue Nov 13 12:10:15 2012 +0000 @@ -40,8 +40,11 @@ content_tag('span', content, :class => 'tag-label') end - def render_project_tag_link(tag) + def render_project_tag_link(tag, options = {}) content = link_to tag.name, :controller => :projects, :action => :index, :project => { :tag_list => tag.name } + if options[:show_count] + content << content_tag('span', "(#{tag.count})", :class => 'tag-count') + end content_tag('span', content, :class => 'tag-label') end @@ -73,7 +76,7 @@ end tag_cloud tags, (1..8).to_a do |tag, weight| - content << " " + content_tag(item_el, render_tag_link(tag, options), :class => "tag-nube-#{weight}") + " " + content << " " + content_tag(item_el, render_project_tag_link(tag, options), :class => "tag-nube-#{weight}") + " " end content_tag(list_el, content, :class => 'tags')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/redmine_tags/app/views/projects/_tagcloud.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,9 @@ +<% content_for :header_tags do %> + <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %> +<% end %> + +<div id="tags"> +<%= render_tags_list(Project.available_tags, :style => :cloud) %> +</div> + +
--- a/vendor/plugins/redmine_tags/app/views/projects/index.html.erb Tue Nov 13 11:22:53 2012 +0000 +++ b/vendor/plugins/redmine_tags/app/views/projects/index.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -11,7 +11,6 @@ </div> - <div style="clear:both;"></div> <% if User.current.logged? %> <%= render :partial => 'my_projects' %>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/redmine_tags/app/views/tags/index.html.erb Tue Nov 13 12:10:15 2012 +0000 @@ -0,0 +1,13 @@ +<% content_for :header_tags do %> + <%= auto_discovery_link_tag(:atom, {:action => 'index', :format => 'atom', :key => User.current.rss_key}) %> + <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %> +<% end %> + + +<div style="clear:both;"></div> +<h2> + <%= l("label_project_tags_all") %> +</h2> + +<%= render_tags_list(Project.available_tags, :style => :cloud) %> +
--- a/vendor/plugins/redmine_tags/assets/stylesheets/redmine_tags.css Tue Nov 13 11:22:53 2012 +0000 +++ b/vendor/plugins/redmine_tags/assets/stylesheets/redmine_tags.css Tue Nov 13 12:10:15 2012 +0000 @@ -22,6 +22,7 @@ ul.tags li { margin: .25em 0px; } div.tags { text-align: center; } +div.tags h3 { text-align: left; } div.tags .tag-label { margin: .25em; } div.tags .tag-nube-1 { font-size: .8em; } div.tags .tag-nube-2 { font-size: .9em; }
--- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb Tue Nov 13 12:10:15 2012 +0000 @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- require_dependency 'projects_controller' module RedmineTags @@ -127,13 +128,10 @@ end unless @tag_list.empty? - @tagged_projects_ids = Project.visible.tagged_with(@tag_list).collect{ |project| Project.find(project.id) } + @tagged_projects_ids = Project.visible.tagged_with(@tag_list).collect{ |project| Project.find(project.id).root } @projects = @projects & @tagged_projects_ids + @projects = @projects.uniq end - - @projects = @projects.collect{ |project| project.root } - @projects = @projects.uniq - end end end
--- a/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_helper_patch.rb Tue Nov 13 11:22:53 2012 +0000 +++ b/vendor/plugins/redmine_tags/lib/redmine_tags/patches/projects_helper_patch.rb Tue Nov 13 12:10:15 2012 +0000 @@ -4,6 +4,7 @@ def self.included(base) # :nodoc: base.send(:include, InstanceMethods) + base.send(:include, TagsHelper) base.class_eval do unloadable end