| @@ -81,7 +81,6 @@ db/bak/ | |||
| docker/ | |||
| educoder.sql | |||
| redis_data/ | |||
| Dockerfile | |||
| dump.rdb | |||
| .tags* | |||
| ceshi_user.xlsx | |||
| @@ -0,0 +1,47 @@ | |||
| FROM ubuntu:20.04 | |||
| RUN apt-get update | |||
| # basics | |||
| RUN apt-get install -y libssl-dev curl libmysqlclient-dev imagemagick nodejs mysql-server redis-server tzdata | |||
| RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime | |||
| # confirm openssl version | |||
| RUN openssl version | |||
| RUN which openssl | |||
| # install RVM, Ruby, and Bundler | |||
| RUN \curl -L https://get.rvm.io | bash -s stable | |||
| RUN /bin/bash -l -c "rvm requirements" | |||
| # replace ruby mirror url, accelerate install ruby | |||
| RUN sed -i 's/rvm_remote_server_url2/#rvm_remote_server_url2/g' /usr/local/rvm/config/db | |||
| RUN sed -i 's/cache.ruby-lang.org/cache.ruby-china.com/g' /usr/local/rvm/config/db | |||
| # install ruby | |||
| RUN /bin/bash -l -c "rvm install 2.4.5" | |||
| # confirm ruby version | |||
| RUN /bin/bash -l -c "rvm list" | |||
| RUN /bin/bash -l -c "ruby -v" | |||
| #RUN apt-get install -y nodejs | |||
| WORKDIR /home/app/gitlink | |||
| ADD ./ /home/app/gitlink | |||
| RUN cd /home/app/gitlink | |||
| RUN /bin/bash -l -c 'gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/' | |||
| RUN /bin/bash -l -c 'gem update --system' | |||
| RUN /bin/bash -l -c 'gem install bundler -v 2.3.26' | |||
| RUN /bin/bash -l -c 'gem install rake' | |||
| RUN /bin/bash -l -c 'gem install puma -v 5.6.5' | |||
| RUN rm -rf Gemfile.lock | |||
| RUN cp config/configuration.yml.example config/configuration.yml | |||
| RUN cp config/database.yml.example config/database.yml | |||
| RUN touch config/redis.yml | |||
| RUN touch config/elasticsearch.yml | |||
| RUN /bin/bash -l -c 'bundle install' | |||
| #EXPOSE 4000 | |||
| #RUN /bin/bash -l -c 'RAILS_ENV=production puma' | |||
| @@ -70,9 +70,6 @@ group :development do | |||
| gem 'web-console', '>= 3.3.0' | |||
| gem 'listen', '>= 3.0.5', '< 3.2' | |||
| gem 'spring' | |||
| gem 'pry-rails' | |||
| gem 'pry-remote' | |||
| gem 'byebug', platforms: [:mri,:mingw,:x64_mingw] | |||
| gem 'spring-watcher-listen', '~> 2.0.0' | |||
| gem "annotate", "~> 2.6.0" | |||
| end | |||
| @@ -102,9 +99,10 @@ gem 'font-awesome-sass', '4.7.0' | |||
| gem 'rails-i18n', '~> 5.1' | |||
| # job | |||
| gem 'sidekiq' | |||
| gem 'sidekiq',"5.2.8" | |||
| gem 'sinatra' | |||
| gem "sidekiq-cron", "~> 1.1" | |||
| gem "sidekiq-cron", "1.2.0" | |||
| gem 'sidekiq-failures' | |||
| # batch insert | |||
| gem 'bulk_insert' | |||
| @@ -84,7 +84,6 @@ GEM | |||
| builder (3.2.4) | |||
| bulk_insert (1.7.0) | |||
| activerecord (>= 3.2.0) | |||
| byebug (11.1.3) | |||
| capybara (3.15.1) | |||
| addressable | |||
| mini_mime (>= 0.1.3) | |||
| @@ -100,7 +99,6 @@ GEM | |||
| archive-zip (~> 0.10) | |||
| nokogiri (~> 1.8) | |||
| chunky_png (1.3.11) | |||
| coderay (1.1.3) | |||
| concurrent-ruby (1.1.6) | |||
| connection_pool (2.2.2) | |||
| crass (1.0.6) | |||
| @@ -137,7 +135,7 @@ GEM | |||
| fugit (1.4.1) | |||
| et-orbi (~> 1.1, >= 1.1.8) | |||
| raabro (~> 1.4) | |||
| gitea-client (1.4.1) | |||
| gitea-client (1.4.2) | |||
| rest-client (~> 2.1.0) | |||
| globalid (0.4.2) | |||
| activesupport (>= 4.2.0) | |||
| @@ -246,14 +244,6 @@ GEM | |||
| popper_js (1.16.0) | |||
| powerpack (0.1.2) | |||
| prettier (0.18.2) | |||
| pry (0.12.2) | |||
| coderay (~> 1.1.0) | |||
| method_source (~> 0.9.0) | |||
| pry-rails (0.3.9) | |||
| pry (>= 0.10.4) | |||
| pry-remote (0.1.8) | |||
| pry (~> 0.9) | |||
| slop (~> 3.0) | |||
| public_suffix (4.0.3) | |||
| puma (3.12.2) | |||
| raabro (1.4.0) | |||
| @@ -407,6 +397,8 @@ GEM | |||
| sidekiq-cron (1.2.0) | |||
| fugit (~> 1.1) | |||
| sidekiq (>= 4.2.1) | |||
| sidekiq-failures (1.0.4) | |||
| sidekiq (>= 4.0.0) | |||
| simple_form (5.0.2) | |||
| actionpack (>= 5.0) | |||
| activemodel (>= 5.0) | |||
| @@ -418,7 +410,6 @@ GEM | |||
| rack (~> 2.0) | |||
| rack-protection (= 2.0.8.1) | |||
| tilt (~> 2.0) | |||
| slop (3.6.0) | |||
| solargraph (0.38.6) | |||
| backport (~> 1.1) | |||
| benchmark | |||
| @@ -490,7 +481,6 @@ DEPENDENCIES | |||
| bootsnap (>= 1.1.0) | |||
| bootstrap (~> 4.3.1) | |||
| bulk_insert | |||
| byebug | |||
| capybara (>= 2.15, < 4.0) | |||
| chartkick | |||
| chinese_pinyin | |||
| @@ -502,7 +492,7 @@ DEPENDENCIES | |||
| enumerize | |||
| faraday (~> 0.15.4) | |||
| font-awesome-sass (= 4.7.0) | |||
| gitea-client (~> 1.4.1) | |||
| gitea-client (~> 1.4.2) | |||
| grape-entity (~> 0.7.1) | |||
| groupdate (~> 4.1.0) | |||
| harmonious_dictionary (~> 0.0.1) | |||
| @@ -524,8 +514,6 @@ DEPENDENCIES | |||
| parallel (~> 1.19, >= 1.19.1) | |||
| pdfkit | |||
| prettier | |||
| pry-rails | |||
| pry-remote | |||
| puma (~> 3.11) | |||
| rack-cors | |||
| rack-mini-profiler | |||
| @@ -546,8 +534,9 @@ DEPENDENCIES | |||
| sass-rails (~> 5.0) | |||
| searchkick | |||
| selenium-webdriver | |||
| sidekiq | |||
| sidekiq-cron (~> 1.1) | |||
| sidekiq (= 5.2.8) | |||
| sidekiq-cron (= 1.2.0) | |||
| sidekiq-failures | |||
| simple_form | |||
| simple_xlsx_reader | |||
| sinatra | |||
| @@ -1,17 +1,27 @@ | |||
| Trustie (确实)是一个以大众化协同开发、开放式资源共享、持续性可信评估为核心机理,面向高校创新实践的在线协作平台。 | |||
| # GitLink - CCF开源创新服务平台 | |||
| ## 特性 | |||
| GitLink(确实开源)是中国计算机学会(CCF)官方指定的开源创新服务平台,旨在以“为开源创新服务”为使命,以“成为开源创新的汇聚地”为愿景,秉承“创新、开放、协作、共享”的价值观,致力于为大规模开源开放协同创新助力赋能,打造创新成果孵化和新工科人才培养的开源创新生态! | |||
| - 软件创作与生产深度融合的软件开发环境体系结构 软件自由创作和工程生产的高效衔接,适于软件开发中群体智慧的有效汇聚。 | |||
| <center> | |||
| <img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/master/docs/figs/gitlink.png?raw=true" width=80% /></center> | |||
| ## 特色功能 | |||
| - 构件化协同开发环境的可扩展运行框架多样化工具的集成和联动,形成了强动态扩展能力的平台框架。 | |||
| - **分布式协作开发**:基于Git打造分布式代码托管环境,提供免费公、私有代码仓库,支持在线文件编辑、代码分支管理、协作贡献统计、代码仓库复刻(Fork)、贡献合并请求(PR)、群智贡献审阅等功能,让您的项目在这里健康、快速的成长! | |||
| - “互联网即资源库”的全新软件复用模式 成长式软件资源管理系统,实现了分散资源的知识融合、资源的可持续增长和有效复用。 | |||
| - **一站式过程管理**:提供疑修(Issue)、里程碑、通知提醒、标签归档等多样化任务管理工具,支持各类开发任务的发布、指派与跟踪,同时提供在线Wiki文档、组织多粒度管理等功能,为您搭建一站式的项目过程管理环境,让您的团队协作更高效、过程更透明! | |||
| ## 部署 | |||
| - **高效流水线运维**:融合DevOps思想,提供轻量级的工作流引擎(Engine),打通编码、测试、构建、部署等开发运维环节;支持自定义配置、代码静态扫描、构建自动触发、容器镜像托管等功能,同时支持接入第三方运维工具,让您的代码更加快速、可靠地形成高质量的产品! | |||
| - **多层次代码分析**:提供软件软代码和芯片RTL代码的溯源分析、文件级和组件级许可证识别及风险分析、输入性开源漏洞检测和加固建议,支持分析结果的多层次可视化展示,帮助您实施有效开源治理,厘清代码引用链,发现并消除漏洞感染链,为安全合规的开源引用保驾护航! | |||
| ### Depends Versions | |||
| - **多维度用户画像**:实时采集和分析平台中的各类开源资源数据,搭建多维度用户画像评估系统,提供开发活动统计、贡献度日历、用户能力建模、角色与专业定位分析等功能,让您在个人主页展示开发动态与创新能力! | |||
| ## 部署流程 | |||
| ### 依赖库 | |||
| * Ruby 2.4.5 | |||
| @@ -23,22 +33,53 @@ Trustie (确实)是一个以大众化协同开发、开放式资源共享、 | |||
| * imagemagick | |||
| ### Steps | |||
| #### 1. 克隆稳定版本 | |||
| ### 步骤 | |||
| (1)安装 Rails 必要的一些三方库: | |||
| - Mac OS X | |||
| ```bash | |||
| brew install imagemagick ghostscript libxml2 libxslt libiconv | |||
| ``` | |||
| git clone -b standalone https://git.trustie.net/jasder/forgeplus.git | |||
| - Ubuntu | |||
| ```bash | |||
| sudo apt-get update | |||
| sudo apt-get install -y openssl libssl-dev imagemagick git ruby-dev nodejs libmariadb-dev libmysqlclient-dev shared-mime-info libpq-dev libxml2-dev libxslt-dev | |||
| sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y tzdata | |||
| sudo ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime | |||
| ``` | |||
| #### 2. 安装依赖包 | |||
| (2)安装 Ruby, Rails 运行环境:[如何快速正确的安装 Ruby, Rails 运行环境](https://ruby-china.org/wiki/install_ruby_guide) | |||
| ```bash | |||
| #检验环境是否正确 | |||
| ruby -v | |||
| #ruby 2.4.x ... | |||
| gem -v | |||
| #3.x.x | |||
| bundle -v | |||
| #Bundler version 2.x.x | |||
| rails -v | |||
| #Rails 5.2.x | |||
| ``` | |||
| (3)克隆稳定版本 | |||
| ```bash | |||
| cd forgeplus && bundle install | |||
| git clone -b master https://gitlink.org.cn/Gitlink/forgeplus.git | |||
| ``` | |||
| #### 3. 配置初始化文件 | |||
| 进入项目根目录执行一下命令: | |||
| (4)安装依赖包 | |||
| ```bash | |||
| #进入目录 | |||
| cd forgeplus | |||
| #删除Gemfile.lock | |||
| rm -rf Gemfile.lock | |||
| #安装依赖包 | |||
| bundle install | |||
| ``` | |||
| (5)配置初始化文件:进入项目根目录执行以下命令 | |||
| ```bash | |||
| cp config/configuration.yml.example config/configuration.yml | |||
| cp config/database.yml.example config/database.yml | |||
| @@ -46,12 +87,8 @@ touch config/redis.yml | |||
| touch config/elasticsearch.yml | |||
| ``` | |||
| #### 4. 配置数据库 | |||
| 数据库配置信息请查看/config/database.yml文件, | |||
| 项目默认采用mysql数据库, 如需更改,请自行修改配置信息, | |||
| 默认配置如下: | |||
| ```bash | |||
| (6)配置数据库:数据库配置信息请查看/config/database.yml文件,项目默认采用mysql数据库, 如需更改,请自行修改配置信息,默认配置如下 | |||
| ```yaml | |||
| default: &default | |||
| adapter: mysql2 | |||
| host: 127.0.0.1 | |||
| @@ -60,16 +97,15 @@ default: &default | |||
| password: 123456 | |||
| ``` | |||
| #### 5. 配置gitea服务(可选) | |||
| **如需要部署自己的gitea平台,请参考gitea官方平台:https://docs.gitea.io/zh-cn/install-from-binary/** | |||
| (7)配置gitea服务(可选):如需要部署自己的gitea平台,请参考[gitea官方平台文档](https://docs.gitea.io/zh-cn/install-from-binary/)。因目前gitea平台api受限,暂时推荐从forge平台获取[gitea部署文件](https://www.gitlink.org.cn/Gitlink/gitea-binary)进行部署 | |||
| **因目前gitea平台api受限,暂时推荐从forge平台获取gitea部署文件进行部署:https://forgeplus.trustie.net/projects/Trustie/gitea-binary** | |||
| - 配置gitea服务步骤: | |||
| -- 部署gitea服务,并注册root账户 | |||
| -- 修改forge平台的 config/configuration.yml中的gitea服务指向地址,如: | |||
| **配置gitea服务步骤** | |||
| 1. 部署gitea服务,并注册root账户 | |||
| 2. 修改forge平台的 config/configuration.yml中的gitea服务指向地址,如: | |||
| ```ruby | |||
| ```yaml | |||
| gitea: | |||
| access_key_id: 'root' | |||
| access_key_secret: 'password' | |||
| @@ -78,102 +114,100 @@ gitea: | |||
| hat_base_url: '/api/hat' | |||
| ``` | |||
| #### 6. 安装redis环境 | |||
| **请自行搜索各平台如何安装部署redis环境** | |||
| #### 7. 安装imagemagick插件 | |||
| - Mac OS X | |||
| ```bash | |||
| brew install imagemagick ghostscript | |||
| ``` | |||
| (8)配置/config/database.yml文件(安装redis环境:请自行搜索各平台如何安装部署redis环境) | |||
| ```yaml | |||
| default: &default | |||
| url: redis://localhost:6379 | |||
| db: 1 | |||
| - Linux | |||
| ```bash | |||
| sudo apt-get install -y imagemagick | |||
| production: | |||
| <<: *default | |||
| url: redis://localhost:6379 | |||
| ``` | |||
| #### 8. 创建数据库 | |||
| **开发环境为development, 生成环境为production** | |||
| (9)创建数据库:开发环境为development, 生成环境为production | |||
| ```bash | |||
| rails db:create RAILS_ENV=development | |||
| ``` | |||
| #### 9. 导入数据表结构 | |||
| (10)导入数据表结构 | |||
| ```bash | |||
| bundle exec rake sync_table_structure:import_csv | |||
| ``` | |||
| #### 10. 执行migrate迁移文件 | |||
| **开发环境为development, 生成环境为production** | |||
| (11)执行migrate迁移文件:开发环境为development, 生成环境为production | |||
| ```bash | |||
| rails db:migrate RAILS_ENV=development | |||
| ``` | |||
| #### 11. clone前端代码 | |||
| **将前端代码克隆到public/react目录下,目录结构应该是: public/react/build** | |||
| (12)clone前端代码:将前端代码克隆到public/react目录下,目录结构应该是: public/react/build | |||
| ```bash | |||
| git clone -b standalone https://git.trustie.net/jasder/build.git | |||
| git clone -b master https://gitlink.org.cn/Gitlink/build.git | |||
| ``` | |||
| #### 12. 启动redis(此处已mac系统为例) | |||
| (13)启动redis(此处以macOS系统为例) | |||
| ```bash | |||
| redis-server& | |||
| ``` | |||
| #### 13. 启动sidekiq | |||
| **开发环境为development, 生成环境为production** | |||
| (14)启动sidekiq:开发环境为development, 生成环境为production | |||
| ```bash | |||
| bundle exec sidekiq -C config/sidekiq.yml -e production -d | |||
| ``` | |||
| #### 14. 启动rails服务 | |||
| (15)启动rails服务 | |||
| ```bash | |||
| rails s | |||
| ``` | |||
| #### 15. 浏览器访问 | |||
| 在浏览器中输入如下地址访问: | |||
| (16)浏览器访问:在浏览器中输入如下地址访问 | |||
| ```bash | |||
| http://localhost:3000/ | |||
| ``` | |||
| #### 16. 其他说明 | |||
| 通过页面注册都第一个用户为平台管理员用户 | |||
| ## 页面展示 | |||
| - 代码库 | |||
| (17)其他说明:通过页面注册以第一个用户为平台管理员用户 | |||
|  | |||
| ## 页面展示 | |||
| - 项目列表 | |||
| <center> | |||
| <img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/master/docs/figs/project_list.png?raw=true" width=50% /> | |||
| </center> | |||
| - 代码仓库 | |||
| <center> | |||
| <img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/master/docs/figs/repo.png?raw=true" width=50% /> | |||
| </center> | |||
| - 任务管理 | |||
|  | |||
| - 任务查看 | |||
|  | |||
| - 任务指派 | |||
| <center> | |||
| <img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/master/docs/figs/issues.png?raw=true" width=50% /> | |||
| </center> | |||
| - 合并请求 | |||
| <center> | |||
| <img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/master/docs/figs/PR.png?raw=true" width=50% /> | |||
| </center> | |||
| - 引擎配置 | |||
| <center> | |||
| <img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/master/docs/figs/engine.png?raw=true" width=50% /> | |||
| </center> | |||
|  | |||
| - 里程碑 | |||
|  | |||
| ### API | |||
| - [API文档](https://forgeplus.trustie.net/docs/api) | |||
| - [API](showdoc.com.cn) | |||
| 账号:forgeplus@admin.com 密码:forge123 | |||
| ## 贡献代码 | |||
| 1. Fork 项目 | |||
| 2. 创建本地分支(git checkout -b my-new-feature) | |||
| 3. 提交更改 (git commit -am 'Add some feature') | |||
| 4. 推送到分支 (git push origin my-new-feature) | |||
| 5. 向源项目的 **develop** 分支发起 Pull Request | |||
| 我们期待您向GitLink提交贡献!在您贡献时,请遵循流程:[【Wiki文档-GitLink协作开发流程】](https://www.gitlink.org.cn/Gitlink/forgeplus/wiki "【Wiki文档-GitLink协作开发流程】") | |||
| #### 指导文档 | |||
| - [API文档](https://www.gitlink.org.cn/docs/api) | |||
| - [Git常用命令](https://git-scm.com/) | |||
| ## License | |||
| ## 许可证协议 | |||
| @@ -214,6 +214,7 @@ class AccountsController < ApplicationController | |||
| def change_password | |||
| @user = User.find_by(login: params[:login]) | |||
| return render_error("此用户禁止修改密码!") if @user.id.to_i === 104691 | |||
| return render_error("未找到相关用户!") if @user.blank? | |||
| return render_error("旧密码不正确") unless @user.check_password?(params[:old_password]) | |||
| @@ -49,24 +49,29 @@ class Oauth::EducoderController < Oauth::BaseController | |||
| open_user = OpenUsers::Educoder.find_by(uid: result['login']) | |||
| if open_user.present? && open_user.user.present? | |||
| successful_authentication(open_user.user) | |||
| redirect_to root_path(new_user: false) | |||
| return | |||
| else | |||
| if current_user.blank? || !current_user.logged? | |||
| new_user = true | |||
| login = User.generate_login('E') | |||
| reg_result = autologin_register(login,"#{login}@forge.com", "Ec#{login}2021#", 'educoder', true) | |||
| if reg_result[:message].blank? | |||
| open_user = OpenUsers::Educoder.create!(user_id: reg_result[:user][:id], uid: result['login'], extra: result) | |||
| autosync_register_trustie(login, "Ec#{login}2021#", "#{login}@forge.com") | |||
| successful_authentication(open_user.user) | |||
| else | |||
| render_error(reg_result[:message]) | |||
| end | |||
| session[:unionid] = result['login'] | |||
| # login = User.generate_login('E') | |||
| # reg_result = autologin_register(login,"#{login}@forge.com", "Ec#{login}2021#", 'educoder', true) | |||
| # if reg_result[:message].blank? | |||
| # open_user = OpenUsers::Educoder.create!(user_id: reg_result[:user][:id], uid: result['login'], extra: result) | |||
| # autosync_register_trustie(login, "Ec#{login}2021#", "#{login}@forge.com") | |||
| # successful_authentication(open_user.user) | |||
| # else | |||
| # render_error(reg_result[:message]) | |||
| # end | |||
| else | |||
| OpenUsers::Educoder.create!(user: current_user, uid: result['login'], extra: result) | |||
| end | |||
| end | |||
| Rails.logger.info("[OAuth2] session[:unionid] -> #{session[:unionid]}") | |||
| redirect_to "/bindlogin/educoder" | |||
| redirect_to root_path(new_user: new_user) | |||
| # redirect_to root_path(new_user: new_user) | |||
| rescue Exception => ex | |||
| render_error(ex.message) | |||
| end | |||
| @@ -22,6 +22,12 @@ class RepositoriesController < ApplicationController | |||
| def detail | |||
| @user = current_user | |||
| @result = Repositories::DetailService.call(@owner, @repository, @user) | |||
| cache_total_forks = $redis_cache.get("ProjectSpecialForks:#{@project.id}") | |||
| if cache_total_forks.present? | |||
| @project_forked_count = cache_total_forks.to_i | |||
| else | |||
| @project_forked_count = @project.forked_count.to_i | |||
| end | |||
| @project_fork_id = @project.try(:forked_from_project_id) | |||
| if @project_fork_id.present? | |||
| @fork_project = Project.find_by(id: @project_fork_id) | |||
| @@ -338,7 +344,12 @@ class RepositoriesController < ApplicationController | |||
| def get_latest_commit | |||
| latest_commit = @project.educoder? ? nil : project_commits | |||
| @latest_commit = latest_commit.present? ? latest_commit[:body][0] : nil | |||
| @commits_count = latest_commit.present? ? latest_commit[:total_count] : 0 | |||
| cache_total_commits = $redis_cache.get("ProjectSpecialCommit:#{@project.id}") | |||
| if cache_total_commits.present? | |||
| @commits_count = cache_total_commits.to_i | |||
| else | |||
| @commits_count = latest_commit.present? ? latest_commit[:total_count] : 0 | |||
| end | |||
| end | |||
| def content_params | |||
| @@ -0,0 +1,6 @@ | |||
| # for oauth2 application | |||
| class ImportRepo < ApplicationRecord | |||
| self.table_name = "open_shixuns" | |||
| end | |||
| @@ -161,6 +161,14 @@ class Organization < Owner | |||
| organization_users.count | |||
| end | |||
| def teams_count | |||
| teams.count | |||
| end | |||
| def organization_users_count | |||
| organization_users.count | |||
| end | |||
| def real_name | |||
| name = lastname + firstname | |||
| name = name.blank? ? (nickname.blank? ? login : nickname) : name | |||
| @@ -448,6 +448,36 @@ class Project < ApplicationRecord | |||
| $redis_cache.hdel("issue_cache_delete_count", self.id) | |||
| end | |||
| def self.mindspore_contributors | |||
| cache_result = $redis_cache.get("ProjectMindsporeContributors") | |||
| if cache_result.nil? | |||
| contributors = [] | |||
| file = File.open('public/mindspore_authors', 'r') | |||
| file.each_line do |l| | |||
| itemArray = l.gsub("\r\n", "").split("|:|") | |||
| email = itemArray[0] | |||
| username = itemArray[1] | |||
| commits = itemArray[2].to_i | |||
| user = User.find_by(login: username, mail: email) | |||
| user = User.find_by(login: username) if user.nil? | |||
| user = User.find_by(mail: email) if user.nil? | |||
| # next if user.nil? | |||
| search_contributor = contributors.select{|con| con["id"]==user.id}[0] | |||
| if search_contributor.present? | |||
| search_contributor["contributions"] += commits | |||
| else | |||
| contributors << {contributions: commits, name: username, login: username, email: email, id: user&.id}.stringify_keys | |||
| end | |||
| end | |||
| file.close | |||
| $redis_cache.set("ProjectMindsporeContributors", contributors.to_json) | |||
| return contributors | |||
| else | |||
| return JSON.parse(cache_result) | |||
| end | |||
| end | |||
| def to_builder | |||
| Jbuilder.new do |project| | |||
| project.id self.id | |||
| @@ -877,11 +877,11 @@ class User < Owner | |||
| end | |||
| end | |||
| def self.develop_score(commit_count, user_id=User.current.id) | |||
| def self.develop_score(commit_count, code_line, user_id=User.current.id) | |||
| user_date_statistic_key = "v2-user-statistic:#{user_id}" | |||
| follow_count = $redis_cache.hget(user_date_statistic_key, "follow-count") || 0 | |||
| pullrequest_count = $redis_cache.hget(user_date_statistic_key, "pullrequest-count").to_i || 0 | |||
| issues_count = $redis_cache.hget(user_date_statistic_key, "issue-count") || 0 | |||
| issues_count = $redis_cache.hget(user_date_statistic_key, "issue-count").to_i || 0 | |||
| project_count = $redis_cache.hget(user_date_statistic_key, "project-count") || 0 | |||
| fork_count = $redis_cache.hget(user_date_statistic_key, "fork-count") || 0 | |||
| project_watchers_count = $redis_cache.hget(user_date_statistic_key, "project-watcher-count") || 0 | |||
| @@ -889,6 +889,7 @@ class User < Owner | |||
| project_language = $redis_cache.hget(user_date_statistic_key, "project-language") | |||
| project_languages_count = project_language.nil? || project_language == "{}" ? 0 : JSON.parse(project_language).length | |||
| pullrequest_count += commit_count | |||
| issues_count += code_line | |||
| influence = (60.0 + follow_count.to_i / (follow_count.to_i + 20.0) * 40.0).to_i | |||
| contribution = (60.0 + pullrequest_count.to_i / (pullrequest_count.to_i + 20.0) * 40.0).to_i | |||
| activity = (60.0 + issues_count.to_i / (issues_count.to_i + 80.0) * 40.0).to_i | |||
| @@ -6,9 +6,13 @@ class Admins::DeleteOrganizationService < Gitea::ClientService | |||
| end | |||
| def call | |||
| response = delete(url, params) | |||
| render_status(response) | |||
| Gitea::Organization::DeleteService.call(token,name) | |||
| end | |||
| private | |||
| def token | |||
| { | |||
| @@ -18,6 +18,7 @@ class Api::V1::Users::UpdateEmailService < ApplicationService | |||
| end | |||
| def call | |||
| raise Error, "此用户禁止修改邮箱." if @user.id.to_i === 104691 | |||
| raise Error, errors.full_messages.join(",") unless valid? | |||
| raise Error, "密码不正确." unless @user.check_password?(@password) | |||
| exist_owner = Owner.find_by(mail: @mail) | |||
| @@ -15,6 +15,7 @@ class Api::V1::Users::UpdatePhoneService < ApplicationService | |||
| end | |||
| def call | |||
| raise Error, "此用户禁止修改手机号." if @user.id.to_i === 104691 | |||
| raise Error, errors.full_messages.join(",") unless valid? | |||
| raise Error, "密码不正确." unless @user.check_password?(@password) | |||
| exist_owner = Owner.find_by(phone: @phone) | |||
| @@ -82,7 +82,7 @@ class Gitea::ClientService < ApplicationService | |||
| req.headers['Content-Type'] = 'application/json' | |||
| req.response :logger # 显示日志 | |||
| req.adapter Faraday.default_adapter | |||
| req.options.timeout = 1200 # open/read timeout in seconds | |||
| req.options.timeout =7200 # open/read timeout in seconds | |||
| req.options.open_timeout = 10 # connection open timeout in seconds | |||
| if token.blank? | |||
| req.basic_auth(username, secret) | |||
| @@ -20,7 +20,7 @@ class Gitea::Repository::Commits::FileListService < Gitea::ClientService | |||
| private | |||
| def params | |||
| {sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "" } | |||
| {sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "", stat: args[:page].to_i != 1 } | |||
| end | |||
| def url | |||
| @@ -1 +1 @@ | |||
| $('.organizations-list-container').html("<%= j( render partial: 'admins/organizations/shared/org_list', locals: { organizations: @orgs } ) %>"); | |||
| $('.organizations-list-container').html("<%= j( render partial: 'admins/organizations/shared/org_list', locals: { organizations: @orgs } ) %>"); | |||
| @@ -1,10 +1,10 @@ | |||
| json.total_count @result_object[:total_data].to_i | |||
| result_arr = @result_object[:data] | |||
| result_arr = result_arr.sort_by!{|c| User.develop_score(c["commits"])}.reverse | |||
| result_arr = result_arr.sort_by!{|c| User.develop_score(c["commits"],c["additions"]+c["deletions"])}.reverse | |||
| json.contributors result_arr.each do |contributor| | |||
| user = $redis_cache.hgetall("v2-owner-common:#{contributor["name"]}-#{contributor["email"]}") | |||
| if user.blank? | |||
| json.score User.develop_score(contributor["commits"]) | |||
| json.score User.develop_score(contributor["commits"],contributor["additions"]+contributor["deletions"] )-300 | |||
| json.contributions contributor["commits"] | |||
| json.additions contributor["additions"] | |||
| json.deletions contributor["deletions"] | |||
| @@ -15,7 +15,7 @@ json.contributors result_arr.each do |contributor| | |||
| json.name contributor["name"] | |||
| json.image_url User::Avatar.get_letter_avatar_url(contributor["name"]) | |||
| else | |||
| json.score User.develop_score(contributor["commits"]) | |||
| json.score User.develop_score(contributor["commits"],contributor["additions"]+contributor["deletions"])-300 | |||
| json.contributions contributor["commits"] | |||
| json.additions contributor["additions"] | |||
| json.deletions contributor["deletions"] | |||
| @@ -1,7 +1,6 @@ | |||
| total_count = @total_count | |||
| json.list @contributors.each do |contributor| | |||
| json.partial! 'contributor', locals: { contributor: contributor, project: @project } | |||
| end | |||
| json.total_count total_count | |||
| json.total_count @total_count | |||
| @@ -11,7 +11,7 @@ json.issues_count @project.issues.issue_issue.size - @project.issues.issue_issue | |||
| json.pull_requests_count @project.pull_requests.opening.size | |||
| json.project_identifier render_identifier(@project) | |||
| json.praises_count @project.praises_count.to_i | |||
| json.forked_count @project.forked_count.to_i | |||
| json.forked_count @project_forked_count.to_i | |||
| json.watchers_count @project.watchers_count.to_i | |||
| json.versions_count @project.versions.opening.size #里程碑数量 | |||
| json.version_releases_count @project.releases_size(@user.try(:id), "all") | |||
| @@ -1,49 +1,50 @@ | |||
| require_relative 'boot' | |||
| require 'rails/all' | |||
| # Require the gems listed in Gemfile, including any gems | |||
| # you've limited to :test, :development, or :production. | |||
| Bundler.require(*Rails.groups) | |||
| module Gitlink | |||
| class Application < Rails::Application | |||
| # Initialize configuration defaults for originally generated Rails version. | |||
| config.load_defaults 5.2 | |||
| # Settings in config/environments/* take precedence over those specified here. | |||
| # Application configuration can go into files in config/initializers | |||
| # -- all .rb files in that directory are automatically loaded after loading | |||
| # the framework and any gems in your application. | |||
| # | |||
| # | |||
| # config.gitlink = config_for(:configuration) | |||
| # Custom directories with classes and modules you want to be autoloadable. | |||
| config.active_record.default_timezone = :utc | |||
| config.time_zone = 'Beijing' | |||
| # I18n | |||
| config.i18n.default_locale = 'zh-CN' | |||
| config.i18n.load_path += Dir[Rails.root.join('config/locales', '**', '*.yml').to_s] | |||
| # job | |||
| config.active_job.queue_adapter = :sidekiq | |||
| # disable actioncable development nend true | |||
| # config.action_cable.disable_request_forgery_protection = true | |||
| config.middleware.use OmniAuth::Builder do | |||
| provider :cas, url: 'https://urp.tfswufe.edu.cn/cas' | |||
| end | |||
| config.middleware.insert_before 0, Rack::Cors do | |||
| allow do | |||
| origins '*' | |||
| # location of your api | |||
| resource '/*', :headers => :any, :methods => [:get, :post, :delete, :options, :put, :patch] | |||
| end | |||
| end | |||
| end | |||
| end | |||
| require_relative 'boot' | |||
| require 'rails/all' | |||
| # Require the gems listed in Gemfile, including any gems | |||
| # you've limited to :test, :development, or :production. | |||
| Bundler.require(*Rails.groups) | |||
| module Gitlink | |||
| class Application < Rails::Application | |||
| # Initialize configuration defaults for originally generated Rails version. | |||
| config.load_defaults 5.2 | |||
| # Settings in config/environments/* take precedence over those specified here. | |||
| # Application configuration can go into files in config/initializers | |||
| # -- all .rb files in that directory are automatically loaded after loading | |||
| # the framework and any gems in your application. | |||
| # | |||
| # | |||
| # config.gitlink = config_for(:configuration) | |||
| # Custom directories with classes and modules you want to be autoloadable. | |||
| config.active_record.default_timezone = :utc | |||
| config.time_zone = 'Beijing' | |||
| # I18n | |||
| config.i18n.default_locale = 'zh-CN' | |||
| config.i18n.load_path += Dir[Rails.root.join('config/locales', '**', '*.yml').to_s] | |||
| # job | |||
| config.active_job.queue_adapter = :sidekiq | |||
| # disable actioncable development nend true | |||
| # config.action_cable.disable_request_forgery_protection = true | |||
| config.middleware.use OmniAuth::Builder do | |||
| provider :cas, url: 'https://urp.tfswufe.edu.cn/cas' | |||
| end | |||
| config.middleware.insert_before 0, Rack::Cors do | |||
| allow do | |||
| # origins '*' | |||
| origins /http:\/\/localhost(:\d+)?\z/, /^(http|https):\/\/(.*(gitlink.org.cn))$/, /^(http|https):\/\/(.*(trustie.net))$/ | |||
| # location of your api | |||
| resource '/*', :headers => :any, :methods => [:get, :post, :delete, :options, :put, :patch], credentials: true | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -1,12 +1,12 @@ | |||
| :concurrency: <%= ENV["sidekiq_threads"] || 20 %> | |||
| :pidfile: tmp/pids/sidekiq.pid | |||
| :logfile: log/sidekiq.log | |||
| :timeout: 30 | |||
| :queues: | |||
| - [default, 3] | |||
| - [searchkick, 10] | |||
| - [notify, 100] | |||
| - [mailers, 101] | |||
| - [cache, 10] | |||
| - [message, 20] | |||
| - [webhook, 20] | |||
| :concurrency: <%= ENV["sidekiq_threads"] || 40 %> | |||
| :pidfile: tmp/pids/sidekiq.pid | |||
| :logfile: log/sidekiq.log | |||
| :timeout: 30 | |||
| :queues: | |||
| - [default, 3] | |||
| - [searchkick, 10] | |||
| - [notify, 100] | |||
| - [mailers, 101] | |||
| - [cache, 10] | |||
| - [message, 20] | |||
| - [webhook, 20] | |||
| @@ -13,7 +13,7 @@ services: | |||
| MYSQL_DATABASE: educoder | |||
| redis: | |||
| image: redis:3.2 | |||
| image: redis:6.2.5 | |||
| container_name: redis | |||
| restart: always | |||
| ports: | |||
| @@ -22,14 +22,17 @@ services: | |||
| - ./redis_data:/data | |||
| web: | |||
| image: guange/educoder:latest | |||
| command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 4000 -b '0.0.0.0'" | |||
| image: gitlink-ubuntu18.04:latest | |||
| build: | |||
| context: ../ | |||
| dockerfile: Dockerfile | |||
| # command: bash -c "rm -f tmp/pids/server.pid && rails s -p 4000 -b '0.0.0.0'" | |||
| stdin_open: true | |||
| tty: true | |||
| volumes: | |||
| - .:/app | |||
| - .:/home/app/gitlink | |||
| ports: | |||
| - "4000:4000" | |||
| depends_on: | |||
| - mysql | |||
| - redis | |||
| - redis | |||
| @@ -0,0 +1,61 @@ | |||
| namespace :batch_add_commits do | |||
| desc "batch_add_issues" | |||
| task gitee: :environment do | |||
| project_id = ENV['project_id'] | |||
| puts "project_id=================#{project_id}" | |||
| next if project_id.blank? | |||
| project = Project.find project_id | |||
| count = 0 | |||
| if ENV['count'].present? | |||
| count = ENV['count'].to_i | |||
| end | |||
| total_count = 65669 | |||
| puts "total_count==========#{total_count}" | |||
| if total_count > 100 | |||
| total_page = (total_count / 100) + 1 | |||
| total_page.times do |i| | |||
| add_commits_to_project(project, i + 1 + count) | |||
| end | |||
| else | |||
| add_commits_to_project(project, 1) | |||
| end | |||
| end | |||
| def add_commits_to_project(project,page) | |||
| puts "page==========#{page}" | |||
| # curl -X GET --header 'Content-Type: application/json;charset=UTF-8' 'https://gitee.com/api/v5/repos/mindspore/mindspore/issues?access_token=5ccebd935915fb6cfcae634b161047a2&state=open&sort=created&direction=desc&page=1&per_page=10' | |||
| api_url = "https://gitee.com/api/v5/repos/mindspore/mindspore/commits?access_token=96a637aa055f15056e77e3cf11a67525&state=all&sort=created&direction=desc&page=#{page}&per_page=100" | |||
| uri = URI.parse(api_url) | |||
| response = Net::HTTP.get_response(uri) | |||
| puts "gitee api response.code ===== #{response.code}" | |||
| lists = JSON.parse(response.body) | |||
| puts "lists.size =====#{lists.size}" | |||
| data = "" | |||
| lists.each do |commit| | |||
| # puts "commit==========#{commit}" | |||
| commiter = commit['commit']['author'] | |||
| commit_sha = commit['sha'] | |||
| next if CommitLog.find_by(commit_id: commit_sha).present? | |||
| ref = "master" | |||
| commit_message = commit['commit']['message'].to_s.size > 200 ? "Message Data too long" : commit['commit']['message'].to_s.gsub("/n","").gsub("\"","") | |||
| user = User.find_by(mail: commiter['email']) | |||
| user_id = user&.id || project.user_id | |||
| commit_date = Time.parse(commit['commit']['author']['date']) | |||
| commit_date_str = commit_date.strftime("%Y-%m-%d %H:%M:%S") | |||
| data += "(#{user_id},#{project.id},#{project.repository&.id},'#{project.identifier}','#{project.owner.name}/#{project.identifier}','#{commit_sha}','#{ref}',\"#{commit_message}\",'#{commit_date_str}','#{commit_date_str}')," | |||
| end | |||
| data = data[0,data.length-1] | |||
| if data.present? | |||
| sql_connection = ActiveRecord::Base.connection | |||
| sql_connection.begin_db_transaction | |||
| sql = "INSERT INTO commit_logs (`user_id`, `project_id`, `repository_id`, `name`, `full_name`, `commit_id`, `ref`, `message`, `created_at`, `updated_at`) VALUES #{data}" | |||
| sql_connection.execute(sql) | |||
| sql_connection.commit_db_transaction | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,129 @@ | |||
| namespace :batch_add_contributors do | |||
| desc "batch_add_contributors" | |||
| task done: :environment do | |||
| puts "project_id=================#{ENV['project_id']}" | |||
| return if ENV['project_id'].blank? | |||
| projects = Project.where(id: ENV['project_id']) | |||
| if ENV['project_name'] == "jittor" | |||
| projects = Project.where(id: [1403791,1404837,1404087,1403854,1403883,1403886,1403894,1404027,1404187,1404234,1404291,1404522,1404238,1404632,1404003,1403975,1404285,1404525,1404017,1403843,1403867,1403874,1403878,1403881,1403887,1403898,1403916,1403937,1403954,1403961,1403963,1403965,1403967,1403972,1403974,1403976,1403977,1403978,1404007,1404012,1404020,1404023,1404032,1404042,1404058,1404065,1404069,1404071,1404072,1404078,1404094,1404104,1404143,1404146,1404167,1404173,1404174,1404175,1404210,1404216,1404226,1404229,1404240,1404242,1404244,1404247,1404248,1404250,1404261,1404272,1404276,1404290,1404327,1404333,1404360,1404450,1404460,1404466,1404485,1404489,1404491,1404496,1404500,1404502,1404507,1404519,1404521,1404523,1404527,1404530,1404532,1404537,1404540,1404541,1404710,1404328,1404211,1404241,1404107,1403880,1404271,1404268,1404745,1404101,1404051,1404047,1404052,1406706,1403852,1403931,1404165,1404607,1404498,1404014,1404045,1404043,1403739,1403801,1403815,1403821,1403822,1403824,1403836,1403853,1403872,1403904,1403906,1403909,1403915,1403919,1403921,1403943,1403945,1403971,1403981,1404022,1404026,1404030,1404037,1404091,1404133,1404163,1404184,1404209,1404212,1404220,1404246,1404249,1404263,1404265,1404287,1404299,1404311,1404497,1404506,1404546,1404558,1404577,1404582,1404591,1404592,1404594,1404595,1404596,1404597,1404605,1404606,1404609,1404639,1404641,1404659,1404671,1404681,1404702,1404748,1404754,1404759,1404785,1404879,1404955,1405169,1405205,1405265,1405374,1407368,1407446,1407671,1407673,1404956,1404079,1404080,1403889,1404665,1404801,1404224,1404931,1404544,1403844,1404036,1404067,1404587,1404696,1404732,1404598,1405192,1404162,1403920,1403903,1405248,1403792,1403817,1403962,1403841,1404188,1404185,1405273,1404575,1404550,1404834,1405420,1404141,1405256,1404633,1405277,1404590,1404796,1405230,1405189,1404584,1405257,1405198,1403925,1404692,1405253,1403809,1405118,1404756,1404962,1404864,1405190,1405258,1405274,1404642,1404924,1404453,1404926,1404649,1404237,1404233,1404600,1404758,1405259,1404157,1404444,1404451,1404920,1404919,1404927,1403830,1404658,1405145,1405185,1403842,1403807,1403895,1404549,1404593,1404750,1404798,1404551,1404701,1405156,1404579,1404655,1405267,1404957,1404556,1404651,1404456,1405430,1403955,1404063,1404214,1403942,1404040,1404804,1428732,1403939,1404208,1404245,1405278,1404139,1403850,1404192,1404293,1404297,1404370,1404492,1404693,1404757,1404329,1404512,1404228,1404314,1404016,1404652,1405275,1404832,1404561,1404653,1404704,1404892,1404589,1404953,1405269,1404881,1404221,1404230,1404793,1403953,1404933,1404035,1404599,1403924,1404119,1404526,1404581,1404705,1404709,1404073,1403808,1403892,1404574,1403849,1404251,1404792,1403865,1404640,1404783,1404048,1406707,1404708,1404053,1404295,1404050,1404049,1404044,1404274,1404046,1404636]) | |||
| end | |||
| if ENV['project_name'] == "mindspore" | |||
| projects = Project.where(identifier: ['MindSpore-first-experience', ' MindSpore-install', 'MindSpore-Application-practice', 'MindSpore-Model-Development', 'MindSpore-Data-preprocessing', 'Mindspore-Data-storage-use', 'MindSpore-Data-storage-kunpeng', 'MindSpore-LeNet-jzx3', 'MindSpore-competition'] ) | |||
| end | |||
| projects.each_with_index do |project, index| | |||
| # result = Gitea::Repository::Contributors::GetService.call(project.owner, project.repository.identifier, {page: params[:page], limit: params[:limit]}) | |||
| result = Gitea::Repository::Commits::ListService.call(project.owner.login,project.identifier,sha: "", page: 1, limit: 200, token: project.owner.gitea_token) | |||
| # @total_count = result[:total_count] | |||
| # @contributors = result.is_a?(Hash) ? result[:body] : [] | |||
| next if result.blank? || result[:total_count].blank? | |||
| total_count = result[:total_count] | |||
| # next if total_count > 2000 | |||
| puts "#{index} total_count==========#{total_count}" | |||
| if total_count > 200 | |||
| total_page = (total_count / 200) + 1 | |||
| total_page.times do |i| | |||
| add_data_by_page(project, i + 1) | |||
| end | |||
| else | |||
| # add_commit_to_index(project, 1) | |||
| data = "" | |||
| result[:body].each do |commit| | |||
| # puts "commit==========#{commit}" | |||
| commiter = commit['commit']['author'] | |||
| # "luoyuan <luoyuan7@huawei.com>" | |||
| commit_author = "#{commiter['name']} <#{commiter['email']}>" | |||
| commit_sha = commit['sha'] | |||
| # next if CommitLog.find_by(commit_id: commit_sha).present? | |||
| ref = "master" | |||
| commit_message = commit['commit']['message'].to_s.size > 200 ? "Message Data too long" : commit['commit']['message'].to_s.gsub("/n","").gsub("\"","") | |||
| # user = User.find_by(mail: commiter['email']) | |||
| # user_id = user&.id || project.user_id | |||
| commit_date = Time.parse(commit['commit']['author']['date']) | |||
| commit_date_str = commit_date.strftime("%Y-%m-%d %H:%M:%S") | |||
| site = Site.find_by_sql("select id, created_at from commit_contributors where name='#{commiter['email']}'") | |||
| sql ="" | |||
| if site.present? | |||
| puts "commit_date====#{commit_date},created_at======#{site.first&.created_at}" | |||
| if commit_date.to_i < site.first&.created_at.to_i | |||
| sql = "update commit_contributors set created_at ='#{commit_date_str}' where name='#{commiter['email']}'" | |||
| end | |||
| else | |||
| sql = "INSERT INTO commit_contributors (`created_at`, `count`, `name`) VALUES ('#{commit_date_str}',1,'#{commiter['email']}')" | |||
| end | |||
| puts "sql====#{sql}" | |||
| next if sql.blank? | |||
| sql_connection = ActiveRecord::Base.connection | |||
| sql_connection.begin_db_transaction | |||
| sql_connection.execute(sql) | |||
| sql_connection.commit_db_transaction | |||
| # data += "(#{user_id},#{project.id},#{project.repository&.id},'#{project.identifier}','#{project.owner.name}/#{project.identifier}','#{commit_sha}','#{ref}',\"#{commit_message}\",'#{commit_date_str}','#{commit_date_str}')," | |||
| end | |||
| # data = data[0,data.length-1] | |||
| # if data.present? | |||
| # sql_connection = ActiveRecord::Base.connection | |||
| # sql_connection.begin_db_transaction | |||
| # sql = "INSERT INTO commit_contributors (`created_at`, `count`, `name`) VALUES #{data}" | |||
| # sql_connection.execute(sql) | |||
| # sql_connection.commit_db_transaction | |||
| # end | |||
| end | |||
| end | |||
| # Time.now | |||
| # Wed Mar 15 14:12:09 2023 +0800 | |||
| # Time.now.strftime("%a %b %d %H:%M:%S %Y") | |||
| # Time.now.strftime("%a %b %d %H:%M:%S %Y +0800") | |||
| Time.parse("2023-03-15 14:12:09").strftime("%a %b %d %H:%M:%S %Y +0800") | |||
| end | |||
| def add_data_by_page(project, page) | |||
| # Gitea::Repository::Commits::ListSliceService.call(project.owner.login,project.identifier,sha: "", page: 1, limit: 1000, token: "a9244ecac647dd33fee3b480c5898baab1d3fe7d") | |||
| result = Gitea::Repository::Commits::ListService.call(project.owner.login,project.identifier,sha: "", page: page, limit: 200, token: project.owner.gitea_token) | |||
| data = "" | |||
| result[:body].each do |commit| | |||
| # puts "commit==========#{commit}" | |||
| commiter = commit['commit']['author'] | |||
| # "luoyuan <luoyuan7@huawei.com>" | |||
| commit_author = "#{commiter['name']} <#{commiter['email']}>" | |||
| commit_sha = commit['sha'] | |||
| # next if CommitLog.find_by(commit_id: commit_sha).present? | |||
| ref = "master" | |||
| commit_message = commit['commit']['message'].to_s.size > 200 ? "Message Data too long" : commit['commit']['message'].to_s.gsub("/n","").gsub("\"","") | |||
| # user = User.find_by(mail: commiter['email']) | |||
| # user_id = user&.id || project.user_id | |||
| commit_date = Time.parse(commit['commit']['author']['date']) | |||
| commit_date_str = commit_date.strftime("%Y-%m-%d %H:%M:%S") | |||
| site = Site.find_by_sql("select id, created_at from commit_contributors where name='#{commiter['email']}'") | |||
| sql= "" | |||
| if site.present? | |||
| puts "commit_date====#{commit_date},created_at======#{site.first&.created_at}" | |||
| if commit_date.to_i < site.first&.created_at.to_i | |||
| sql = "update commit_contributors set created_at ='#{commit_date_str}' where name='#{commiter['email']}'" | |||
| end | |||
| else | |||
| sql = "INSERT INTO commit_contributors (`created_at`, `count`, `name`) VALUES ('#{commit_date_str}',1,'#{commiter['email']}')" | |||
| end | |||
| puts "sql====#{sql}" | |||
| next if sql.blank? | |||
| sql_connection = ActiveRecord::Base.connection | |||
| sql_connection.begin_db_transaction | |||
| sql_connection.execute(sql) | |||
| sql_connection.commit_db_transaction | |||
| # data += "(#{user_id},#{project.id},#{project.repository&.id},'#{project.identifier}','#{project.owner.name}/#{project.identifier}','#{commit_sha}','#{ref}',\"#{commit_message}\",'#{commit_date_str}','#{commit_date_str}')," | |||
| end | |||
| # data = data[0,data.length-1] | |||
| # if data.present? | |||
| # sql_connection = ActiveRecord::Base.connection | |||
| # sql_connection.begin_db_transaction | |||
| # sql = "INSERT INTO commit_logs (`user_id`, `project_id`, `repository_id`, `name`, `full_name`, `commit_id`, `ref`, `message`, `created_at`, `updated_at`) VALUES #{data}" | |||
| # sql_connection.execute(sql) | |||
| # sql_connection.commit_db_transaction | |||
| # end | |||
| end | |||
| end | |||
| @@ -0,0 +1,130 @@ | |||
| namespace :batch_add_issues do | |||
| desc "batch_add_issues" | |||
| task gitee: :environment do | |||
| project_id = ENV['project_id'] | |||
| puts "project_id=================#{project_id}" | |||
| next if project_id.blank? | |||
| project = Project.find project_id | |||
| count = 0 | |||
| if ENV['count'].present? | |||
| count = ENV['count'].to_i | |||
| end | |||
| total_count = 2066 + 499 + 16675 + 451 | |||
| puts "total_count==========#{total_count}" | |||
| if total_count > 100 | |||
| total_page = (total_count / 100) + 1 | |||
| total_page.times do |i| | |||
| add_issues_to_project(project, i + 1 + count) | |||
| end | |||
| else | |||
| add_issues_to_project(project, 1) | |||
| end | |||
| end | |||
| def add_issues_to_project(project,page) | |||
| # curl -X GET --header 'Content-Type: application/json;charset=UTF-8' 'https://gitee.com/api/v5/repos/mindspore/mindspore/issues?access_token=5ccebd935915fb6cfcae634b161047a2&state=open&sort=created&direction=desc&page=1&per_page=10' | |||
| api_url = "https://gitee.com/api/v5/repos/mindspore/mindspore/issues?access_token=96a637aa055f15056e77e3cf11a67525&state=all&sort=created&direction=desc&page=#{page}&per_page=100" | |||
| uri = URI.parse(api_url) | |||
| response = Net::HTTP.get_response(uri) | |||
| puts "gitee api response.code ===== #{response.code}" | |||
| lists = JSON.parse(response.body) | |||
| puts "lists.size =====#{lists.size}" | |||
| # "1" => "新增", | |||
| # "2" => "正在解决", | |||
| # "3" => "已解决", | |||
| # "5" => "关闭", | |||
| # "6" => "拒绝" | |||
| # Issue的状态: open(开启的), progressing(进行中), closed(关闭的), rejected(拒绝的)。 默认: open | |||
| lists.each do |issue| | |||
| created_issue = Issue.find_by(project_id: project.id, subject: issue['title']) | |||
| unless created_issue.present? | |||
| priority = [1, 2, 3, 4].include?(issue['priority'].to_i) ? issue['priority'].to_i : 2 | |||
| issue_status = ["", "open", "progressing", "", "", "closed", "rejected"].index(issue['state']) | |||
| issue_status = 1 if issue_status.nil? | |||
| user_login = issue['user']['login'] | |||
| user_login = user_login[0..20] if user_login.size > 29 | |||
| issue_created_at = issue['created_at'] | |||
| issue_updated_at = issue['updated_at'] | |||
| user = User.find_by(login: user_login) | |||
| unless user.present? | |||
| username = user_login | |||
| email = "#{username}@gitlink.org.cn" | |||
| phone = "" | |||
| password = "a12345678" | |||
| user = User.new(nickname: user_login, login: username, mail: email, password: password, type: 'User', phone: phone) | |||
| interactor = Gitea::RegisterInteractor.call({ username: username, email: email, password: password }) | |||
| gitea_user = interactor.result | |||
| result = Gitea::User::GenerateTokenService.call(username, password) | |||
| user.gitea_token = result['sha1'] | |||
| user.gitea_uid = gitea_user[:body]['id'] | |||
| user.created_on = issue_created_at | |||
| user.updated_on = issue_created_at | |||
| user.is_test = true | |||
| user.save! | |||
| UserExtension.create!(user_id: user.id) | |||
| puts "import_user batch success: phone #{phone} email: #{email}" | |||
| end | |||
| issue_params = { | |||
| :status_id => issue_status, | |||
| :priority_id => priority, | |||
| # :milestone_id, | |||
| # :branch_name, | |||
| # :start_date, | |||
| # :due_date, | |||
| :subject => issue['title'], | |||
| :description => issue['body'], | |||
| # :blockchain_token_num, | |||
| :issue_tag_ids => [], | |||
| :assigner_ids => [], | |||
| :attachment_ids => [], | |||
| :receivers_login => [] | |||
| } | |||
| created_issue = Api::V1::Issues::CreateService.call(project, issue_params, user) | |||
| created_issue.update_columns(created_on: issue_created_at, updated_on: issue_updated_at) | |||
| end | |||
| issue_number = issue['number'] | |||
| comment_api_url = "https://gitee.com/api/v5/repos/mindspore/mindspore/issues/#{issue_number}/comments?access_token=96a637aa055f15056e77e3cf11a67525&page=1&per_page=100&order=asc" | |||
| comment_uri = URI.parse(comment_api_url) | |||
| comment_response = Net::HTTP.get_response(comment_uri) | |||
| comment_lists = JSON.parse(comment_response.body) | |||
| comment_lists.each do |comment| | |||
| next if Journal.find_by(journalized_id: created_issue.id, journalized_type: 'Issue', notes: comment['body']).present? | |||
| user_login = comment['user']['login'] | |||
| next if user_login.size >29 | |||
| comment_created_at = comment['created_at'] | |||
| comment_updated_at = comment['updated_at'] | |||
| comment_user = User.find_by(login: user_login) | |||
| unless comment_user.present? | |||
| username = user_login | |||
| email = "#{username}@gitlink.org.cn" | |||
| phone = "" | |||
| password = "a12345678" | |||
| comment_user = User.new(nickname: user_login, login: username, mail: email, password: password, type: 'User', phone: phone) | |||
| interactor = Gitea::RegisterInteractor.call({ username: username, email: email, password: password }) | |||
| gitea_user = interactor.result | |||
| result = Gitea::User::GenerateTokenService.call(username, password) | |||
| comment_user.gitea_token = result['sha1'] | |||
| comment_user.gitea_uid = gitea_user[:body]['id'] | |||
| comment_user.created_on = comment_created_at | |||
| comment_user.updated_on = comment_created_at | |||
| comment_user.save! | |||
| UserExtension.create!(user_id: comment_user.id) | |||
| end | |||
| journal_params = {:notes => comment['body'], | |||
| :attachment_ids => [], | |||
| :receivers_login => []} | |||
| object_result = Api::V1::Issues::Journals::CreateService.call(created_issue, journal_params, comment_user) | |||
| object_result.update_columns(created_on: comment_created_at, updated_on: comment_updated_at) | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -7,6 +7,9 @@ namespace :commit_log_to_db do | |||
| if ENV['project_name'] == "jittor" | |||
| projects = Project.where(id: [1403791,1404837,1404087,1403854,1403883,1403886,1403894,1404027,1404187,1404234,1404291,1404522,1404238,1404632,1404003,1403975,1404285,1404525,1404017,1403843,1403867,1403874,1403878,1403881,1403887,1403898,1403916,1403937,1403954,1403961,1403963,1403965,1403967,1403972,1403974,1403976,1403977,1403978,1404007,1404012,1404020,1404023,1404032,1404042,1404058,1404065,1404069,1404071,1404072,1404078,1404094,1404104,1404143,1404146,1404167,1404173,1404174,1404175,1404210,1404216,1404226,1404229,1404240,1404242,1404244,1404247,1404248,1404250,1404261,1404272,1404276,1404290,1404327,1404333,1404360,1404450,1404460,1404466,1404485,1404489,1404491,1404496,1404500,1404502,1404507,1404519,1404521,1404523,1404527,1404530,1404532,1404537,1404540,1404541,1404710,1404328,1404211,1404241,1404107,1403880,1404271,1404268,1404745,1404101,1404051,1404047,1404052,1406706,1403852,1403931,1404165,1404607,1404498,1404014,1404045,1404043,1403739,1403801,1403815,1403821,1403822,1403824,1403836,1403853,1403872,1403904,1403906,1403909,1403915,1403919,1403921,1403943,1403945,1403971,1403981,1404022,1404026,1404030,1404037,1404091,1404133,1404163,1404184,1404209,1404212,1404220,1404246,1404249,1404263,1404265,1404287,1404299,1404311,1404497,1404506,1404546,1404558,1404577,1404582,1404591,1404592,1404594,1404595,1404596,1404597,1404605,1404606,1404609,1404639,1404641,1404659,1404671,1404681,1404702,1404748,1404754,1404759,1404785,1404879,1404955,1405169,1405205,1405265,1405374,1407368,1407446,1407671,1407673,1404956,1404079,1404080,1403889,1404665,1404801,1404224,1404931,1404544,1403844,1404036,1404067,1404587,1404696,1404732,1404598,1405192,1404162,1403920,1403903,1405248,1403792,1403817,1403962,1403841,1404188,1404185,1405273,1404575,1404550,1404834,1405420,1404141,1405256,1404633,1405277,1404590,1404796,1405230,1405189,1404584,1405257,1405198,1403925,1404692,1405253,1403809,1405118,1404756,1404962,1404864,1405190,1405258,1405274,1404642,1404924,1404453,1404926,1404649,1404237,1404233,1404600,1404758,1405259,1404157,1404444,1404451,1404920,1404919,1404927,1403830,1404658,1405145,1405185,1403842,1403807,1403895,1404549,1404593,1404750,1404798,1404551,1404701,1405156,1404579,1404655,1405267,1404957,1404556,1404651,1404456,1405430,1403955,1404063,1404214,1403942,1404040,1404804,1428732,1403939,1404208,1404245,1405278,1404139,1403850,1404192,1404293,1404297,1404370,1404492,1404693,1404757,1404329,1404512,1404228,1404314,1404016,1404652,1405275,1404832,1404561,1404653,1404704,1404892,1404589,1404953,1405269,1404881,1404221,1404230,1404793,1403953,1404933,1404035,1404599,1403924,1404119,1404526,1404581,1404705,1404709,1404073,1403808,1403892,1404574,1403849,1404251,1404792,1403865,1404640,1404783,1404048,1406707,1404708,1404053,1404295,1404050,1404049,1404044,1404274,1404046,1404636]) | |||
| end | |||
| if ENV['project_name'] == "mindspore" | |||
| projects = Project.where(identifier: ['MindSpore-first-experience', ' MindSpore-install', 'MindSpore-Application-practice', 'MindSpore-Model-Development', 'MindSpore-Data-preprocessing', 'Mindspore-Data-storage-use', 'MindSpore-Data-storage-kunpeng', 'MindSpore-LeNet-jzx3', 'MindSpore-competition'] ) | |||
| end | |||
| projects.each_with_index do |project, index| | |||
| result = Gitea::Repository::Commits::ListService.call(project.owner.login,project.identifier,sha: "", page: 1, limit: 200, token: project.owner.gitea_token) | |||
| next if result.blank? || result[:total_count].blank? | |||
| @@ -0,0 +1,97 @@ | |||
| namespace :import_educoder_cource_repo do | |||
| desc "sync outer repository to gitlink" | |||
| task done: :environment do | |||
| data = ImportRepo.all | |||
| if ENV['name'].present? | |||
| data = data.where("name like '%#{ENV['name']}%'") | |||
| end | |||
| data.each_with_index do |row, index| | |||
| puts index | |||
| root_repo = Repository.find_by(mirror_url: row.git_url) | |||
| next if root_repo.blank? | |||
| root_project = root_repo.project | |||
| begin | |||
| user = User.find_by(phone: row.myshixun_user_phone) || User.find_by(mail: row.myshixun_user_mail) | |||
| unless user.present? | |||
| username = generate_user_login('p') | |||
| email = row.myshixun_user_mail.blank? ? "#{username}@gitlink.org.cn" : row.myshixun_user_mail | |||
| phone = row.myshixun_user_phone | |||
| password = "a12345678" | |||
| user = User.new(nickname: row.myshixun_user_name, login: username, mail: email, password: password, type: 'User', phone: phone) | |||
| interactor = Gitea::RegisterInteractor.call({ username: username, email: email, password: password }) | |||
| gitea_user = interactor.result | |||
| result = Gitea::User::GenerateTokenService.call(username, password) | |||
| user.gitea_token = result['sha1'] | |||
| user.gitea_uid = gitea_user[:body]['id'] | |||
| user.save! | |||
| UserExtension.create!(user_id: user.id) | |||
| puts "import_user batch success: phone #{phone} email: #{email}" | |||
| end | |||
| repo = Repository.find_by(mirror_url: row.myshixun_git_url) | |||
| next if repo.present? && repo.project.present? | |||
| mirror_params = { | |||
| user_id: user.id, | |||
| auth_username: "xxqfamous@gmail.com", | |||
| auth_password: "eHhxMTIzNDU2Nzg5NTIx", | |||
| name: root_project.name, | |||
| description: root_project.description, | |||
| repository_name: root_project.identifier, | |||
| project_category_id: root_project.project_category_id, | |||
| project_language_id: root_project.project_language_id, | |||
| clone_addr: row.myshixun_git_url | |||
| } | |||
| Projects::MigrateService.call(user, mirror_params) | |||
| puts "sync outer repository to gitlink Success repo: #{row.myshixun_git_url}" | |||
| rescue Exception => e | |||
| puts "sync outer repository to gitlink Error repo: #{row.myshixun_git_url}, error:#{e}" | |||
| end | |||
| end | |||
| end | |||
| desc "batch forked project" | |||
| task forked: :environment do | |||
| data =ImportRepo.all | |||
| if ENV['name'].present? | |||
| data = data.where("name like '%#{ENV['name']}%'") | |||
| end | |||
| puts data.to_sql | |||
| data.each_with_index do |row, index| | |||
| begin | |||
| puts index | |||
| user = User.find_by(phone: row.myshixun_user_phone) || User.find_by(mail: row.myshixun_user_mail) | |||
| next if user.blank? | |||
| root_repo = Repository.find_by(mirror_url: row.git_url) | |||
| next if root_repo.blank? | |||
| root_project = root_repo.project | |||
| repo = Repository.find_by(mirror_url: row.myshixun_git_url) | |||
| # 学员项目未导入就跳过 | |||
| next if repo.blank? | |||
| # 已绑定的跳过 | |||
| next if repo.project.forked_from_project_id.present? | |||
| # 绑定fork关系 | |||
| ForkUser.create(project_id: root_project.id, fork_project_id: repo.project.id, user_id: repo.project.user_id) | |||
| # 处理时间防止列表刷屏 | |||
| new_date = root_project.created_on > Time.now - 30.days ? Time.now - 1.years + rand(5..90).day + rand(5..60).hour : root_project.created_on | |||
| root_project.update_columns(created_on: new_date, updated_on: new_date, forked_count: (root_project.forked_count.to_i + 1)) | |||
| # fork时间同样处理在创建时间之后 | |||
| new_date2 = new_date + rand(5..50).day + rand(5..30).hour | |||
| repo.project.update_columns(created_on: new_date2, updated_on: new_date2, forked_from_project_id: root_project.id) | |||
| puts "forked project success username: #{user.id}:#{repo.project.id}" | |||
| rescue Exception => e | |||
| puts "forked project error username: #{username}" | |||
| end | |||
| end | |||
| end | |||
| # 生成邀请码 | |||
| CODES = %W(0 1 2 3 4 5 6 7 8 9) | |||
| def generate_user_login type | |||
| code = CODES.sample(8).join | |||
| code = type + code.to_s | |||
| return generate_user_login(type) if User.where(login: code).present? | |||
| code | |||
| end | |||
| end | |||
| @@ -0,0 +1,26 @@ | |||
| # 执行示例 bundle exec rake init_project_topic:project | |||
| # RAILS_ENV=production bundle exec rake init_project_topic:project | |||
| namespace :init_project_topic do | |||
| desc "Init Project Topic for Project" | |||
| task project: :environment do | |||
| Project.order(created_at: :desc).find_each do |p| | |||
| next unless p.owner.present? | |||
| next if p.project_topics.size >= 3 | |||
| begin | |||
| languages = $gitea_client.get_repos_languages_by_owner_repo(p.owner.login, p.identifier) | |||
| topic_count = p.project_topics.size | |||
| languages.each do |k, _| | |||
| next if topic_count >= 3 | |||
| project_topic = ProjectTopic.find_or_create_by!(name: k.downcase) | |||
| project_topic_ralate = project_topic.project_topic_ralates.find_or_create_by!(project_id: p.id) | |||
| if project_topic.present? && project_topic_ralate.present? | |||
| topic_count +=1 | |||
| end | |||
| end | |||
| rescue | |||
| next | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,44 @@ | |||
| # 执行示例 bundle exec rake "special_commit:load[yystopf, pig]" | |||
| # RAILS_ENV=production bundle exec rake "special_commit:load[yystopf, pig]" | |||
| # | |||
| namespace :special_commit do | |||
| desc "Sync Special Commit to Cache" | |||
| task :load, [:login, :identifier] => :environment do |t, args| | |||
| owner = Owner.find_by(login: args.login) | |||
| project = Project.find_by(user_id: owner&.id, identifier: args.identifier) | |||
| if owner.nil? || project.nil? | |||
| puts "====Project is not found.====" | |||
| else | |||
| puts "====Sync Project: #{owner.real_name}/#{project.name}====" | |||
| puts "====Sync Count Project Self Commit====" | |||
| self_commit_list_result = $gitea_client.get_repos_commits_by_owner_repo(owner.login, project.identifier) | |||
| total_commits = self_commit_list_result[:total_data].to_i | |||
| puts "====Sync Count Project Submodule Commit====" | |||
| entries = $gitea_client.get_repos_contents_by_owner_repo(owner.login, project.identifier) | |||
| entries.each do |entry| | |||
| next if entry["submodule_git_url"].nil? | |||
| submodule_git_url = entry["submodule_git_url"].gsub(Rails.application.config_for(:configuration)['platform_url'], '').gsub(".git", '') | |||
| real_relative_path = File.expand_path(submodule_git_url, "#{owner.login}/#{project.identifier}").gsub("#{Rails.root}/", '') | |||
| sub_project_owner_login = real_relative_path.split("/")[0] | |||
| sub_project_identifier = real_relative_path.split("/")[1] | |||
| sub_owner = Owner.find_by(login: sub_project_owner_login) | |||
| sub_project = sub_owner.projects.find_by(identifier: sub_project_identifier) | |||
| next if sub_owner.nil? || sub_project.nil? | |||
| sub_commit_list_result = $gitea_client.get_repos_commits_by_owner_repo(sub_project_owner_login, sub_project_identifier) | |||
| total_commits += sub_commit_list_result[:total_data].to_i | |||
| puts "====Sync Count Project Submodule forkproject Commit====" | |||
| sub_project.forked_projects.each do |p| | |||
| forked_project_owner_login = p.owner&.login | |||
| forked_project_identifier = p.identifier | |||
| next if forked_project_owner_login.nil? || forked_project_owner_login.nil? | |||
| forked_commit_list_result = $gitea_client.get_repos_commits_by_owner_repo(forked_project_owner_login, forked_project_identifier) | |||
| total_commits += forked_commit_list_result[:total_data].to_i | |||
| end | |||
| end | |||
| puts "====Write total commits to cache====" | |||
| $redis_cache.set("ProjectSpecialCommit:#{project.id}", total_commits) | |||
| $redis_cache.expireat("ProjectSpecialCommit:#{project.id}", (Date.today+30.days).to_time.to_i) | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,79 @@ | |||
| # 执行示例 bundle exec rake "sync_mindspore:contributor_to_user" | |||
| # RAILS_ENV=production bundle exec rake "sync_mindspore:contributor_to_user" | |||
| desc "mindspore项目贡献者数据" | |||
| # 同步mindspre贡献者数据至用户表 | |||
| namespace :sync_mindspore do | |||
| desc "同步用户信息" | |||
| task contributor_to_user: :environment do | |||
| i = 1 | |||
| fail_users = [] | |||
| file = File.open('public/mindspore_authors', 'r') | |||
| file.each_line do |l| | |||
| itemArray = l.gsub("\r\n", "").split("|:|") | |||
| email = itemArray[0] | |||
| username = itemArray[1] | |||
| password = '12345678' | |||
| puts "=======Generate:[#{i}] Username: #{username}, Password: #{password}, Email: #{email}=======" | |||
| puts "=======Create User Begin====== " | |||
| user = User.new(admin: false, login: username, mail: email, type: "User", is_test: 1) | |||
| user.password = password | |||
| user.platform = 'forge' | |||
| user.activate | |||
| unless user.valid? | |||
| puts "=======Create User Fail!====== " | |||
| fail_users << [username, email] | |||
| next | |||
| end | |||
| interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password}) | |||
| if interactor.success? | |||
| gitea_user = interactor.result | |||
| result = Gitea::User::GenerateTokenService.call(username, password) | |||
| user.gitea_token = result['sha1'] | |||
| user.gitea_uid = gitea_user[:body]['id'] | |||
| if user.save! | |||
| UserExtension.create!(user_id: user.id) | |||
| end | |||
| end | |||
| i += 1 | |||
| puts "=======Create User End====== " | |||
| end | |||
| puts "=======Fail Users:#{fail_users}====== " | |||
| file.close | |||
| end | |||
| # 执行示例 bundle exec rake "sync_mindspore:init_project_blockchain[1,2]" | |||
| # RAILS_ENV=production bundle exec rake "sync_mindspore:init_project_blockchain[1,2]" | |||
| desc "初始化区块链项目" | |||
| task :init_project_blockchain, [:id, :init_id] => :environment do |t, args| | |||
| puts "=====Init Project Blockchain: #{args.id}=====" | |||
| project = Project.find_by_id(args.id) | |||
| project.update_column(:use_blockchain, true) | |||
| username = project.user_id.to_s | |||
| token_name = project.id.to_s | |||
| total_supply = 10000 | |||
| token_balance = [[init_id.to_s, 100]] | |||
| contributions = Project.mindspore_contributors | |||
| total_contributions = contributions.sum{|i| i["contributions"]} | |||
| contributions.each do |con| | |||
| cont_balance = Float(con["contributions"]*9900/total_contributions).round(0) | |||
| token_balance << [con["id"].to_s, cont_balance] if cont_balance > 0 | |||
| end | |||
| params = { | |||
| "request-type": "create repo", | |||
| "username": username, | |||
| "token_name": token_name, | |||
| "total_supply": total_supply, | |||
| "token_balance": token_balance | |||
| }.to_json | |||
| resp_body = Blockchain::InvokeBlockchainApi.call(params) | |||
| end | |||
| end | |||
| @@ -0,0 +1,57 @@ | |||
| namespace :total_commit_count do | |||
| desc "total_commit_count" | |||
| task done: :environment do | |||
| project_name = ENV['name'] || "mindspore" | |||
| puts "project_id=================#{project_name}" | |||
| projects = Project.where("name like ?", "%#{project_name}%") | |||
| if ENV['count'].present? | |||
| projects = projects.limit(ENV['count'].to_i) | |||
| end | |||
| @date_count_hash = {} | |||
| # projects = Project.where(:name => 'opensource0311') | |||
| projects.each_with_index do |project, index| | |||
| result = Gitea::Repository::Commits::ListService.call(project.owner.login,project.identifier,sha: "", page: 1, limit: 5, token: project.owner.gitea_token) | |||
| next if result.blank? || result[:total_count].blank? | |||
| total_count = result[:total_count] | |||
| next if total_count > 2000 | |||
| puts "#{index} total_count==========#{total_count}" | |||
| if total_count > 50 | |||
| total_page = (total_count / 50) + 1 | |||
| total_page.times do |i| | |||
| add_commit_to_index(project, i + 1) | |||
| end | |||
| else | |||
| add_commit_to_index(project, 1) | |||
| end | |||
| puts "#{index} date_count_hash===========#{@date_count_hash.to_json}" | |||
| end | |||
| puts "@date_count_hash===========#{@date_count_hash.to_json}" | |||
| # Time.now | |||
| # Wed Mar 15 14:12:09 2023 +0800 | |||
| # Time.now.strftime("%a %b %d %H:%M:%S %Y") | |||
| # Time.now.strftime("%a %b %d %H:%M:%S %Y +0800") | |||
| Time.parse("2023-03-15 14:12:09").strftime("%a %b %d %H:%M:%S %Y +0800") | |||
| end | |||
| def add_commit_to_index(project, page) | |||
| # Gitea::Repository::Commits::ListSliceService.call(project.owner.login,project.identifier,sha: "", page: 1, limit: 1000, token: "a9244ecac647dd33fee3b480c5898baab1d3fe7d") | |||
| result = Gitea::Repository::Commits::ListService.call(project.owner.login,project.identifier,sha: "", page: page, limit: 50, token: project.owner.gitea_token) | |||
| result[:body].each do |commit| | |||
| commit_date = Time.parse(commit['commit']['author']['date']) | |||
| commit_date_str = commit_date.strftime("%Y-%m") | |||
| if @date_count_hash[commit_date_str].present? | |||
| @date_count_hash[commit_date_str] = @date_count_hash[commit_date_str] + 1 | |||
| else | |||
| @date_count_hash[commit_date_str] = 1 | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -3,10 +3,13 @@ namespace :total_commit_to_db do | |||
| task done: :environment do | |||
| project_name = ENV['name'] || "mindspore" | |||
| puts "project_id=================#{project_name}" | |||
| projects = Project.where(identifier: ['MindSpore-first-experience', ' MindSpore-install', 'MindSpore-Application-practice', 'MindSpore-Model-Development', 'MindSpore-Data-preprocessing', 'Mindspore-Data-storage-use', 'MindSpore-Data-storage-kunpeng', 'MindSpore-LeNet-jzx3', 'MindSpore-competition'] ) | |||
| if ENV['project_id'].present? | |||
| projects = Project.where(id: ENV['project_id']) | |||
| else | |||
| projects = Project.where(identifier: ['MindSpore-first-experience', ' MindSpore-install', 'MindSpore-Application-practice', 'MindSpore-Model-Development', 'MindSpore-Data-preprocessing', 'Mindspore-Data-storage-use', 'MindSpore-Data-storage-kunpeng', 'MindSpore-LeNet-jzx3', 'MindSpore-competition'] ) | |||
| end | |||
| projects.each_with_index do |project, index| | |||
| result = Gitea::Repository::Commits::ListService.call(project.owner.login,project.identifier,sha: "", page: 1, limit: 5, token: project.owner.gitea_token) | |||
| result = Gitea::Repository::Commits::ListService.call(project.owner.login,project.identifier,sha: "", page: 1, limit: 200, token: project.owner.gitea_token) | |||
| next if result.blank? || result[:total_count].blank? | |||
| total_count = result[:total_count] | |||
| # next if total_count > 2000 | |||
| @@ -62,4 +65,4 @@ namespace :total_commit_to_db do | |||
| sql_connection.execute(sql) | |||
| end | |||
| end | |||
| end | |||
| @@ -23,7 +23,7 @@ namespace :update_educoder_user do | |||
| department_name = user_info["school"] | |||
| password = "12345678" | |||
| user.update_columns(mail: "#{email}", phone: "#{phone}") | |||
| user.update_columns(mail: "#{email}", phone: "#{phone}", lastname: real_name, nickname: real_name) | |||
| sync_params = { | |||
| email: email, | |||
| @@ -28030,6 +28030,187 @@ $.fn.bootstrapViewer.defaults = { | |||
| src: 'src' | |||
| } | |||
| ; | |||
| /*! ======================================================================== | |||
| * Bootstrap Toggle: bootstrap-toggle.js v2.2.0 | |||
| * http://www.bootstraptoggle.com | |||
| * ======================================================================== | |||
| * Copyright 2014 Min Hur, The New York Times Company | |||
| * Licensed under MIT | |||
| * ======================================================================== */ | |||
| +function ($) { | |||
| 'use strict'; | |||
| // TOGGLE PUBLIC CLASS DEFINITION | |||
| // ============================== | |||
| var Toggle = function (element, options) { | |||
| this.$element = $(element) | |||
| this.options = $.extend({}, this.defaults(), options) | |||
| this.render() | |||
| } | |||
| Toggle.VERSION = '2.2.0' | |||
| Toggle.DEFAULTS = { | |||
| on: 'On', | |||
| off: 'Off', | |||
| onstyle: 'primary', | |||
| offstyle: 'default', | |||
| size: 'normal', | |||
| style: '', | |||
| width: null, | |||
| height: null | |||
| } | |||
| Toggle.prototype.defaults = function() { | |||
| return { | |||
| on: this.$element.attr('data-on') || Toggle.DEFAULTS.on, | |||
| off: this.$element.attr('data-off') || Toggle.DEFAULTS.off, | |||
| onstyle: this.$element.attr('data-onstyle') || Toggle.DEFAULTS.onstyle, | |||
| offstyle: this.$element.attr('data-offstyle') || Toggle.DEFAULTS.offstyle, | |||
| size: this.$element.attr('data-size') || Toggle.DEFAULTS.size, | |||
| style: this.$element.attr('data-style') || Toggle.DEFAULTS.style, | |||
| width: this.$element.attr('data-width') || Toggle.DEFAULTS.width, | |||
| height: this.$element.attr('data-height') || Toggle.DEFAULTS.height | |||
| } | |||
| } | |||
| Toggle.prototype.render = function () { | |||
| this._onstyle = 'btn-' + this.options.onstyle | |||
| this._offstyle = 'btn-' + this.options.offstyle | |||
| var size = this.options.size === 'large' ? 'btn-lg' | |||
| : this.options.size === 'small' ? 'btn-sm' | |||
| : this.options.size === 'mini' ? 'btn-xs' | |||
| : '' | |||
| var $toggleOn = $('<label class="btn">').html(this.options.on) | |||
| .addClass(this._onstyle + ' ' + size) | |||
| var $toggleOff = $('<label class="btn">').html(this.options.off) | |||
| .addClass(this._offstyle + ' ' + size + ' active') | |||
| var $toggleHandle = $('<span class="toggle-handle btn btn-default">') | |||
| .addClass(size) | |||
| var $toggleGroup = $('<div class="toggle-group">') | |||
| .append($toggleOn, $toggleOff, $toggleHandle) | |||
| var $toggle = $('<div class="toggle btn" data-toggle="toggle">') | |||
| .addClass( this.$element.prop('checked') ? this._onstyle : this._offstyle+' off' ) | |||
| .addClass(size).addClass(this.options.style) | |||
| this.$element.wrap($toggle) | |||
| $.extend(this, { | |||
| $toggle: this.$element.parent(), | |||
| $toggleOn: $toggleOn, | |||
| $toggleOff: $toggleOff, | |||
| $toggleGroup: $toggleGroup | |||
| }) | |||
| this.$toggle.append($toggleGroup) | |||
| var width = this.options.width || Math.max($toggleOn.outerWidth(), $toggleOff.outerWidth())+($toggleHandle.outerWidth()/2) | |||
| var height = this.options.height || Math.max($toggleOn.outerHeight(), $toggleOff.outerHeight()) | |||
| $toggleOn.addClass('toggle-on') | |||
| $toggleOff.addClass('toggle-off') | |||
| this.$toggle.css({ width: width, height: height }) | |||
| if (this.options.height) { | |||
| $toggleOn.css('line-height', $toggleOn.height() + 'px') | |||
| $toggleOff.css('line-height', $toggleOff.height() + 'px') | |||
| } | |||
| this.update(true) | |||
| this.trigger(true) | |||
| } | |||
| Toggle.prototype.toggle = function () { | |||
| if (this.$element.prop('checked')) this.off() | |||
| else this.on() | |||
| } | |||
| Toggle.prototype.on = function (silent) { | |||
| if (this.$element.prop('disabled')) return false | |||
| this.$toggle.removeClass(this._offstyle + ' off').addClass(this._onstyle) | |||
| this.$element.prop('checked', true) | |||
| if (!silent) this.trigger() | |||
| } | |||
| Toggle.prototype.off = function (silent) { | |||
| if (this.$element.prop('disabled')) return false | |||
| this.$toggle.removeClass(this._onstyle).addClass(this._offstyle + ' off') | |||
| this.$element.prop('checked', false) | |||
| if (!silent) this.trigger() | |||
| } | |||
| Toggle.prototype.enable = function () { | |||
| this.$toggle.removeAttr('disabled') | |||
| this.$element.prop('disabled', false) | |||
| } | |||
| Toggle.prototype.disable = function () { | |||
| this.$toggle.attr('disabled', 'disabled') | |||
| this.$element.prop('disabled', true) | |||
| } | |||
| Toggle.prototype.update = function (silent) { | |||
| if (this.$element.prop('disabled')) this.disable() | |||
| else this.enable() | |||
| if (this.$element.prop('checked')) this.on(silent) | |||
| else this.off(silent) | |||
| } | |||
| Toggle.prototype.trigger = function (silent) { | |||
| this.$element.off('change.bs.toggle') | |||
| if (!silent) this.$element.change() | |||
| this.$element.on('change.bs.toggle', $.proxy(function() { | |||
| this.update() | |||
| }, this)) | |||
| } | |||
| Toggle.prototype.destroy = function() { | |||
| this.$element.off('change.bs.toggle') | |||
| this.$toggleGroup.remove() | |||
| this.$element.removeData('bs.toggle') | |||
| this.$element.unwrap() | |||
| } | |||
| // TOGGLE PLUGIN DEFINITION | |||
| // ======================== | |||
| function Plugin(option) { | |||
| return this.each(function () { | |||
| var $this = $(this) | |||
| var data = $this.data('bs.toggle') | |||
| var options = typeof option == 'object' && option | |||
| if (!data) $this.data('bs.toggle', (data = new Toggle(this, options))) | |||
| if (typeof option == 'string' && data[option]) data[option]() | |||
| }) | |||
| } | |||
| var old = $.fn.bootstrapToggle | |||
| $.fn.bootstrapToggle = Plugin | |||
| $.fn.bootstrapToggle.Constructor = Toggle | |||
| // TOGGLE NO CONFLICT | |||
| // ================== | |||
| $.fn.toggle.noConflict = function () { | |||
| $.fn.bootstrapToggle = old | |||
| return this | |||
| } | |||
| // TOGGLE DATA-API | |||
| // =============== | |||
| $(function() { | |||
| $('input[type=checkbox][data-toggle^=toggle]').bootstrapToggle() | |||
| }) | |||
| $(document).on('click.bs.toggle', 'div[data-toggle^=toggle]', function(e) { | |||
| var $checkbox = $(this).find('input[type=checkbox]') | |||
| $checkbox.bootstrapToggle('toggle') | |||
| e.preventDefault() | |||
| }) | |||
| }(jQuery); | |||
| /* Author:mingyuhisoft@163.com | |||
| * Github:https://github.com/imingyu/jquery.mloading | |||
| * Npm:npm install jquery.mloading.js | |||
| @@ -135541,8 +135722,8 @@ $(document).on('turbolinks:load', function() { | |||
| ; | |||
| $(document).on('turbolinks:load', function() { | |||
| if ($('body.admins-courses-index-page').length > 0) { | |||
| let searchContainer = $(".course-list-form"); | |||
| let searchForm = $("form.search-form",searchContainer); | |||
| var searchContainer = $(".course-list-form"); | |||
| var searchForm = $("form.search-form",searchContainer); | |||
| searchContainer.on('change', '.course-homepage-show', function(){ | |||
| searchForm.find('input[type="submit"]').trigger('click'); | |||
| @@ -135979,6 +136160,20 @@ $(document).on('turbolinks:load', function() { | |||
| }); | |||
| } | |||
| }); | |||
| // ------------ 上移/下移 ------------- | |||
| $('.discipline-list-container').on('click', ".move-action", function () { | |||
| var $doAction = $(this); | |||
| var disciplineId = $doAction.data('id'); | |||
| var opr = $doAction.data('opr'); | |||
| $.ajax({ | |||
| url: '/admins/disciplines/' + disciplineId + '/adjust_position', | |||
| method: 'POST', | |||
| dataType: 'script', | |||
| data: {opr: opr} | |||
| }); | |||
| }); | |||
| } | |||
| }); | |||
| $(document).on('turbolinks:load', function() { | |||
| @@ -136082,6 +136277,9 @@ $(document).on('turbolinks:load', function() { | |||
| }); | |||
| } | |||
| }); | |||
| // Place all the behaviors and hooks related to the matching controller here. | |||
| // All this logic will automatically be available in application.js. | |||
| ; | |||
| $(document).on('turbolinks:load', function() { | |||
| if($(".admins-graduation-standards-index-page").length > 0){ | |||
| $(".admin-body-container").on("click", ".standard-create-modal", function () { | |||
| @@ -137754,17 +137952,149 @@ $(document).on('turbolinks:load', function() { | |||
| } | |||
| }) | |||
| ; | |||
| $(document).on('turbolinks:load', function () { | |||
| if ($('body.admins-projects-index-page').length > 0) { | |||
| var $form = $('.search-form'); | |||
| /* | |||
| * @Description: Do not edit | |||
| * @Date: 2021-08-31 11:16:45 | |||
| * @LastEditors: viletyy | |||
| * @Author: viletyy | |||
| * @LastEditTime: 2021-08-31 14:19:46 | |||
| * @FilePath: /forgeplus/app/assets/javascripts/admins/system_notifications/index.js | |||
| */ | |||
| // 清空 | |||
| $form.on('click', '.clear-btn', function () { | |||
| $form.find('input[name="search"]').val(''); | |||
| $form.find('input[type="submit"]').trigger('click'); | |||
| }); | |||
| $(document).on('turbolinks:load', function(){ | |||
| var showSuccessNotify = function() { | |||
| $.notify({ | |||
| message: '操作成功' | |||
| },{ | |||
| type: 'success' | |||
| }); | |||
| } | |||
| }); | |||
| // close user | |||
| $('.project-list-container').on('click', '.recommend-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unrecommend-action'); | |||
| var $editAction = $closeAction.siblings('.edit-recommend-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认将该项目设置为推荐项目吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| recommend: true, | |||
| recommend_index: 1 | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $editAction.show(); | |||
| $(".project-item-"+keywordID).children('td').eq(5).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.project-list-container').on('click', '.unrecommend-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.recommend-action'); | |||
| var $editAction = $closeAction.siblings('.edit-recommend-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认取消该推荐项目吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| recommend: false, | |||
| recommend_index: 0 | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $editAction.hide(); | |||
| $(".project-item-"+keywordID).children('td').eq(5).text("") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| // close user | |||
| $('.project-list-container').on('click', '.pinned-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unpinned-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认将该项目设置为精选项目吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| is_pinned: true, | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $(".project-item-"+keywordID).children('td').eq(4).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.project-list-container').on('click', '.unpinned-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.pinned-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认取消该精选项目吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| is_pinned: false, | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $(".project-item-"+keywordID).children('td').eq(4).text("") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| }) | |||
| ; | |||
| $(document).on('turbolinks:load', function() { | |||
| if ($('body.admins-repertoires-index-page').length > 0) { | |||
| @@ -137830,6 +138160,84 @@ $(document).on('turbolinks:load', function() { | |||
| }); | |||
| } | |||
| }); | |||
| /* | |||
| * @Description: Do not edit | |||
| * @Date: 2021-08-31 11:16:45 | |||
| * @LastEditors: viletyy | |||
| * @Author: viletyy | |||
| * @LastEditTime: 2021-08-31 14:19:46 | |||
| * @FilePath: /forgeplus/app/assets/javascripts/admins/reversed_keywords/index.js | |||
| */ | |||
| $(document).on('turbolinks:load', function(){ | |||
| var showSuccessNotify = function() { | |||
| $.notify({ | |||
| message: '操作成功' | |||
| },{ | |||
| type: 'success' | |||
| }); | |||
| } | |||
| // close user | |||
| $('.reversed-keyword-list-container').on('click', '.close-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unclose-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认关闭限制吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/reversed_keywords/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| reversed_keyword: { | |||
| closed: true | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $(".reversed-keyword-item-"+keywordID).children('td').eq(3).text("") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.reversed-keyword-list-container').on('click', '.unclose-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.close-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认开启限制吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/reversed_keywords/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| reversed_keyword: { | |||
| closed: false | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $(".reversed-keyword-item-"+keywordID).children('td').eq(3).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| }) | |||
| ; | |||
| $(document).on('turbolinks:load', function() { | |||
| if ($('body.admins-salesman-channels-index-page').length > 0) { | |||
| @@ -137837,13 +138245,13 @@ $(document).on('turbolinks:load', function() { | |||
| var $addMemberModal = $('.admin-add-salesman-channel-user-modal'); | |||
| var $addMemberForm = $addMemberModal.find('.admin-add-salesman-channel-user-form'); | |||
| var $memberSelect = $addMemberModal.find('.salesman-channel-user-select'); | |||
| var $salesmanIdInput = $('.salesman-channel-list-form').find(".btn-primary"); | |||
| var $form = $addMemberModal.find('form.admin-add-salesman-user-form'); | |||
| $addMemberModal.on('show.bs.modal', function(event){ | |||
| var $link = $(event.relatedTarget); | |||
| // var salesmanId = $link.data('salesman_id'); | |||
| // $salesmanIdInput.val(salesmanId); | |||
| // 搜索 | |||
| var searchscForm = $(".saleman-channel-list-form .search-form"); | |||
| $addMemberModal.on('show.bs.modal', function(event){ | |||
| $memberSelect.select2('val', ' '); | |||
| }); | |||
| @@ -137880,28 +138288,73 @@ $(document).on('turbolinks:load', function() { | |||
| // var salesmanId = $salesmanIdInput.val(); | |||
| var memberIds = $memberSelect.val(); | |||
| if (memberIds && memberIds.length > 0) { | |||
| var url = $form.data('url'); | |||
| $.ajax({ | |||
| method: 'POST', | |||
| dataType: 'json', | |||
| url: '/admins/salesman_channels/batch_add', | |||
| data: { salesman_id: $salesmanIdInput.data("salesman-id"), school_ids: memberIds }, | |||
| url: url, | |||
| data: $form.serialize(), | |||
| success: function(){ | |||
| $.notify({ message: '创建成功' }); | |||
| $addMemberModal.modal('hide'); | |||
| searchscForm.find('input[name="keyword"]').val(''); | |||
| setTimeout(function(){ | |||
| window.location.reload(); | |||
| submitForm(); | |||
| }, 500); | |||
| }, | |||
| error: function(res){ | |||
| var data = res.responseJSON; | |||
| $form.find('.error').html(data.message); | |||
| $addMemberForm.find('.error').html(data.message); | |||
| } | |||
| }); | |||
| } else { | |||
| $addMemberModal.modal('hide'); | |||
| } | |||
| }); | |||
| // 清空 | |||
| searchscForm.on('click', '.clear-btn', function () { | |||
| searchscForm.find('.start_date').val(''); | |||
| searchscForm.find('.end_date').val('').trigger('change'); | |||
| searchscForm.find('input[name="keyword"]').val(''); | |||
| }); | |||
| // 时间跨度 | |||
| var baseOptions = { | |||
| autoclose: true, | |||
| language: 'zh-CN', | |||
| format: 'yyyy-mm-dd', | |||
| startDate: '2017-04-01' | |||
| }; | |||
| var defineDateRangeSelect = function(element){ | |||
| var options = $.extend({inputs: $(element).find('.start-date, .end-date')}, baseOptions); | |||
| $(element).datepicker(options); | |||
| $(element).find('.start-date').datepicker().on('changeDate', function(e){ | |||
| $(element).find('.end-date').datepicker('setStartDate', e.date); | |||
| }); | |||
| }; | |||
| defineDateRangeSelect('.grow-date-input-daterange'); | |||
| // 区间搜索 | |||
| searchscForm.on('click', ".search-btn", function(){ | |||
| submitForm(); | |||
| }); | |||
| var submitForm = function(){ | |||
| var url = searchscForm.data('search-form-url'); | |||
| var form = searchscForm; | |||
| $.ajax({ | |||
| url: url, | |||
| data: form.serialize(), | |||
| dataType: "script" | |||
| }) | |||
| }; | |||
| } | |||
| }); | |||
| $(document).on('turbolinks:load', function() { | |||
| @@ -137964,7 +138417,7 @@ $(document).on('turbolinks:load', function() { | |||
| $addMemberModal.modal('hide'); | |||
| setTimeout(function(){ | |||
| window.location.reload(); | |||
| listForm(); | |||
| }, 500); | |||
| }, | |||
| error: function(res){ | |||
| @@ -137976,6 +138429,14 @@ $(document).on('turbolinks:load', function() { | |||
| $addMemberModal.modal('hide'); | |||
| } | |||
| }); | |||
| var listForm = function(){ | |||
| $.ajax({ | |||
| url: '/admins/salesman_customers?salesman_id='+ $salesmanIdInput.data("salesman-id"), | |||
| dataType: "script" | |||
| }); | |||
| }; | |||
| } | |||
| }); | |||
| $(document).on('turbolinks:load', function() { | |||
| @@ -138272,22 +138733,19 @@ $(document).on('turbolinks:load', function(){ | |||
| defineDateRangeSelect('.grow-date-input-daterange'); | |||
| } | |||
| }); | |||
| $(document).on('turbolinks:load', function () { | |||
| if ($('body.admins-shixun-modify-records-index-page').length > 0) { | |||
| var $form = $('.search-form'); | |||
| /* | |||
| * @Description: Do not edit | |||
| * @Date: 2021-07-16 11:58:16 | |||
| * @LastEditors: viletyy | |||
| * @Author: viletyy | |||
| * @LastEditTime: 2021-08-31 14:48:59 | |||
| * @FilePath: /forgeplus/app/assets/javascripts/admins/shixun_settings/index.js | |||
| */ | |||
| // 清空 | |||
| $form.on('click', '.clear-btn', function () { | |||
| $form.find('select[name="date"]').val('weekly'); | |||
| $form.find('input[name="user_name"]').val(''); | |||
| $form.find('input[type="submit"]').trigger('click'); | |||
| }); | |||
| } | |||
| }); | |||
| $(document).on('turbolinks:load', function() { | |||
| if ($('body.admins-shixun-settings-index-page').length > 0) { | |||
| let searchContainer = $(".shixun-settings-list-form"); | |||
| let searchForm = $("form.search-form",searchContainer); | |||
| var searchContainer = $(".shixun-settings-list-form"); | |||
| var searchForm = $("form.search-form",searchContainer); | |||
| searchContainer.on('change', '.shixun-settings-select', function(){ | |||
| searchForm.find('input[type="submit"]').trigger('click'); | |||
| @@ -138458,6 +138916,19 @@ $(document).on('turbolinks:load', function () { | |||
| data: json | |||
| }); | |||
| }); | |||
| // ------------ 上移/下移 ------------- | |||
| $('.sub-discipline-list-container').on('click', ".move-action", function () { | |||
| var $doAction = $(this); | |||
| var objectId = $doAction.data('id'); | |||
| var opr = $doAction.data('opr'); | |||
| $.ajax({ | |||
| url: '/admins/sub_disciplines/' + objectId + '/adjust_position', | |||
| method: 'POST', | |||
| dataType: 'script', | |||
| data: {opr: opr} | |||
| }); | |||
| }); | |||
| } | |||
| }); | |||
| $(document).on('turbolinks:load', function() { | |||
| @@ -138577,6 +139048,7 @@ $(document).on('turbolinks:load', function () { | |||
| } | |||
| }); | |||
| $(".subject-setting-list-container").on("change", '.subject-setting-form', function () { | |||
| var s_id = $(this).attr("data-id"); | |||
| var s_value = $(this).val(); | |||
| @@ -138716,6 +139188,84 @@ $(document).on('turbolinks:load', function () { | |||
| defineStatusChangeFunc('.cancel-excellent-action', '.excellent-action', '/cancel_excellent', excellentCallback); | |||
| } | |||
| }); | |||
| /* | |||
| * @Description: Do not edit | |||
| * @Date: 2021-08-31 11:16:45 | |||
| * @LastEditors: viletyy | |||
| * @Author: viletyy | |||
| * @LastEditTime: 2021-08-31 14:19:46 | |||
| * @FilePath: /forgeplus/app/assets/javascripts/admins/system_notifications/index.js | |||
| */ | |||
| $(document).on('turbolinks:load', function(){ | |||
| var showSuccessNotify = function() { | |||
| $.notify({ | |||
| message: '操作成功' | |||
| },{ | |||
| type: 'success' | |||
| }); | |||
| } | |||
| // close user | |||
| $('.system-notification-list-container').on('click', '.close-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unclose-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认取消置顶吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/system_notifications/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| system_notification: { | |||
| is_top: false | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $(".system-notification-item-"+keywordID).children('td').eq(3).text("") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.system-notification-list-container').on('click', '.unclose-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.close-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认置顶吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/system_notifications/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| system_notification: { | |||
| is_top: true | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $(".system-notification-item-"+keywordID).children('td').eq(3).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| }) | |||
| ; | |||
| $(document).on('turbolinks:load', function () { | |||
| if ($('body.admins-tag-disciplines-index-page').length > 0) { | |||
| @@ -138790,6 +139340,20 @@ $(document).on('turbolinks:load', function () { | |||
| data: json | |||
| }); | |||
| }); | |||
| // ------------ 上移/下移 ------------- | |||
| $('.tag-discipline-list-container').on('click', ".move-action", function () { | |||
| var $doAction = $(this); | |||
| var objectId = $doAction.data('id'); | |||
| var opr = $doAction.data('opr'); | |||
| $.ajax({ | |||
| url: '/admins/tag_disciplines/' + objectId + '/adjust_position', | |||
| method: 'POST', | |||
| dataType: 'script', | |||
| data: {opr: opr} | |||
| }); | |||
| }); | |||
| } | |||
| }); | |||
| $(document).on('turbolinks:load', function() { | |||
| @@ -139603,6 +140167,7 @@ $(document).on('turbolinks:load', function() { | |||
| $.ajaxSetup({ | |||
| @@ -139675,3 +140240,39 @@ $(document).on("turbolinks:before-cache", function () { | |||
| $(function () { | |||
| }); | |||
| $(document).on('turbolinks:load', function() { | |||
| $('.logo-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| $('.attachment-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| }) | |||
| ; | |||
| @@ -0,0 +1 @@ | |||
| {"version":3,"file":"bootstrap-toggle.min.js","sources":["bootstrap-toggle.js"],"names":["$","Plugin","option","this","each","$this","data","options","Toggle","element","$element","extend","defaults","render","VERSION","DEFAULTS","on","off","onstyle","offstyle","size","style","width","height","prototype","attr","_onstyle","_offstyle","$toggleOn","html","addClass","$toggleOff","$toggleHandle","$toggleGroup","append","$toggle","prop","wrap","parent","Math","max","outerWidth","outerHeight","css","update","trigger","toggle","silent","removeClass","enable","removeAttr","disable","change","proxy","destroy","remove","removeData","unwrap","old","fn","bootstrapToggle","Constructor","noConflict","document","e","$checkbox","find","preventDefault","jQuery"],"mappings":"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"} | |||
| @@ -0,0 +1 @@ | |||
| {"version":3,"file":"bootstrap2-toggle.min.js","sources":["bootstrap2-toggle.js"],"names":["$","Plugin","option","this","each","$this","data","options","Toggle","element","$element","extend","defaults","render","VERSION","DEFAULTS","on","off","onstyle","offstyle","size","style","width","height","prototype","attr","_onstyle","_offstyle","$toggleOn","html","addClass","$toggleOff","$toggleHandle","$toggleGroup","append","$toggle","prop","wrap","parent","Math","max","outerWidth","css","update","trigger","toggle","silent","removeClass","enable","removeAttr","disable","change","proxy","destroy","remove","removeData","unwrap","old","fn","bootstrapToggle","Constructor","noConflict","document","e","$checkbox","find","preventDefault","jQuery"],"mappings":"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"} | |||