2018年12月9日 星期日

[css/scss] 自適寬度的圖片(Responsive Image)-img隨div大小改變自行縮放


將同一張圖片不切割就能用不同的尺寸顯示,效果預覽如下:


這是利用照片當作背景,並以圖片的短邊為依據,不經剪裁就能以需要的比例呈現,利用css對背景圖片設定的支援功能,設定像是background-position,如果圖片過大時從哪個位置開始進行剪裁,以下為scss撰寫的原始碼。

首先,先準備一個Parent Class,『%』後面加上名稱,在編譯成css後不會看到這個class。
%responsive-image {
 display: flex;
 width: 100%;
 flex: 100%;
 background-size: cover;
 background-repeat: no-repeat;
 background-position: center center;
}

接著設定一個image-1x1的class,使所有的圖片都能以1比1的比例顯示。
// H:1, W:1
.image-1x1{
 @extend %responsive-image;
 padding-bottom: percentage(1 / 1);
}
以此類推,如果需要顯示一個長300、寬400的圖片,請在括號內填入『(3/4)』,就能達到想要的效果!

在html裡面套用這個class。
<div class="image-1x1" style="background-image: url('https://picsum.photos/400/?image=318');"></div>


我搭配bootstrap的grid system寫了一個完整的範例,可以在codepen上找到[連結],請參考:

See the Pen Responsive Image by chenuin (@chenuin) on CodePen.



[css/scss] 水平時間軸(Horizontal Timeline)範例及原始碼分享


水平時間軸


為了方便客製化,這次使用scss來寫,並把幾個重要的參數拉出來寫,其中比較重要的是$event-num,如果有4個項目的話,會用全部的長度均分,因此請依照自己實際使用去修改這個參數。

style.scss
@import url('https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css');

$line-color: #FA8072;  // 水平線的顏色
$point-color: #FF4500; // 圓點的顏色
$point-size: 16px;     // 圓點的大小(直徑) 
$half-point-size: $point-size/2;
$active-font-color: #FA8072;
$inactive-font-color: rgba(0, 0, 0, 0.5);
$event-num: 4;  // 圓點(項目)數量

#timeline {
  ol {
    position: relative;
    display: block;
    margin-top: 50px;
    margin-bottom: 100px;
    height: 1px;
    padding-inline-start: 0;
  }

  li {
    position: relative;
    display: inline-block;
    float: left;
    width: calc(100% /  #{$event-num});
    height: 1px;
    background: $line-color;
    color: $inactive-font-color;
    .diplome {
      text-align: center;
      margin-top: 20px;
    }
    .point {
      content: "";
      display: block;
      width: $point-size;
      height: $point-size;
      border-radius: 50%;
      border: 1px solid $point-color;
      background: #fff;
      position: absolute;
      top: -#{$half-point-size};
      left: calc(50% - #{$half-point-size});
    }
    .timestamp {
      font-size: 14px;
      text-align: center;
    }
    &.active>.point {
      border: $half-point-size solid $point-color;
    }
    &.active>.diplome,
    &.active>.timestamp {
      color: $active-font-color;
    }
  }
}

如果習慣看css的人,可以利用一些線上的轉換網站(如:sassmeister)轉換成css。

index.html
<div class="container">
  <div id="timeline">
    <ol>
      <li class="active">
        <span class="point"></span>
        <h6 class="diplome">My Birthday</h6>
        <p class="timestamp">2018/06/01</p>
      </li>
      <li class="active">
        <span class="point"></span>
        <h6 class="diplome">Father's Day</h6>
        <p class="timestamp">2018/08/08</p>
      </li>
      <li>
        <span class="point"></span>
        <h6 class="diplome">Helloween</h6>
        <p class="timestamp"></p>
      </li>
      <li>
        <span class="point"></span>
        <h6 class="diplome">Christmas</h6>
        <p class="timestamp"></p>
      </li>
    </ol>
  </div>
</div>


原始碼放在codepen上[連結],歡迎參考:

See the Pen Horizontal Timeline by chenuin (@chenuin) on CodePen.


2018年11月18日 星期日

[Github] 用Django專案示範如何使用Travis CI自動測試


開始之前請先到Travis CI[官網]用Github帳號登入
同步github上的專案,並啟動需要測試的專案(Repository)!

請在根目錄新建檔案.travis.yml
使用語言為python,並用3.5和3.6版本。
language: python
python:
  - "3.5"
  - "3.6"

env則是設定這個腳本中的參數,後面install則是安裝這個專案所需的套件。
env:
  - DJANGO_VERSION=2.1
  - DJANGO_VERSION=2.1.2
# command to install dependencies
install:
  - pip install -q Django==$DJANGO_VERSION
  - pip install -r requirements.txt

針對這個專案我寫了一段測試(連結),script就是執行裡面預先寫好的元件測試。
script: python manage.py test

上傳Github後,可以看到測試的結果,我們分別設定了2個版本的python、2個版本Django,所以會產生4個執行結果,就不需要一一下載各個版本來進行測試囉!

如果測試成功會顯示passing,若失敗會顯示failing,我在readme.md加上的標誌方便知道結果,新增方法請參考下面:


完整檔案如下(連結):
# .travis.yml
language: python
python:
  - "3.5"
  - "3.6"
env:
  - DJANGO_VERSION=2.1
  - DJANGO_VERSION=2.1.2
# command to install dependencies
install:
  - pip install -q Django==$DJANGO_VERSION
  - pip install -r requirements.txt
# command to run tests
script: python manage.py test

完整專案請到Github(連結)下載。

2018年11月16日 星期五

[Symfony] Ubuntu16+PHP7 安裝Synfony3.4


這次作業軟體是Ubuntu16.04,安裝的php是7.0。

首先請下載symfony指令,並移到指定資料夾方便全域使用。
sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony
sudo chmod a+x /usr/local/bin/symfony

接著馬上就能新增一個專案,my_project可以替換成想要的專案名稱。
# create a symfony project
symfony new my_project 3.4

我第一次建完專案出現訊息提示"simplexml_import_dom() must be available",請指令安裝php-xml排除這個問題。
如果想確定環境是否符合symfony的執行需求,可用下面指令:
php my_project/bin/symfony_requirements

如果看到這樣的畫面,基本上就沒問題,下面還另外有一些建議,可以選擇性安裝!


將Symfony專案啟動的方式
# run application
cd my_project
php bin/console server:run

打開網頁 http://localhost:8080可以看到預設Symfony的頁面,收工囉~


2018年11月13日 星期二

[Boostrap] 透過scss自訂個人風格的主題(Theme)


Bootstrap將常用的Navbar、Button等元件定義好css
可以讓前端的開發更加快速,是最多人使用的前端開發套件!

但有時boostrap的設定不合意要怎麼辦呢?
而且這麼多人使用bootstrap是不是有失個人風格呢?
這時你可以下載scss的版本來定義屬於自己的bootstrap模板喔!

請新建一個檔案加入bootstrap,第一種方法是把所有的元件都引用:
// Custom.scss
// Option A: Include all of Bootstrap

@import "node_modules/bootstrap/scss/bootstrap";

或者有時你只需要使用bootstrap部分的元件
// Custom.scss
// Option B: Include parts of Bootstrap

// Required
@import "node_modules/bootstrap/scss/functions";
@import "node_modules/bootstrap/scss/variables";
@import "node_modules/bootstrap/scss/mixins";

// Optional
@import "node_modules/bootstrap/scss/reboot";
@import "node_modules/bootstrap/scss/type";
@import "node_modules/bootstrap/scss/images";
@import "node_modules/bootstrap/scss/code";
@import "node_modules/bootstrap/scss/grid";
以上二選一即可,另外還提供了bootstrap-grid、bootstrap-reboot兩種常用的部分元件集,像bootstrap-grid就是針對網頁排版的所有bootstrap元件集,在RWD的網站時非常便利。

接著,進入正題!例如btn-primary、text-primary等都是藍色(#007bff),如果要自訂為粉紅色(#e83e8c),請將重新定義的顏色寫在引用bootstrap之前,檔案內容如下:
// Custom.scss
$primary: #e83e8c;

@import "node_modules/bootstrap/scss/bootstrap";
所有的primary就會變成你定義的粉紅色(#e83e8c),基本上所有的參數都能在_variables.scss找到原始的設定,只要加上你需要修改的參數,就可以輕鬆打造屬於你的bootstrap主題囉!

參考資料:
https://getbootstrap.com/docs/4.0/getting-started/theming/

2018年11月2日 星期五

[Javascript] 用Javascirpt截圖的小幫手 html2canvas


請先到html2canvas的官網下載套件
https://html2canvas.hertzen.com/

在想要擷取的畫面加上一個id方便來獲取這個元件,這邊設成capture。
<div id="capture"> ... </div>

這時候就可以擷取儲存成canvas,這邊是直接將這個畫面加到網頁的結尾。
html2canvas(document.querySelector("#capture")).then(canvas => {
    // do something
    document.body.appendChild(canvas)
});
querySelector這邊請記得針對你要擷取的元件設定!

完整的範例如下:
<html>
 <head>
<html>
 <head>
  <title>Screenshots with JavaScript</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" >
 </head>

 <body>
  <div class="container">
   <div class="row">
    <div class="col my-5">
     <!-- The Screenshot Component -->
     <div class="target">
      <div class="card text-center">
       <div class="card-header">
        Featured
       </div>
       <div class="card-body">
        <h5 class="card-title">Special title treatment</h5>
        <p class="card-text">With supporting text below as a natural lead-in to additiona    l content.</p>
        <a href="#" class="btn btn-primary">Go somewhere</a>
       </div>
       <div class="card-footer text-muted">
        2 days ago
       </div>
      </div>
     </div>

     <!-- Display Area -->
     <div class="mt-5 result border border-success">
      <p class="text-center">Display Here!</p>
     </div>

     <!-- Download Link -->
     <a herf="#" class="download-link btn">Download</a>
    </div>
   </div>
  </div>
  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
  <script type="text/javascript" src="./html2canvas.min.js"></script>
  <script>
  $(document).ready(function() {
   html2canvas(document.querySelector(".target")).then(canvas => {
     $(".result").append(canvas);
     $(".download-link").attr("href", canvas.toDataURL());
   });
  });
  </script>
 </body>

</html>

2018年10月30日 星期二

[Vue.js] 使用美化頁面的小幫手Bootstrap


Bootstrap是最常用的前端開發套件,以下介紹在Vue.js的專案內如何使用。

除了一般直接下載原始碼並在<head>加入,Bootstrap其實已經提供Vue.js使用的套件。
以下使用webpack產生的專案來說明,先創立一個新的專案,並依造下面步驟安裝。
# install related packages
npm install jquery
npm install ajv

# install boostrap-vue
npm i bootstrap-vue
創立專案的方式可參考『安裝 Vue.js』方法二。

請依照你的需求在頁面加入css檔,通常我會放在main.js,這樣所有的頁面都能使用。
# src/main.js
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
此時已經可以用平常的方法套用bootstrap。

不過這麼一來就跟下載原始碼沒什麼兩樣了,我們還沒發揮BootstrapVue plugin的好處,接著在main.js裡註冊bootstrap的元件。
# src/main.js
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'

Vue.use(BootstrapVue);

這時侯如果要在頁面裡加入一個button,你只要這樣寫:
# src/App.vue
<template>
    <div id="app">
        <b-button variant="primary">I am a Button</b-button>
    </div>
</template>

...

比較一下原始的寫法,是不是方便多了呢!
# src/App.vue
<template>
    <div id="app">
        <button class="btn btn-primary">I am a Button</button>
    </div>
</template>

...

另外,你也可以根據需求只載入需要的元件(Component),例如只加入按鈕的元件。
import { Button } from 'bootstrap-vue/es/components';
Vue.use(Button);

import bButton from 'bootstrap-vue/es/components/button/button';
Vue.component('b-button', bButton);

更多資訊可參閱https://bootstrap-vue.js.org/

2018年10月29日 星期一

[Django] 內建管理系統(Admin)使用技巧


Django內建的管理系統,就類似phpmyadmin一樣,可以用網頁的方式來管理資料庫,請先確定urls.py內已經加入這個路徑。
# project/urls.py
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

請先執行migrate,建立一個後臺使用者的資料表,再新增一個使用者來登入後台介面。
# apply admin models
python manage.py migrate

# create a user to login as admin
python manage.py createsuperuser

打開瀏覽器'http://your-ip-address/admin'就可以看到管理介面的登入畫面,請輸入剛剛設定的帳號密碼。

首先,建立一個models紀錄產品(Product)的資訊:
# myapp.models.py
class Product(models.Model):
    name = models.CharField(max_length=128)
    price = models.DecimalField(max_digits = 11, decimal_places = 2)
    count = models.IntegerField()
    created = models.DateTimeField(auto_now_add=True)
請執行以下指令,讓這個models生效,makemigrations的目的是將models.py的內容轉成sql,若models.py有任何異動,可以透過migrations針對修改的部分進行更新。migrate則是執行sql,也就是實際建立新的table。
python manage.py makemigrations
python manage.py migrate

執行結果請參考

方法一、
最簡單的使用方式,就是在admin.py註冊這個models。
# myapp/admin.py
from django.contrib import admin
from .models import Product

admin.site.register(Product)

此時,管理介面會多一個你註冊資料表Product。另外,上面的User則是一開始新增的superuser儲存的資料表。

新增資料後,預設的畫面如下,括號內的數字是自動遞增的ID,必須點開才能知道其他資訊,不方便管理。

方法二、
同方法一在admin.py內註冊models,此外我們重新定義回傳值,不再顯示流水編號,舉例我們能回傳產品名稱(name)。
# myapp.models.py
class Product(models.Model):
    name = models.CharField(max_length=128)
    price = models.DecimalField(max_digits = 11, decimal_places = 2)
    count = models.IntegerField()
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        # value to display
        return str(self.name)

效果如下:


方法三、
# myapp/admin.py
from django.contrib import admin
from .models import Product

class ProductAdmin(admin.ModelAdmin):
    # fields to display
    list_display = ('id', 'name', 'price', 'created')

admin.site.register(Product, ProductAdmin)

效果如下:


進階使用

[Django] 如何設定static files(css, javascript, images)


請確定設定檔內的參數,通常已經預設寫入。
# project/settings.py
INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
]

STATIC_URL = '/static/'
接下來,分此兩種情形:

(1) 開發階段 (DEBUG=True)
首先,在開發階段時,因為static files經常性的修改,因此先讀取STATICFILES_DIRS的設定。
# project/settings.py
DEBUG = True

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
    # another directory ...
]


建立一個新的static資料夾,在裡面依照習慣將檔案分類放好,可以參考一下我的分類方式:

當在templates中要使用static資料夾內的資源,請記得加入tag。
# templates/index.html
{% load static %}
<img src="{% static 'images/example.png' %}" alt="My image" />


(2) 佈建正式環境 (DEBUG=False)
在正式環境時,請加入STATIC_ROOT的設定,請注意STATIC_ROOT與STATICFILES_DIRS的名稱不能重複!只要Debug設為False,一定要加入這個設定,否則網頁無法正常開啟。
# project/settings.py
DEBUG = False

STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

接著,請執行指令,將static裡面的資料輸出到新的資料夾staticfiles內。
python manage.py collectstatic
此時,觀察一下兩個資料夾差別,會發現staticfiles內除了我們自己新增的images,Django內建的admin或其他套件所需的資源,都會一並新增至此。但因為js和css兩個資料夾是空的,所以載入時會被忽略。



若之後static資料夾內有任何修改,必須重複執行collectstatic的指令來更新。


【重點整理】比較三個相似參數的差別
1. STATIC_URL:
設定static files的URL前綴,按照設定的前綴+路徑、檔名產生URL,以本文example.png為例,STATIC_URL='/static/',因此透過URL 'http://your-ip-address/static/images/example.png'檢視example.png這個檔案。

2. STATICFILES_DIRS:
開發階段,static files存放的位置,可以寫入多個路徑。

3. STATIC_ROOT:
正式環境中,透過collectstatic指令,將STATICFILES_DIRS設定目錄下檔案,收錄到STATIC_ROOT指定目錄。

2018年10月4日 星期四

[Django] 支援多國語言的網站

internationalization(國際化)被簡寫為i18n,因為字首i和字尾n的中間共有18個字母。

安裝相關軟體
sudo apt-get install gettext

修改settings.py,在MIDDLEWARE加上所需的套件,LANGUAGES列出支援哪些語言,LOCALE_PATHS則是將語言的翻譯包儲存的路徑。
# project/settings.py
MIDDLEWARE = [
    ...,

    'django.middleware.locale.LocaleMiddleware',
]


LANGUAGES = (
    ('zh-tw', '繁體中文'),
    ('en', 'English'),
)
LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)

新增一個html的模板,記得加入i18n的tag,頁面內想翻譯的字詞標註起來。另外在url的設定裡,將所有頁面區分成多個語言,也就是原先的路徑http://your.ip.address/welcome,可由http://your.ip.address/en/welcome來檢視英文版網站,中文網站則是打開http://your.ip.address/zh-tw/welcome。
# templates/index.html
{% load i18n %}

{% trans '一起唱DoReMi' %}
設定url
# myapp/views.py
def index(request):
    return render(request, 'index.html', {})

# project/urls.py
from myapp import views
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns(
    path('', views.index, name='index'),
)

標註好想翻譯的字詞或段落後,請透過下列指令產生翻譯文件。
python manage.py makemessages -l en
python manage.py makemessages -l zh_TW

此時在locale資料夾內,會找到產生的檔案,msgid是你標註的字詞,請在msgstr寫入翻譯。針對重複的id,在文件內只能設定一種翻譯。
# locale\en\LC_MESSAGES\django.po
msgid "一起唱DoReMi"
msgstr "singing DoReMi together"
# locale\zh_TW\LC_MESSAGES\django.po
msgid "一起唱DoReMi"
msgstr "一起唱DoReMi"

翻譯完成後,將翻譯包編譯出*.mo
python manage.py compilemessages -l en
python manage.py compilemessages -l zh_TW
可以參考一下完成編譯的檔案結構
locale/
├── en
│   └── LC_MESSAGES
│       ├── django.mo
│       └── django.po
└── zh_TW
    └── LC_MESSAGES
        ├── django.mo
        └── django.po

4 directories, 4 files

這時就可以開啟瀏覽器查看成果囉!
http://your.ip.address/en/
http://your.ip.address/zh-tw/

2018年9月28日 星期五

[Django] 如何將Django專案與MariaDB、mysql連線

一般Django專案會使用內建的sqlite3當作資料庫
只要安裝相關套件 Django也是可以支援mariaDB、mysql

一、環境建置
首先安裝需要的軟體,mysql和mariadb二擇一即可。
sudo apt-get update
# mysql
sudo apt-get install mysqldb-server libmysqlclient-dev
# mariadb
sudo apt-get install mariadb-server libmariadbclient-dev

sudo apt-get install python-pip python-dev
pip install mysqlclient

登入資料庫
sudo mysql
新增一個資料庫和使用者,並將資料庫的存取權限授權給使用者。
> CREATE DATABASE `djangoDB`;
> CREATE USER 'myuser' IDENTIFIED BY 'mypassword';
> GRANT ALL privileges ON `djangoDB`.* TO 'myuser'@localhost;
> FLUSH PRIVILEGES;'

完成設定後,指令exit即可離開資料庫
用新使用者的身分登入試試看設定是否正確
-p代表要輸入密碼(不加會跳error)
mysql -u myuser -p


二、Django專案設定
請記得剛剛設定的資料庫名稱和使用者帳密
打開settings.py將原本設定的sqlite3修改成mysql
# project/settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djangoDB', # DB名稱
        'USER': 'myuser', # 使用者帳號
        'PASSWORD': 'mypassword', # 使用者密碼
        'HOST': 'localhost',
        'PORT': '3306',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
        },
    }
}

option如果沒有設定的話,執行migrate會出現警告 相關資料請見 https://docs.djangoproject.com/en/2.1/ref/databases/#mysql-sql-mode


三、設定生效
設定完成後就可以執行migrate
python manage.py migrate
這時就可以打開資料庫確認table有沒有建好,就大功告成囉!

[Django] 如何使外來鍵指向多個模型


[本文翻譯自Bhrigu Srivastava-Django: How to add ForeignKey to multiple models]
https://medium.com/@bhrigu/django-how-to-add-foreignkey-to-multiple-models-394596f06e84

如何用一個外來鍵(Foreign Key)和1種以上的模型(Model)進行關聯。

先假設你有兩個模型,Article和Post
class Article(models.Model):
    content = models.CharField(max_length=100)
class Post(models.Model):
    content = models.CharField(max_length=100)

現在,我們新增一個Comment的模型,且Article和Post皆與此關聯。因此,我們要如何在Comment中新增一個FK,並指向上述模型中其一。

能達到這功能的概念即為通用關係(Generic Relation),Django包含一種contenttypes的應用,你的模型和content type模型在應用中的關係,能使一個模型物件與任何你建立的模型物件之間啟用通用關係,

Comment模型的格式如下:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class Comment(models.Model):
    comm = models.CharField(max_length=50)
    content_type =   models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object=GenericForeignKey('content_type', 'object_id')
(建立Generic Relations時,固定加上最後三行)

ContentType是一種模型,一個ContentType的物件儲存了關於專案中的模型資訊,提供方法來回傳他們代表的模型或進行搜尋。

因此,我們能將Comment與任何種類的模型建立關聯,儲存在content_type。(content_type回傳模型種類,object_id回傳此模型物件的id)

接下來,我們新增一個Comment的物件(instance),並將此指向一個Articel物件或Post物件。

將Comment物件指向Articel物件:
>>> art = Article.objects.get(id=1)
>>> c = Comment(content_object=art, comm='asdf')
>>> c.save()
>>> c.content_object
<Post: post1>
將Comment物件指向Post物件:
>>> pos= Post.objects.get(id=1)
>>> c= Comment(content_object=pos, comm='new comment')
>>> c.save()
>>> c.content_object
<Post: post1>

逆向Generic Relations
再來,為了取得所有與Article和Post關聯的Comment物件,我們能利用GenericRelations來達成。在Article和Post中定義一個新的欄位,在你的模型中新增一個欄位來逆向搜尋。
from django.contrib.contenttypes.fields import GenericRelation

class Article(models.Model):
    content = models.CharField(max_length=100)
    comments = GenericRelation(Comment)
class Post(models.Model):
    content = models.CharField(max_length=100)
    comments = GenericRelation(Comment)

接下來,指令model_object.comments.all()會取得所有指向這個Comment物件,舉例:art是一個Article物件;post是一個Post物件。
>>> art.comments.all()
<QuerySet [<Comment: asdf>, <Comment: test>]>
>>> pos.comments.all()
<QuerySet [<Comment: new_comment>, <Comment: test2>]>
以上就是「物件指向多個模型」你所有需要知道的基本使用方式。

請參考The contenttypes framework,能知道更多關於contenttypes的應用。

2018年9月27日 星期四

[Python] 用Selenium訂台鐵車票

首先說明Selenium如何抓到指定的欄位
假設有個HTML原始碼
<input id="my_name" type="text" name="fname"/>
代表可以透過id my_name來填入資料,寫成:
input = browser.find_element_by_xpath("//input[@id='my_name']")
input.send_keys('Amy')

就可以在指定的欄位中填入你的名字
但如果剛好網站不像這樣這麼單純的話
還有一個簡單的方法可以取得xpath

檢視原始碼找到元件,點原始碼按右鍵→Copy→Copy XPath
再到程式碼中填上就可以了


訂購車票程式範例
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException, WebDriverException
import time

if __name__ == '__main__':
    try:
        browser = webdriver.Chrome()
        browser.get('http://railway.hinet.net/Foreign/TW/etno1.html')

        person_id = browser.find_element_by_xpath("//input[@id='person_id']")
        person_id.send_keys('A123456789') # 身分證字號

        date = browser.find_element_by_xpath("//select[@id='getin_date']")
        date.send_keys('2018/09/27') # 日期

        from_station = Select(browser.find_element_by_id('from_station'))
        from_station.select_by_value('100') # 起站代碼
        to_station = Select(browser.find_element_by_id("to_station"))
        to_station.select_by_value('149') # 到站代碼

        train_no = browser.find_element_by_xpath("//input[@id='train_no']")
        train_no.send_keys('181') # 車次

        browser.find_element_by_css_selector('button.btn.btn-primary').click()

        rand = browser.find_element_by_xpath("//input[@id='randInput']")
        input_str = input('請輸入圖形中的英數字: ') # [!!] 手動輸入圖形驗證碼
        rand.send_keys(input_str)

        browser.find_element_by_css_selector('button.btn.btn-primary').click()

        print('close brower after 10s...')
        time.sleep(10)
        browser.close()
    except NoSuchElementException as e:
        print(e)
    except WebDriverException as e:
        print(e)
其中幾個參數請根據情形修改
(1) 身分證字號
(2) 乘車日期(格式YYYY/MM/DD)
(3) 起站代碼、到站代碼
(4) 車次 代碼請參考
http://railway.hinet.net/Foreign/TW/etno1.html

圖形驗證碼其實就是防止自動程式搶購
因此這部分需要自己看圖輸入答案才會真正完成訂購
雖然如此能自動填寫資料還是可以節省不少時間
程式未針對資料錯誤進行防呆(像是車次不存在、代碼填錯)
資料正確的情況下都能成功訂購一般車次

如果是普悠瑪的話
因為還要填人數所以不太一樣
需要的話記得要修改一下才適用

2018年8月23日 星期四

[Python] Selenium操作Firefox


安裝selenium
pip install selenium==3.14.0

安裝geckodriver
# 取得合適的版本
wget https://github.com/mozilla/geckodriver/releases/download/v0.21.0/geckodriver-v0.21.0-linux64.tar.gz
# 解壓縮
tar zxvf geckodriver-v0.21.0-linux64.tar.gz
sudo mv geckodriver /usr/bin

設定firefox環境參數
export DISPLAY=:0.0

現在可以來測試selenium效果,打開瀏覽器到google,截圖後存檔。若執行成功應該會在同一目錄下產生google擷取畫面。
from selenium import webdriver
browser = webdriver.Firefox()
browser.get('https://www.google.com')
browser.save_screenshot('screenshot_google.png')
browser.close()

2018年8月13日 星期一

[Vue.js] 在ubuntu安裝 Vue.js

方法一、下載原始碼
# install
weget http://vuejs.org/js/vue.min.js

方法二、npm安裝
npm install vue
npm install --global vue-cli

# create a new project
vue init webpack my-project
執行這個專案
npm install
npm run dev
打開網頁 http://localhost:8080可以看到預設vue的頁面

2018年8月6日 星期一

[django] 將Django專案部署到Heroku

首先安裝相關套件
sudo apt-get install snapd
# 安裝heroku cli(Linunx)
# https://devcenter.heroku.com/articles/heroku-cli#download-and-install
sudo snap install heroku --classic

# 安裝django-heroku
pip install django-heroku
# 安裝gunicorn
pip install gunicorn # 安裝git
sudo apt install git

若成功安裝就可以登入Heroku開始建立專案囉! 尚未有Heroku帳號的,請按註冊Heroku會員。
# 登入Heroku
heroku login

先建立一個空白的專案
mkdir heroku-python
cd heroku-python
# create project
django-admin startproject mysite .
打開mysite/settings.py,先將IP加到ALLOWED_HOSTS,'*'是允許所有的Hostname,比較偷懶的方式。再來加入heroku的套件,套用這個檔案內的設定。
# mysite/settings.py
import django_heroku

ALLOWED_HOSTS = ['*']
...

# end of file
django_heroku.settings(locals())
為了讓Heroku知道專案需要的環境,有兩個必要的檔案Procfilerequirements.txt必須手動加入,請注意Procfile沒有副檔名。
# Procfile
web: gunicorn mysite.wsgi

requirements.txt可以透過pip freeze自動生成,內容可以參考下面。
# requirements.txt
Django==2.1
django-heroku==0.3.1
gunicorn==19.9.0

接著,新增一個Heroku專案,一種由系統自動產生,一種直接在create後面加上你想要的名稱。 之後這個專案網址是[project_name].herokuapp.com,因此如果顯示"Name [project-name] is already taken",可能是你想要的名稱已經先被佔用了。
# 由Heroku自動產生專案名稱
heroku create
# 使用者指定專案名稱
heroku create [project_name]
執行heroku create自動產生的結果,可以參考下面,intense-river-54924就是Heroku隨機產生的專案名稱。
Creating app... done, ⬢ intense-river-54924
https://intense-river-54924.herokuapp.com/ | https://git.heroku.com/intense-river-54924.git
新增一個 git repository
git init
git add .
git commit -m "initial commit"

# 設定遠端伺服器/設定專案的上傳目標
heroku git:remote -a intense-river-54924
# 確認是否設定成功
git remote -v
執行git remote -v的顯示範例
heroku https://git.heroku.com/intense-river-54924.git (fetch)
heroku https://git.heroku.com/intense-river-54924.git (push)

因為staticfiles設定要調整,所以先取消執行collectstatic的部分,才不會有錯誤喔!
# 設定參數
# heroku config:set [key]=[value]
heroku config:set DISABLE_COLLECTSTATIC=1
設定完成後就上傳至遠端Heroku的伺服器
# git push [remote-name] [branch-name]
git push heroku master
執行網站,此時就可以打開瀏覽器查看網頁囉!
heroku ps:scale web=1

專案名稱是intense-river-54924,只要打開https://intense-river-54924.herokuapp.com/,就可以看到結果囉!如果不記得專案名稱,可以透過指令顯示執行網站url。
heroku open


部署到這邊告一段落,底下有些指令可以參考使用,像是有錯誤無法正常顯示網站,可以查看Log來排除錯誤。
heroku logs --tail
heroku logs --tail --app [HEROKU_APP_NAME]
其他Django的指令執行範例,不是本地端而是在遠端伺服器執行。
heroku run python mysite/manage.py migrate
heroku run python mysite/manage.py createsuperuser
在編輯你的專案時,小幅度的修改可以透過以下指令在本地端執行,確定沒有問題時再推到遠端去。
# Procfile is required.
# 效果如同 "python manage.py runserver 0.0.0.0:5000"
heroku local
當你想要執行已有的專案時,可以自遠端伺服器取得專案
heroku git:clone -a [project-name]

參考資料:
https://devcenter.heroku.com/articles/getting-started-with-python

2018年8月5日 星期日

連線VMware VM


在Windows環境下安裝VMware,如果要從windows連到VMware的linux虛擬主機,首先請確定VM->Settings,網路的設定為NAT。


接者請安裝以下套件。
# install
sudo apt-get install ssh
sudo apt-get install openssh-server

查詢指令ifconfig得到VM的IP,即可連線。

2018年7月24日 星期二

安裝node js v8.9.4

前置作業
# install
apt-get update
sudo apt-get install build-essential libssl-dev
curl https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | sh
source ~/.profile
安裝
# install
nvm install v8.9.4
確認安裝版本
# check
node -v

2018年3月21日 星期三

[django] django-widget-tweaks 設定Form widget套件

請擇一方式完成django-widget-tweaks安裝
[方法一] pip
# 利用pip安裝
pip install django-widget-tweaks

[方法二] 原始碼安裝
# 選定安裝的版本,將原始碼下載到當前目錄
wget --no-check-certificate https://github.com/jazzband/django-widget-tweaks/archive/1.4.2.tar.gz
# 解壓縮
tar zvxf 1.4.2.tar.gz
cd django-widget-tweaks-1.4.2/
python setup.py install

完成安裝後,使用套件要在settings.py裡面include
# project/settings.py
INSTALLED_APPS = (
    ...
    'widget_tweaks',
)

在Template裡面可以這樣設定,這邊套用了bootstrap的form style。
# templates/index.html
{% load widget_tweaks %}

<form method="post">
  {% csrf_token %}
  {% for field in form %}<br />
    <div class="form-group">
      <label for="{{ field.id_for_label }}">{{ field.label }} :</label>
      {% render_field field placeholder=field.help_text class+="form-control" %}
    </div>
  {% endfor %}
  <button class="btn btn-primary" type="sumbit">儲存</button>
</form>

其他的運用方式,可以參考 這裡,了解更多!

參考資料:
https://github.com/jazzband/django-widget-tweaks

2018年3月13日 星期二

[HTML] 上傳媒體(圖片、影片、聲音)

透過accept這個attr來設定上傳檔案的類型,以及資料來源,可用手機測試實作效果。
<input accept="video/*;capture=camcorder" />
<input accept="audio/*;capture=microphone" />
<input accept="image/*" /> 限制檔案為影像
<input accept="image/*;capture=camera" /> 限制檔案為影像,資料來源為相機
<input accept="image/*" multiple="" type="file" />


[Python] Virtualenv基本操作

使用pip安裝virtualenv
# install virtualenv
sudo pip install virtualenv
開發Djagno專案,常搭配virtualenv使用,能透過virtualenv創造虛擬環境,在啟動虛擬環境的情況下,pip所安裝的套件只存在虛擬環境中,使得專案可以互相獨立,因此不同的專案可以安裝不同版本的使用套件。

建立新的虛擬環境
# move to your project
cd [myproject]
# add virtualenv
virtualenv [projectenv]

啟動虛擬環境
# active your virtualenv
source [projectenv]/bin/activate
在command line前面出現 (projectenv),代表啟動成功,即可安裝所需的套件。

關閉虛擬環境
# close your virtualenv
deactivate

2018年2月23日 星期五

[Django] 重設admin密碼

如果忘記django user的密碼的重設方式 先開啟 python shell
python manage.py shell
指定要修改的帳號,set_password中填入新密碼
# import User first
from django.contrib.auth.models import User

# reset password
user = User.objects.get(username='root') user.set_password('newpassword')
user.save()
如果忘記帳號的查詢方式
# list superuser
User.objects.filter(is_superuser=True)

# list all user
User.objects.all()
找到帳號後再用剛剛的方式重設密碼即可

2018年1月28日 星期日

[Django] REST framework

首先必須安裝rest framework
# install django rest framework
pip install djangorestframework
在settings.py加上rest_framework
# project/settings.py
INSTALLED_APPS = (
    ...
    'app',
    'rest_framework',
)
準備資料表
# app/models.py
from django.db import models
def Participant():
    name = models.CharField(max_length=20)
    age = models.DecimalField(max_digits=3,decimal_places=0)
    GENDER = (
        ('F', 'Female'),
        ('M', 'Male'),
    )
    gender = models.CharField(max_length=1, choices=GENDER)

初始資料表或更新時,執行makemigrations產生model.py的資料庫語言,執行migrate則會根據這份文件去建立/修改資料表。shell指令則是進入Python的互動模式去操作資料庫。
# active model
python manage.py makemigrations
python manage.py migrate

# execute python shell
python manage.py shell
# (Python Shell)add a new record in Participant
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from app.models import Participant
>>> record = Participant(name='Mike', age='20', gender='M')
>>> record.save()
serializers.py
# app/serializers.py
from rest_framework import serializers
from app.models import Participant

class ParticipantSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Participant
        fields = ('url', 'name', 'age', 'gender')
views.py
# app/views.py
from rest_framework import viewsets
from app.models import Participant
from app.serializers import ParticipantSerializer

class ParticipantViewSet(viewsets.ModelViewSet):
    queryset = Participant.objects.all()
    serializer_class = ParticipantSerializer
url.py
# project/url.py
from django.conf.urls import url, include
from rest_framework import routers
from app.views import ParticipantViewSet

router = routers.DefaultRouter()
router.register(r'participants', ParticipantViewSet)

# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
    ...
    url(r'^api/', include(router.urls)),
    url(r'^api/api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

接著就可以在瀏覽器上確認API有沒有正常運作。

URL- http://www.example.com.tw/api/

URL- http://www.example.com.tw/api/participants/


URL- http://www.example.com.tw/api/participants/1/

我們能夠透過設定權限,防止資料被任意串改,在登入的情況下才能新增/修改/刪除,其他僅能取得資料。
# app/views.py
class ParticipantViewSet(viewsets.ModelViewSet):
    queryset = Participant.objects.all()
    serializer_class = ParticipantSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly)
或是
# app/views.py
class ParticipantViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Participant.objects.all()
    serializer_class = ParticipantSerializer

URL- http://www.example.com.tw/api/api-auth/login/
右上角點選「Log in」,輸入帳密後才有修改的權限。




參考資料:
http://www.django-rest-framework.org/tutorial/quickstart/

[Sublime] SFTP同步遠端資料夾

使用Sublime[官網]可以很輕鬆地和遠端伺服器的資料夾同步,
首先記得下載Sublime SFTP的套件[下載]。

除了到官方網站下載,另外也可以透過sublime的內建指令安裝。
最後搜尋SFTP即可完成安裝!



操作方法如下:
1. 在本地端(也就是你的電腦)新增一個資料夾,我命名為myProject,之後從遠端下載資料後要放的資料夾,打開sublime後,按「Open Folder」,並選擇剛剛新增的資料夾。

選擇的資料夾 myProject,將會出現在底下。


2. 在資料夾名稱點選右鍵,選擇「SFTP/FTP」→「Map to Remote」

此時會自動產生一個 sftp-config.json的檔案,

3. 編輯sftp-config.json,請根據遠端伺服器的資料填寫,也可以選擇使用password或key登入,port若不設定,就是根據連線方式(type)設定成default port number。
{
    "upload_on_save": true,                /* 儲存後自動同步到遠端伺服器*/
    "type": "sftp",                        /* sftp, ftp or ftps */
   
    "host": "example.com",                 /* server */
    "user": "username",                    /* 帳號 */
    //"password": "password",              /* 密碼 */
    //"port": "22",                        /* 埠號 */

    "remote_path": "/example/path/",       /* 遠端伺服器同步資料夾 */

    //"ssh_key_file": "~/.ssh/id_rsa",     /* 使用金鑰 */
}


4. 完成後存檔,並在資料夾名稱按右鍵,選擇「Download Folder」或是「Sync Remote->Local」,底下看到console會開始與遠端伺服器連線,下載檔案。


相關文章:
[Sublime] SFTP參數說明


參考資料:
Sublime Text 加入 FTP 資料夾
[Sublime Text] 使用 SFTP 套件自動同步本地/遠端檔案