Commit 6e6682f3 by Quang Vinh Nguyen

add admin page

parent a60f35d4
......@@ -292,6 +292,7 @@ GEM
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
redis (4.0.1)
responders (2.4.0)
actionpack (>= 4.2.0, < 5.3)
railties (>= 4.2.0, < 5.3)
......@@ -430,6 +431,7 @@ DEPENDENCIES
pry
puma (~> 3.11)
rails (~> 5.2.0)
redis
rsolr
rsolr-ext
rspec-rails
......
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
// Place all the styles related to the Admins controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
body {
background-color: #fff;
color: #333;
margin: 33px;
font-family: verdana, arial, helvetica, sans-serif;
font-size: 13px;
line-height: 18px;
}
p, ol, ul, td {
font-family: verdana, arial, helvetica, sans-serif;
font-size: 13px;
line-height: 18px;
}
pre {
background-color: #eee;
padding: 10px;
font-size: 11px;
}
a {
color: #000;
&:visited {
color: #666;
}
&:hover {
color: #fff;
background-color: #000;
}
}
th {
padding-bottom: 5px;
}
td {
padding: 0 5px 7px;
}
div {
&.field, &.actions {
margin-bottom: 10px;
}
}
#notice {
color: green;
}
.field_with_errors {
padding: 2px;
background-color: red;
display: table;
}
#error_explanation {
width: 450px;
border: 2px solid red;
padding: 7px 7px 0;
margin-bottom: 20px;
background-color: #f0f0f0;
h2 {
text-align: left;
font-weight: bold;
padding: 5px 5px 5px 15px;
font-size: 12px;
margin: -7px -7px 0;
background-color: #c00;
color: #fff;
}
ul li {
font-size: 12px;
list-style: square;
}
}
label {
display: block;
}
class AdminsController < ApplicationController
before_action :set_admin, only: [:show, :edit, :update, :destroy]
# GET /admins
# GET /admins.json
def index
@admins = Admin.all
end
# GET /admins/1
# GET /admins/1.json
def show
end
# GET /admins/new
def new
@admin = Admin.new
end
# GET /admins/1/edit
def edit
end
def create
@admin = Admin.find_by(admin_id: params[:admin][:admin_id])
if @admin && @admin.authenticated?(params[:admin][:password])
flash.now[:success] = 'Admin login success'
redirect_to admin_applies_url
#session delete
else
flash.now[:danger] = 'Invalid id or password'
render :new
end
end
def destroy
# log_out if logged_in?
# redirect_to root_url
end
# Return the current logged-in user (if any).
def current_user
# @current_user ||= User.find_by(id: session[:user_id])
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
# if user && user.authenticated?(cookies[:remember_token])
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
# Logs out the current user.
def log_out
forget(current_user)
session.delete(:user_id)
@current_user = nil
end
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
end
module AdminsHelper
end
class Admin < ApplicationRecord
validates :admin_id, presence: true
validates :password_digest, presence: true
has_secure_password
end
json.extract! admin, :id, :admin_id, :password_digest, :created_at, :updated_at
json.url admin_url(admin, format: :json)
<%= form_with(model: admin, local: true) do |form| %>
<% if admin.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(admin.errors.count, "error") %> prohibited this admin from being saved:</h2>
<ul>
<% admin.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :admin_id %>
<%= form.text_field :admin_id %>
</div>
<div class="field">
<%= form.label :password_digest %>
<%= form.text_field :password_digest %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
<h1>Editing Admin</h1>
<%= render 'form', admin: @admin %>
<%= link_to 'Show', @admin %> |
<%= link_to 'Back', admins_path %>
<p id="notice"><%= notice %></p>
<h1>Admins</h1>
<table>
<thead>
<tr>
<th>Admin</th>
<th>Password digest</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @admins.each do |admin| %>
<tr>
<td><%= admin.admin_id %></td>
<td><%= admin.password_digest %></td>
<td><%= link_to 'Show', admin %></td>
<td><%= link_to 'Edit', edit_admin_path(admin) %></td>
<td><%= link_to 'Destroy', admin, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Admin', new_admin_path %>
json.array! @admins, partial: 'admins/admin', as: :admin
<h3>Admin login</h3>
<%#= render 'form', admin: @admin %>
<%= form_for(@admin, url: admin_login_path) do |form| %>
<% if @admin.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@admin.errors.count, "error") %> when login:</h2>
<ul>
<% @admin.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :admin_id %><br>
<%= form.text_field :admin_id %>
</div>
<div class="field">
<%= form.label :password %><br>
<%= form.text_field :password %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
<%= link_to 'Back', admins_path %>
<p>
<strong>Admin:</strong>
</p>
<%= link_to "Log out", logout_path %>
json.partial! "admins/admin", admin: @admin
Rails.application.routes.draw do
resources :admins
get '/admin/login', to: 'admins#new'
post '/admin/login', to: 'admins#create'
get '/admin/applies', to: 'admins#index'
delete '/logout', to: 'admins#destroy'
root to: 'jobs#home'
resources :entries, only: [:new, :create, :show, :edit, :update]
resources :cities, only: :index
......
class CreateAdmins < ActiveRecord::Migration[5.2]
def change
create_table :admins do |t|
t.string :admin_id
t.string :password_digest
t.timestamps
end
end
end
......@@ -10,7 +10,14 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2018_06_19_011959) do
ActiveRecord::Schema.define(version: 2018_06_29_033452) do
create_table "admins", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "admin_id"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "cities", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "name"
......
require 'rubygems'
require 'nokogiri'
require 'open-uri'
require 'csv'
require_relative './helpers'
require 'yaml'
require 'redis'
require 'pry'
$r = Redis.new
print_memory_usage do
print_time_spent do
crawler_job
crawler_city
crawler_industry
end
end
def crawler_job
headers =
%w[ id
title
company_name
city
year_experience
industries
position
salary
update_date
exp_time
description
job_requirements
job_others
job_id_careerbuilder
job_link]
header_links = %w[id link]
@id = 0
page_url = 'https://careerbuilder.vn/viec-lam/tat-ca-viec-lam-vi.html'
# STEP 1: get link list
CSV.open('links.csv', 'w', write_headers: true, headers: header_links) do |csv|
loop do
page = Nokogiri::HTML(open(page_url))
page.css('div.gird_standard > dl > dd.brief').each do |row|
link = row.at_css('span:nth-child(2) > h3.job > a').attributes['href'].value
puts link
puts @id
csv << [@id, link]
@id += 1
end
@btn_res = page.css('div.paginationTwoStatus > a.right')
break unless @btn_res.any? && @id < 1100
page_url = @btn_res[0].attributes['href'].value
end
# TODO:
# check if total job # IDsum because sometime server is dump
end
# STEP 2: go to get data for each link:
CSV.open('jobs.csv', 'wb', write_headers: true, headers: headers) do |jobsfile|
CSV.foreach('links.csv', headers: true) do |link_row|
data_arr = []
@url = normalize_uri(link_row['link']).to_s
next if $r.sismember('visited', @url)
page = Nokogiri::HTML(open(@url))
page.css('div.LeftJobCB').each do |object|
salary = if object.xpath('//*[@id="showScroll"]/ul/li[2]/p[2]/span').text.strip == 'Lương:'
object.xpath('//*[@id="showScroll"]/ul/li[2]/p[2]/label')
.map(&:children).reject(&:nil?).reject(&:empty?).join(' - ')
elsif object.xpath('//*[@id="showScroll"]/ul/li[1]/p[2]/span').text.strip == 'Lương:'
object.xpath('//*[@id="showScroll"]/ul/li[1]/p[2]/label')
.map(&:children).reject(&:nil?).reject(&:empty?).join(' - ')
else
'Cạnh tranh'
end
industry = if object.xpath('//*[@id="showScroll"]/ul/li[3]/p[1]/span').text.strip == 'Ngành nghề:'
object.xpath('//*[@id="showScroll"]/ul/li[3]/p[1]/b/a/text()').text.tr(',', '+')
elsif object.xpath('//*[@id="showScroll"]/ul/li[2]/p[1]/span').text.strip == 'Ngành nghề:'
object.xpath('//*[@id="showScroll"]/ul/li[2]/p[1]/b/a/text()').text.tr(',', '+')
else
'Khác'
end
job_des_select = object.css('div[class = MarBot20]')
description = if job_des_select.count == 3
object.css('div:nth-child(5) > div.content_fck').text.gsub(/^(.{500,}?).*$/m, '\1...')
elsif job_des_select.count == 4
object.css('div:nth-child(6) > div.content_fck').text.gsub(/^(.{500,}?).*$/m, '\1...')
else
'NA'
end
x = object.at_css('div[class = MarBot20]')
puts "#{link_row['id']} -- #{description.gsub(/^(.{10,}?).*$/m, '\1...')}"
binding.pry
data_arr =
[ # [0] id
link_row['id'],
# [1] title
object.at_css('div.top-job > div.top-job-info > h1').inner_text,
# [2] company_name
object.css(' div.box1Detail > p.TitleDetailNew > span').text,
# [3] city
object.xpath('//*[@id="showScroll"]/ul/li[1]/p[1]/b/a').text,
# [4] year_experience
'',
# [5] industries
industry,
# [6] position
'',
# [7] salary
salary,
# [8] update_date
'',
# [9] exp_time
'',
# [10] description
description,
# [11] job_requirements
'',
# [12] job_others
'',
# [13] job_id_careerbuilder
'',
# [14] job_link
@url
]
$r.sadd('crawled', YAML.dump(data_arr))
$r.sadd('visited', @url)
end
end
end
end
def crawler_city
page_url = 'https://careerbuilder.vn/viec-lam/tat-ca-viec-lam-vi.html'
page = Nokogiri::HTML(open(page_url))
cities = page.xpath('//*[@id="location"]')
cities[0].children.each do |city, _id|
code = city['value'].scan(/\s?\-?\d+/).first
slug = code == '-1' ? 'tat-ca-dia-diem' : city['value'].scan(/[[a-z]|\-]+/).first
name = city.children.first.content.strip
$r.sadd('cities', YAML.dump([code, name, slug]))
end
end
def crawler_industry
page_url = 'https://careerbuilder.vn/viec-lam/tat-ca-viec-lam-vi.html'
page = Nokogiri::HTML(open(page_url))
inds = page.xpath('//*[@id="industry"]')
inds[0].children.each do |ind|
code = ind['value'].scan(/\s?\-?\d+/).first
slug = code == '-1' ? 'tat-ca-nganh-nghe' : ind['value'].scan(/[[a-z]|\-]+/).first
name = ind.children.first.content
$r.sadd('industry', YAML.dump([code, name, slug]))
end
end
# convert to uri standard if any non ASCII character
def normalize_uri(uri)
return uri if uri.is_a? URI
uri = uri.to_s
uri, *tail = uri.rpartition '#' if uri['#']
URI(URI.encode(uri) << Array(tail).join)
end
require 'rails_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
#
# Also compared to earlier versions of this generator, there are no longer any
# expectations of assigns and templates rendered. These features have been
# removed from Rails core in Rails 5, but can be added back in via the
# `rails-controller-testing` gem.
RSpec.describe AdminsController, type: :controller do
# This should return the minimal set of attributes required to create a valid
# Admin. As you add validations to Admin, be sure to
# adjust the attributes here as well.
let(:valid_attributes) {
skip("Add a hash of attributes valid for your model")
}
let(:invalid_attributes) {
skip("Add a hash of attributes invalid for your model")
}
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# AdminsController. Be sure to keep this updated too.
let(:valid_session) { {} }
describe "GET #index" do
it "returns a success response" do
admin = Admin.create! valid_attributes
get :index, params: {}, session: valid_session
expect(response).to be_success
end
end
describe "GET #show" do
it "returns a success response" do
admin = Admin.create! valid_attributes
get :show, params: {id: admin.to_param}, session: valid_session
expect(response).to be_success
end
end
describe "GET #new" do
it "returns a success response" do
get :new, params: {}, session: valid_session
expect(response).to be_success
end
end
describe "GET #edit" do
it "returns a success response" do
admin = Admin.create! valid_attributes
get :edit, params: {id: admin.to_param}, session: valid_session
expect(response).to be_success
end
end
describe "POST #create" do
context "with valid params" do
it "creates a new Admin" do
expect {
post :create, params: {admin: valid_attributes}, session: valid_session
}.to change(Admin, :count).by(1)
end
it "redirects to the created admin" do
post :create, params: {admin: valid_attributes}, session: valid_session
expect(response).to redirect_to(Admin.last)
end
end
context "with invalid params" do
it "returns a success response (i.e. to display the 'new' template)" do
post :create, params: {admin: invalid_attributes}, session: valid_session
expect(response).to be_success
end
end
end
describe "PUT #update" do
context "with valid params" do
let(:new_attributes) {
skip("Add a hash of attributes valid for your model")
}
it "updates the requested admin" do
admin = Admin.create! valid_attributes
put :update, params: {id: admin.to_param, admin: new_attributes}, session: valid_session
admin.reload
skip("Add assertions for updated state")
end
it "redirects to the admin" do
admin = Admin.create! valid_attributes
put :update, params: {id: admin.to_param, admin: valid_attributes}, session: valid_session
expect(response).to redirect_to(admin)
end
end
context "with invalid params" do
it "returns a success response (i.e. to display the 'edit' template)" do
admin = Admin.create! valid_attributes
put :update, params: {id: admin.to_param, admin: invalid_attributes}, session: valid_session
expect(response).to be_success
end
end
end
describe "DELETE #destroy" do
it "destroys the requested admin" do
admin = Admin.create! valid_attributes
expect {
delete :destroy, params: {id: admin.to_param}, session: valid_session
}.to change(Admin, :count).by(-1)
end
it "redirects to the admins list" do
admin = Admin.create! valid_attributes
delete :destroy, params: {id: admin.to_param}, session: valid_session
expect(response).to redirect_to(admins_url)
end
end
end
FactoryGirl.define do
factory :admin do
admin_id "MyString"
password_digest "MyString"
end
end
require 'rails_helper'
# Specs in this file have access to a helper object that includes
# the AdminsHelper. For example:
#
# describe AdminsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
RSpec.describe AdminsHelper, type: :helper do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'rails_helper'
RSpec.describe Admin, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'rails_helper'
RSpec.describe "Admins", type: :request do
describe "GET /admins" do
it "works! (now write some real specs)" do
get admins_path
expect(response).to have_http_status(200)
end
end
end
require "rails_helper"
RSpec.describe AdminsController, type: :routing do
describe "routing" do
it "routes to #index" do
expect(:get => "/admins").to route_to("admins#index")
end
it "routes to #new" do
expect(:get => "/admins/new").to route_to("admins#new")
end
it "routes to #show" do
expect(:get => "/admins/1").to route_to("admins#show", :id => "1")
end
it "routes to #edit" do
expect(:get => "/admins/1/edit").to route_to("admins#edit", :id => "1")
end
it "routes to #create" do
expect(:post => "/admins").to route_to("admins#create")
end
it "routes to #update via PUT" do
expect(:put => "/admins/1").to route_to("admins#update", :id => "1")
end
it "routes to #update via PATCH" do
expect(:patch => "/admins/1").to route_to("admins#update", :id => "1")
end
it "routes to #destroy" do
expect(:delete => "/admins/1").to route_to("admins#destroy", :id => "1")
end
end
end
require 'rails_helper'
RSpec.describe "admins/edit", type: :view do
before(:each) do
@admin = assign(:admin, Admin.create!(
:admin_id => "MyString",
:password_digest => "MyString"
))
end
it "renders the edit admin form" do
render
assert_select "form[action=?][method=?]", admin_path(@admin), "post" do
assert_select "input[name=?]", "admin[admin_id]"
assert_select "input[name=?]", "admin[password_digest]"
end
end
end
require 'rails_helper'
RSpec.describe "admins/index", type: :view do
before(:each) do
assign(:admins, [
Admin.create!(
:admin_id => "Admin",
:password_digest => "Password Digest"
),
Admin.create!(
:admin_id => "Admin",
:password_digest => "Password Digest"
)
])
end
it "renders a list of admins" do
render
assert_select "tr>td", :text => "Admin".to_s, :count => 2
assert_select "tr>td", :text => "Password Digest".to_s, :count => 2
end
end
require 'rails_helper'
RSpec.describe "admins/new", type: :view do
before(:each) do
assign(:admin, Admin.new(
:admin_id => "MyString",
:password_digest => "MyString"
))
end
it "renders new admin form" do
render
assert_select "form[action=?][method=?]", admins_path, "post" do
assert_select "input[name=?]", "admin[admin_id]"
assert_select "input[name=?]", "admin[password_digest]"
end
end
end
require 'rails_helper'
RSpec.describe "admins/show", type: :view do
before(:each) do
@admin = assign(:admin, Admin.create!(
:admin_id => "Admin",
:password_digest => "Password Digest"
))
end
it "renders attributes in <p>" do
render
expect(rendered).to match(/Admin/)
expect(rendered).to match(/Password Digest/)
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment