Saturday 31 August 2019

python machine learning 2 - Linear Regression

//cmd

(tensor) C:\Users\bob>cd C:\Users\bob\python-machineLearning

(tensor) C:\Users\bob\python-machineLearning>pip install sklearn
Collecting sklearn
  Downloading https://files.pythonhosted.org/packages/1e/7a/dbb3be0ce9bd5c8b7e3d87328e79063f8b263b2b1bfa4774cb1147bfcd3f/sklearn-0.0.tar.gz
Requirement already satisfied: scikit-learn in c:\users\bob\anaconda3\lib\site-packages (from sklearn) (0.21.2)
Requirement already satisfied: joblib>=0.11 in c:\users\bob\anaconda3\lib\site-packages (from scikit-learn->sklearn) (0.13.2)
Requirement already satisfied: scipy>=0.17.0 in c:\users\bob\anaconda3\lib\site-packages (from scikit-learn->sklearn) (1.2.1)
Requirement already satisfied: numpy>=1.11.0 in c:\users\bob\anaconda3\lib\site-packages (from scikit-learn->sklearn) (1.16.4)
Building wheels for collected packages: sklearn
  Building wheel for sklearn (setup.py) ... done
  Stored in directory: C:\Users\bob\AppData\Local\pip\Cache\wheels\76\03\bb\589d421d27431bcd2c6da284d5f2286c8e3b2ea3cf1594c074
Successfully built sklearn
Installing collected packages: sklearn
Successfully installed sklearn-0.0

(tensor) C:\Users\bob\python-machineLearning>pip install pandas
Requirement already satisfied: pandas in c:\users\bob\anaconda3\lib\site-packages (0.24.2)
Requirement already satisfied: python-dateutil>=2.5.0 in c:\users\bob\anaconda3\lib\site-packages (from pandas) (2.8.0)
Requirement already satisfied: numpy>=1.12.0 in c:\users\bob\anaconda3\lib\site-packages (from pandas) (1.16.4)
Requirement already satisfied: pytz>=2011k in c:\users\bob\anaconda3\lib\site-packages (from pandas) (2019.1)
Requirement already satisfied: six>=1.5 in c:\users\bob\appdata\roaming\python\python37\site-packages (from python-dateutil>=2.5.0->pandas) (1.12.0)

(tensor) C:\Users\bob\python-machineLearning>pip install numpy
Requirement already satisfied: numpy in c:\users\bob\anaconda3\lib\site-packages (1.16.4)

-------------------------------------
go to https://archive.ics.uci.edu/ml/datasets/Student+Performance -> click data folder -> student.zip -> extract -> copy student-mat.csv to project directory

#test

import pandas as pd
import numpy as np
import keras
import sklearn
from sklearn import  linear_model
from sklearn.utils import shuffle

data = pd.read_csv("student-mat.csv", sep=";")
#print top 5 of original data
print(data.head())

data = data[['G1','G2','G3','studytime','failures','absences']]
#print top 5 of reduced data fields
print(data.head())

-------------------------------------
#logs
G1 - first period grade (numeric: from 0 to 20)
G2 - second period grade (numeric: from 0 to 20)
G3 - final grade (numeric: from 0 to 20, output target)

  school sex  age address famsize Pstatus  ...  Walc  health absences  G1  G2  G3
0     GP   F   18       U     GT3       A  ...     1       3        6   5   6   6
1     GP   F   17       U     GT3       T  ...     1       3        4   5   5   6
2     GP   F   15       U     LE3       T  ...     3       3       10   7   8  10
3     GP   F   15       U     GT3       T  ...     1       5        2  15  14  15
4     GP   F   16       U     GT3       T  ...     2       5        4   6  10  10

[5 rows x 33 columns]
   G1  G2  G3  studytime  failures  absences
0   5   6   6          2         0         6
1   5   5   6          2         0         4
2   7   8  10          2         3        10
3  15  14  15          3         0         2
4   6  10  10          2         0         4

----------------------------------------

#test continue

#'G3' is the column we are predicting using the rest of the columns
predict = 'G3'

#x is the array of columns in data other than column name 'G3'
x = np.array(data.drop([predict],1))
#y is column 'G3'
y = np.array(data[predict])

#x_test, y_test are the real data value
#x_train, y_train are the 10% of the data set used for prediction, prediction will generate a math model, x_test, y_test are used tor testing the accuracy of the model.
x_train, x_text, y_train, y_test = sklearn.model_selection.train_test_split(x,y,test_size=0.1)

#prediction method
linear = linear_model.LinearRegression()
linear.fit(x_train, y_train)

#prediction success rate
acuracy = linear.score(x_text, y_test)
print(acuracy)

#predicted slope and intercept
print("Co: \n", linear.coef_)
print("Intercept: \n", linear.intercept_)

predictions = linear.predict(x_text)

#print real data set with predicted value
for x in range(len(predictions)):
    print(predictions[x], x_text[x], y_test[x])

---------------------------------------
#logs

#accuracy
0.8131211978854692
Co:
 [ 0.16246545  0.97828472 -0.1944719  -0.26809678  0.03586954]
Intercept:
 -1.5963742429164807

#predicted value, variables for predictions, real value
3.6128014305152814 [6 5 2 1 0] 0
7.035051917962379 [9 8 2 1 0] 0
10.249288008311625 [12 10  2  0 14] 11
13.346607601626296 [13 13  2  0 14] 14
6.985943972123177 [9 8 4 0 2] 8
10.031965729151835 [10 10  2  0 17] 10
9.565661730937075 [10 10  2  0  4] 10
12.746005563221631 [14 12  2  0 20] 13
-1.7828092550568728 [5 0 1 3 0] 0
7.01795042719044 [8 8 3 0 2] 10
11.031342787551868 [13 11  2  0  4] 11
8.424911568454709 [9 9 2 0 4] 10
9.74698670281766 [11 10  2  1 12] 10
5.954256371206496 [ 7  6  2  0 26] 6
12.804573674551168 [11 13  1  1 10] 13
8.69112254723661 [9 9 1 0 6] 10
15.360138913766692 [16 15  2  0  2] 15
5.861771966746274 [ 7  6  1  0 18] 6
14.251395206351592 [14 14  1  0  2] 14
9.331457207222437 [ 9 10  2  0  2] 10
8.32116603673902 [10  9  3  0  2] 9
13.934190478733061 [14 14  3  0  4] 14
9.033239773373271 [10 10  4  0  0] 10
7.212422329323918 [8 8 2 0 2] 8
12.183311149567002 [10 13  4  0  6] 13
11.807429434320646 [11 12  1  0  2] 11
13.150377665450387 [14 13  2  0  4] 13
7.335155154808971 [8 8 1 0 0] 11
6.532622843513421 [ 7  8  2  3 10] 10
15.647095220360393 [16 15  2  0 10] 15
7.110434831650663 [ 8  8  4  0 10] 8
9.208724381737385 [ 9 10  3  0  4] 10
9.422183577640226 [10 10  2  0  0] 10
18.78238940121379 [19 18  2  0  2] 18
-0.9216847973657908 [7 0 1 1 0] 0
9.65626033239987 [10 10  2  1 14] 9
7.967787683346893 [10  8  2  0 14] 9
9.291724585641276 [ 8 10  1  0  0] 11
9.331457207222437 [ 9 10  2  0  2] 9
7.465614146807921 [10  8  2  0  0] 9

--------------------------------------
reference:
https://www.youtube.com/watch?v=1BYu65vLKdA

python machine learning 1

download and install anaconda
https://www.anaconda.com/distribution/

-------------------------------------
//command prompt

Microsoft Windows [Version 10.0.18362.295]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\Users\bob>conda
usage: conda-script.py [-h] [-V] command ...

conda is a tool for managing and deploying applications, environments and packages.

Options:

positional arguments:
  command
    clean        Remove unused packages and caches.
    config       Modify configuration values in .condarc. This is modeled
                 after the git config command. Writes to the user .condarc
                 file (C:\Users\bob\.condarc) by default.
    create       Create a new conda environment from a list of specified
                 packages.
    help         Displays a list of available conda commands and their help
                 strings.
    info         Display information about current conda install.
    init         Initialize conda for shell interaction. [Experimental]
    install      Installs a list of packages into a specified conda
                 environment.
    list         List linked packages in a conda environment.
    package      Low-level conda package utility. (EXPERIMENTAL)
    remove       Remove a list of packages from a specified conda environment.
    uninstall    Alias for conda remove.
    run          Run an executable in a conda environment. [Experimental]
    search       Search for packages and display associated information. The
                 input is a MatchSpec, a query language for conda packages.
                 See examples below.
    update       Updates conda packages to the latest compatible version.
    upgrade      Alias for conda update.

optional arguments:
  -h, --help     Show this help message and exit.
  -V, --version  Show the conda version number and exit.

conda commands available from other packages:
  build
  convert
  debug
  develop
  env
  index
  inspect
  metapackage
  render
  server
  skeleton
  verify

C:\Users\bob>python
Python 3.7.3 (default, Apr 24 2019, 15:29:51) [MSC v.1915 64 bit (AMD64)] :: Anaconda, Inc. on win32

Warning:
This Python interpreter is in a conda environment, but the environment has
not been activated.  Libraries may fail to load.  To activate this environment
please see https://conda.io/activation

Type "help", "copyright", "credits" or "license" for more information.
>>> exit
Use exit() or Ctrl-Z plus Return to exit
>>> exit()

C:\Users\bob>conda create -n tensor
Collecting package metadata (current_repodata.json): done
Solving environment: done


==> WARNING: A newer version of conda exists. <==
  current version: 4.7.10
  latest version: 4.7.11

Please update conda by running

    $ conda update -n base -c defaults conda



## Package Plan ##

  environment location: C:\Users\bob\Anaconda3\envs\tensor



Proceed ([y]/n)? y

Preparing transaction: done
Verifying transaction: done
Executing transaction: done
#
# To activate this environment, use
#
#     $ conda activate tensor
#
# To deactivate an active environment, use
#
#     $ conda deactivate


C:\Users\bob>conda activate tensor

(tensor) C:\Users\bob>pip install tensorflow
Collecting tensorflow
  Downloading https://files.pythonhosted.org/packages/f7/08/25e47a53692c2e0dcd2211a493ddfe9007a5cd92e175d6dffa6169a0b392/tensorflow-1.14.0-cp37-cp37m-win_amd64.whl (68.3MB)
     |████████████████████████████████| 68.3MB 2.2MB/s
Collecting google-pasta>=0.1.6 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/d0/33/376510eb8d6246f3c30545f416b2263eee461e40940c2a4413c711bdf62d/google_pasta-0.1.7-py3-none-any.whl (52kB)
     |████████████████████████████████| 61kB 1.3MB/s
Collecting keras-preprocessing>=1.0.5 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/28/6a/8c1f62c37212d9fc441a7e26736df51ce6f0e38455816445471f10da4f0a/Keras_Preprocessing-1.1.0-py2.py3-none-any.whl (41kB)
     |████████████████████████████████| 51kB 1.1MB/s
Collecting wrapt>=1.11.1 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/23/84/323c2415280bc4fc880ac5050dddfb3c8062c2552b34c2e512eb4aa68f79/wrapt-1.11.2.tar.gz
Collecting astor>=0.6.0 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/d1/4f/950dfae467b384fc96bc6469de25d832534f6b4441033c39f914efd13418/astor-0.8.0-py2.py3-none-any.whl
Collecting grpcio>=1.8.6 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/32/e7/478737fd426798caad32a2abb7cc63ddb4c12908d9e03471dd3c41992b05/grpcio-1.23.0-cp37-cp37m-win_amd64.whl (1.6MB)
     |████████████████████████████████| 1.6MB 6.4MB/s
Requirement already satisfied: six>=1.10.0 in c:\users\bob\appdata\roaming\python\python37\site-packages (from tensorflow) (1.12.0)
Collecting absl-py>=0.7.0 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/3c/0d/7cbf64cac3f93617a2b6b079c0182e4a83a3e7a8964d3b0cc3d9758ba002/absl-py-0.8.0.tar.gz (102kB)
     |████████████████████████████████| 112kB 119kB/s
Collecting protobuf>=3.6.1 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/46/8b/5e77963dac4a944a0c6b198c004fac4c85d7adc54221c288fc6ca9078072/protobuf-3.9.1-cp37-cp37m-win_amd64.whl (1.0MB)
     |████████████████████████████████| 1.0MB 1.3MB/s
Collecting tensorflow-estimator<1.15.0rc0,>=1.14.0rc0 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/3c/d5/21860a5b11caf0678fbc8319341b0ae21a07156911132e0e71bffed0510d/tensorflow_estimator-1.14.0-py2.py3-none-any.whl (488kB)
     |████████████████████████████████| 491kB 6.4MB/s
Collecting tensorboard<1.15.0,>=1.14.0 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/91/2d/2ed263449a078cd9c8a9ba50ebd50123adf1f8cfbea1492f9084169b89d9/tensorboard-1.14.0-py3-none-any.whl (3.1MB)
     |████████████████████████████████| 3.2MB 2.2MB/s
Requirement already satisfied: wheel>=0.26 in c:\users\bob\anaconda3\lib\site-packages (from tensorflow) (0.33.4)
Requirement already satisfied: numpy<2.0,>=1.14.5 in c:\users\bob\anaconda3\lib\site-packages (from tensorflow) (1.16.4)
Collecting gast>=0.2.0 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/4e/35/11749bf99b2d4e3cceb4d55ca22590b0d7c2c62b9de38ac4a4a7f4687421/gast-0.2.2.tar.gz
Collecting termcolor>=1.1.0 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz
Collecting keras-applications>=1.0.6 (from tensorflow)
  Downloading https://files.pythonhosted.org/packages/71/e3/19762fdfc62877ae9102edf6342d71b28fbfd9dea3d2f96a882ce099b03f/Keras_Applications-1.0.8-py3-none-any.whl (50kB)
     |████████████████████████████████| 51kB 3.4MB/s
Requirement already satisfied: setuptools in c:\users\bob\anaconda3\lib\site-packages (from protobuf>=3.6.1->tensorflow) (41.0.1)
Collecting markdown>=2.6.8 (from tensorboard<1.15.0,>=1.14.0->tensorflow)
  Downloading https://files.pythonhosted.org/packages/c0/4e/fd492e91abdc2d2fcb70ef453064d980688762079397f779758e055f6575/Markdown-3.1.1-py2.py3-none-any.whl (87kB)
     |████████████████████████████████| 92kB 1.5MB/s
Requirement already satisfied: werkzeug>=0.11.15 in c:\users\bob\anaconda3\lib\site-packages (from tensorboard<1.15.0,>=1.14.0->tensorflow) (0.15.4)
Requirement already satisfied: h5py in c:\users\bob\anaconda3\lib\site-packages (from keras-applications>=1.0.6->tensorflow) (2.9.0)
Building wheels for collected packages: wrapt, absl-py, gast, termcolor
  Building wheel for wrapt (setup.py) ... done
  Stored in directory: C:\Users\bob\AppData\Local\pip\Cache\wheels\d7\de\2e\efa132238792efb6459a96e85916ef8597fcb3d2ae51590dfd
  Building wheel for absl-py (setup.py) ... done
  Stored in directory: C:\Users\bob\AppData\Local\pip\Cache\wheels\9a\1e\7a\456008eb5e47fd5de792c6139df6d5b3d5f71d51c6a0b94799
  Building wheel for gast (setup.py) ... done
  Stored in directory: C:\Users\bob\AppData\Local\pip\Cache\wheels\5c\2e\7e\a1d4d4fcebe6c381f378ce7743a3ced3699feb89bcfbdadadd
  Building wheel for termcolor (setup.py) ... done
  Stored in directory: C:\Users\bob\AppData\Local\pip\Cache\wheels\7c\06\54\bc84598ba1daf8f970247f550b175aaaee85f68b4b0c5ab2c6
Successfully built wrapt absl-py gast termcolor
Installing collected packages: google-pasta, keras-preprocessing, wrapt, astor, grpcio, absl-py, protobuf, tensorflow-estimator, markdown, tensorboard, gast, termcolor, keras-applications, tensorflow
  Found existing installation: wrapt 1.10.11
    Uninstalling wrapt-1.10.11:
      Successfully uninstalled wrapt-1.10.11
Successfully installed absl-py-0.8.0 astor-0.8.0 gast-0.2.2 google-pasta-0.1.7 grpcio-1.23.0 keras-applications-1.0.8 keras-preprocessing-1.1.0 markdown-3.1.1 protobuf-3.9.1 tensorboard-1.14.0 tensorflow-1.14.0 tensorflow-estimator-1.14.0 termcolor-1.1.0 wrapt-1.11.2

(tensor) C:\Users\bob>pip install keras
Collecting keras
  Downloading https://files.pythonhosted.org/packages/f8/ba/2d058dcf1b85b9c212cc58264c98a4a7dd92c989b798823cc5690d062bb2/Keras-2.2.5-py2.py3-none-any.whl (336kB)
     |████████████████████████████████| 337kB 1.1MB/s
Requirement already satisfied: h5py in c:\users\bob\anaconda3\lib\site-packages (from keras) (2.9.0)
Requirement already satisfied: numpy>=1.9.1 in c:\users\bob\anaconda3\lib\site-packages (from keras) (1.16.4)
Requirement already satisfied: keras-applications>=1.0.8 in c:\users\bob\anaconda3\lib\site-packages (from keras) (1.0.8)
Requirement already satisfied: keras-preprocessing>=1.1.0 in c:\users\bob\anaconda3\lib\site-packages (from keras) (1.1.0)
Requirement already satisfied: pyyaml in c:\users\bob\anaconda3\lib\site-packages (from keras) (5.1.1)
Requirement already satisfied: six>=1.9.0 in c:\users\bob\appdata\roaming\python\python37\site-packages (from keras) (1.12.0)
Requirement already satisfied: scipy>=0.14 in c:\users\bob\anaconda3\lib\site-packages (from keras) (1.2.1)
Installing collected packages: keras
Successfully installed keras-2.2.5

(tensor) C:\Users\bob>

---------------------------------------
#pycharm

create new project -> enter project name -> click file -> settings -> project interpreter -> click gear icon -> add -> conda environment -> existing environment -> click 3 dot icon ->  change path to c:/users/(username)/Anaconda3/pythonw.exe -> ok -> apply -> waiting for package update to finish -> create new python file -> type import tensorflow, should have no error -> click add configration (top right)-> click + -> python -> name configuration -> change script path to project path/(name).py -> apply ok

-------------------------------
reference:
https://www.youtube.com/watch?v=ujTCoH21GlA

Thursday 22 August 2019

Wireshark Course: Beginner to Network Admin!

icmp (ping)
decryption: edit -> preference -> protocols -> wpa key -> data readable
export media file: right click on data -> export -> save -> open with vlc

reference:
https://www.youtube.com/watch?v=JnKc6fptviI&t=293s

Wednesday 21 August 2019

Top 10 Wireshark Display Filters

ip.addr == 192.168.0.16
ip.src == 192.168.0.16
ip.dst == 192.168.0.16
dns or http
tcp.port ==80
tcp.srcport == 80
tcp.dstport == 80
tcp.analysis.flags
right click on packet -> follow -> tcp/http stream
tcp contains 163
http.request
http.response == 200
tcp.flags.syn == 1
tcp.flags.reset == 1

reference:
https://www.youtube.com/watch?v=68t07-KOH9Y

Monday 12 August 2019

django 67 deploy to heroku

deployed to heroku
https://chuanshuoge-ecommerce.herokuapp.com/

code link
https://github.com/chuanshuoge6/ecommerce

----------------------------------------
create app on heroku


git init
git:remote -a chuanshuoge-ecommerce

------------------------
#power shell

heroku login
heroku buildpacks:set heroku/python
heroku buildpacks:add --index 1 heroku/nodejs

---------------------
copy files from react folder to django folder

------------------------
//package.json add postinstall

{
  "name": "python-react",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "antd": "^3.19.1",
    "axios": "^0.19.0",
    "bootstrap": "^4.3.1",
    "image-to-base64": "^2.0.1",
    "querystring": "^0.2.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-icons": "^3.7.0",
    "react-redux": "^7.0.3",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.0.1",
    "react-stripe-checkout": "^2.6.3",
    "react-table": "^6.10.0",
    "reactstrap": "^8.0.0",
    "redux": "^4.0.1",
    "redux-logger": "^3.0.6",
    "redux-promise-middleware": "^6.1.0",
    "redux-thunk": "^2.3.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "postinstall": "npm run build"
  },
  "engines": {
    "node": "10.12.0",
    "npm": "6.10.2"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

------------------------------------
//Procfile

release: python manage.py migrate
web: gunicorn reactdjango.wsgi --log-file -

---------------------------------
//create requirement.txt

pip freeze > requirements.txt

-------------------------------
//whitenoise static storage
#power shell

pip install Django whitenoise gunicorn

#django settings

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'build', 'static'),
]

------------------------------
//tell heroku to use react as frontend
#django setting

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'build')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

#django urls

from django.contrib import admin
from django.urls import path, include, re_path
from django.views.generic import  TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('rest-auth/',include('rest_auth.urls')),
    path('api/', include('music.api.urls')),
    path('', include('django.contrib.auth.urls')),
    re_path('.*', TemplateView.as_view(template_name='index.html'))
]

-------------------------------------------------
//allow heroku
#django settings

ALLOWED_HOSTS = ['chuanshuoge-ecommerce.herokuapp.com', '127.0.0.1']

----------------------------------
change request url to heroku
sample action

import axios from 'axios';
const config = require('../../config');

export function changePassword(token, data) {
    return {
        type: "change_password",
        payload: axios({
            method: 'post',
            url: config.URL + 'rest-auth/password/change/',
            headers: {
                Authorization: 'Token ' + token,
            },
            data: data,
        })
    }
}

//config

module.exports = {
    URL: 'https://chuanshuoge-ecommerce.herokuapp.com/',
    //URL: 'http://127.0.0.1:8000/',
}

------------------------------------
#power shell

git add .
git commit -am "make it better"
git push heroku master

--------------------------------
heroku logs

PS C:\Users\bob\django\project2> git push heroku master
Enumerating objects: 21, done.
Counting objects: 100% (21/21), done.
Delta compression using up to 4 threads.
Compressing objects: 100% (11/11), done.
Writing objects: 100% (11/11), 1.47 KiB | 751.00 KiB/s, done.
Total 11 (delta 9), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Node.js app detected
remote:
remote: -----> Creating runtime environment
remote:
remote:        NPM_CONFIG_LOGLEVEL=error
remote:        NODE_ENV=production
remote:        NODE_MODULES_CACHE=true
remote:        NODE_VERBOSE=false
remote:
remote: -----> Installing binaries
remote:        engines.node (package.json):  10.12.0
remote:        engines.npm (package.json):   6.10.2
remote:
remote:        Resolving node version 10.12.0...
remote:        Downloading and installing node 10.12.0...
remote:        Bootstrapping npm 6.10.2 (replacing 6.4.1)...
remote:        npm 6.10.2 installed
remote:
remote: -----> Restoring cache
remote:        - node_modules
remote:
remote: -----> Installing dependencies
remote:        Installing node modules (package.json + package-lock)
remote:
remote:        > python-react@0.1.0 postinstall /tmp/build_3d982eb1603cca98d4fb0a55ee743746
remote:        > npm run build
remote:
remote:
remote:        > python-react@0.1.0 build /tmp/build_3d982eb1603cca98d4fb0a55ee743746
remote:        > react-scripts build
remote:
remote:        Creating an optimized production build...
remote:        Compiled with warnings.
remote:
...warnings
remote:
remote:        Search for the keywords to learn more about each warning.
remote:        To ignore, add // eslint-disable-next-line to the line before.
remote:
remote:        File sizes after gzip:
remote:
remote:          524.18 KB  build/static/js/2.17f36e06.chunk.js
remote:          75.89 KB   build/static/css/2.cf0cc74f.chunk.css
remote:          9.41 KB    build/static/js/main.1191967e.chunk.js
remote:          916 B      build/static/css/main.b8ed5430.chunk.css
remote:          762 B      build/static/js/runtime~main.a8a9905a.js
remote:
remote:        The project was built assuming it is hosted at the server root.
remote:        You can control this with the homepage field in your package.json.
remote:        For example, add this to build it for GitHub Pages:
remote:
remote:          "homepage" : "http://myname.github.io/myapp",
remote:
remote:        The build folder is ready to be deployed.
remote:        You may serve it with a static server:
remote:
remote:          npm install -g serve
remote:          serve -s build
remote:
remote:        Find out more about deployment here:
remote:
remote:          https://bit.ly/CRA-deploy
remote:
remote:        audited 904172 packages in 103.782s
remote:        found 0 vulnerabilities
remote:
remote:
remote: -----> Build
remote:        Running build
remote:
remote:        > python-react@0.1.0 build /tmp/build_3d982eb1603cca98d4fb0a55ee743746
remote:        > react-scripts build
remote:
remote:        Creating an optimized production build...
remote:        Compiled with warnings.
remote:
...warnings
remote:        Search for the keywords to learn more about each warning.
remote:        To ignore, add // eslint-disable-next-line to the line before.
remote:
remote:        File sizes after gzip:
remote:
remote:          524.18 KB  build/static/js/2.17f36e06.chunk.js
remote:          75.89 KB   build/static/css/2.cf0cc74f.chunk.css
remote:          9.41 KB    build/static/js/main.1191967e.chunk.js
remote:          916 B      build/static/css/main.b8ed5430.chunk.css
remote:          762 B      build/static/js/runtime~main.a8a9905a.js
remote:
remote:        The project was built assuming it is hosted at the server root.
remote:        You can control this with the homepage field in your package.json.
remote:        For example, add this to build it for GitHub Pages:
remote:
remote:          "homepage" : "http://myname.github.io/myapp",
remote:
remote:        The build folder is ready to be deployed.
remote:        You may serve it with a static server:
remote:
remote:          npm install -g serve
remote:          serve -s build
remote:
remote:        Find out more about deployment here:
remote:
remote:          https://bit.ly/CRA-deploy
remote:
remote:
remote: -----> Pruning devDependencies
remote:        audited 904172 packages in 12.523s
remote:        found 0 vulnerabilities
remote:
remote:
remote: -----> Caching build
remote:        - node_modules
remote:
remote: -----> Build succeeded!
remote: -----> Python app detected
remote: -----> Installing requirements with pip
remote:
remote: -----> $ python manage.py collectstatic --noinput
remote:        163 static files copied to '/tmp/build_3d982eb1603cca98d4fb0a55ee743746/staticfiles', 511 post-processed.
remote:
remote: -----> Discovering process types
remote:        Procfile declares types -> release, web
remote:
remote: -----> Compressing...
remote:        Done: 179.4M
remote: -----> Launching...
remote:  !     Release command declared: this new release will not be available until the command succeeds.
remote:        Released v11
remote:        https://chuanshuoge-ecommerce.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
remote: Running release command...
remote:
remote: Operations to perform:
remote:   Apply all migrations: admin, auth, authtoken, contenttypes, music, sessions
remote: Running migrations:
remote:   No migrations to apply.
remote: Waiting for release.... done.
To https://git.heroku.com/chuanshuoge-ecommerce.git
   cd76f6b..3a6cdd2  master -> master

--------------------------------------

reference:
https://simpleisbetterthancomplex.com/tutorial/2016/08/09/how-to-deploy-django-applications-on-heroku.html
https://www.codementor.io/jamesezechukwu/how-to-deploy-django-app-on-heroku-dtsee04d4
https://medium.com/@qazi/how-to-deploy-a-django-app-to-heroku-in-2018-the-easy-way-48a528d97f9c
https://devcenter.heroku.com/articles/django-app-configuration
https://chuanshuoge2.blogspot.com/2018/12/deploy-react-and-express-to-heroku.html
https://alphacoder.xyz/deploy-react-django-app-on-heroku/
https://www.youtube.com/watch?v=r0ECufCyyyw
https://librenepal.com/article/django-and-create-react-app-together-on-heroku/
https://support.dnsimple.com/articles/heroku-error-ssl/

Sunday 11 August 2019

django 66 react loading spin

Migrated to cloud, request response time is longer, display loading status with spin

logging in

fetching albums

adding item to cart

added item to cart

updating cart

cart updated

deleting item in cart

item deleted

checking out cart

receipt

fetching order history

order history fetched

fetching order detail

detail fetched

registering user

user registered

changing password

password changed

resetting password

password reset email is sent

password reset email received

chuanshuo updating album

album updated

nick deleting album

permission denied
//react/pages/orderhistory

import React, { Component } from 'react';
import '../App.css';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { getOrderHistory } from '../redux/actions/getOrderHistory';
import { message, Collapse, Spin } from 'antd';
import OrderCollapse from '../partials/orderCollapse';

class OrderHistory extends Component {
    constructor(props) {
        super(props);

        this.state = {
            spin: false,
            spinTip: '',
        };
    }

    componentDidMount() {
        //start fetching database once logged in
        if (this.props.loggedin) {
            if (!this.props.gotOrders) {
                this.props.dispatch(getOrderHistory(this.props.token));
            }

            //wait for 10sec, check every 0.1s to see if orders are fetched
            let i = 0;
            this.setState({ spin: true, spinTip: 'Fetching Orders...' })
            const waitOrders = setInterval(() => {
                if (this.props.gotOrders) {

                    clearInterval(waitOrders);
                    this.setState({ spin: false })
                }
                if (i == 100) {
                    message.error('fetching order history timed out.')
                    clearInterval(waitOrders);
                    this.setState({ spin: false })
                }
                i++;
            }, 100)
        }
    }

    render() {
        if (!this.props.loggedin) {
            return <Redirect to='/login' />
        }

        return (
            <div style={{ padding: '10px', marginTop: '10px' }}>
                <legend>Order History</legend>
                {this.state.spin ? <Spin tip={this.state.spinTip} style={{ width: '100%', textAlign: 'center' }}></Spin> : null}
                <hr />

                {this.props.gotOrders ?
                    this.props.orders.sort((a, b) => { return new Date(b.date) - new Date(a.date) })
                        .map((order, index) => {
                            return <OrderCollapse header={order.date.toString()} extra={order.total.toString()}
                                orderid={order.id} key={index} />
                        })
                    : null}

            </div>
        );
    }
}

export default connect(
    (store) => {
        return {
            loggedin: store.login.fetched,
            token: store.login.token,
            gotOrders: store.orderHistory.fetched,
            orders: store.orderHistory.orders,
        };
    }
)(OrderHistory);

------------------------------
//react/pages/resetPassword

import React, { Component } from 'react';
import '../App.css';
import { resetPassword } from '../redux/actions/resetPassword';
import { connect } from 'react-redux';
import { Input, Spin } from 'antd';
import { Button } from 'reactstrap';
import { MdEmail } from "react-icons/md";

class ResetPassword extends Component {

    constructor(props) {
        super(props);

        this.state = {
            email: '',
        };
    }

    inputChange = (e, p) => {
        this.setState({ [p]: e.target.value });
    }

    formSubmit = (e) => {
        e.preventDefault();

        const formData = new FormData();
        formData.set('email', this.state.email);

        this.props.dispatch(resetPassword(formData));
    }

    render() {
        return (
            <form style={{ marginLeft: '10px', width: '300px' }}
                onSubmit={(e) => this.formSubmit(e)}>
                <legend>Reset Password</legend>
                {this.props.sending ? <Spin tip='Reseting Password...' style={{ width: '100%', textAlign: 'center' }}></Spin> : null}
                <hr />
                <p style={{ color: 'red' }}>{this.props.error}</p>
                {this.props.sent ? <p style={{ color: 'green' }}>Password reset email has been sent to {this.state.email}</p> : null}
                <Input placeholder="email" required='required' type='email'
                    prefix={<MdEmail style={{ color: 'rgba(0,0,0,.25)' }} />}
                    onChange={(e) => this.inputChange(e, 'email')}
                    style={{ marginTop: '5px' }}
                />
                <Button color="success" type='submit' size='sm'
                    style={{ marginTop: '15px' }}
                >Submit</Button>
            </form>
        );
    }
}

export default connect(
    (store) => {
        return {
            sent: store.login.sent,
            sending: store.login.sending,
            error: store.login.error
        };
    }
)(ResetPassword);

reference:
https://ant.design/components/spin/

Wednesday 7 August 2019

django 65 migrate to elephantSQL

register at elephantsql.com for a free postgres database on cloud

----------------------------------------
#update database
#django/settings

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'pupssizu',
        'USER': 'pupssizu',
        'PASSWORD': 'xxxxx',
        'HOST': 'raja.db.elephantsql.com',
        'PORT': '5432',
    }
}

----------------------------
#pgadmin connect cloud database
pgAdmin 4 create server

lots of database on elephantSQL, locate my database
---------------------------
#django/models

from django.db import models
from django.urls import reverse
from django.contrib.auth.models import User

class Album(models.Model):
    artist = models.CharField(max_length=50)
    album_title = models.CharField(max_length=50)
    genre = models.CharField(max_length=50)
    album_logo = models.TextField(default='iVBOR...')
    date_posted = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
    price = models.DecimalField(decimal_places=2,max_digits=5, default=10)

    #form submitted without action redirect to detail
    def get_absolute_url(self):
        return reverse('music:detail', kwargs={'pk': self.pk})

    #query album.objects.get(pk=1)
    def __str__(self):
        return self.album_title + ' - ' + self.artist

class Song(models.Model):
    album = models.ForeignKey(Album, on_delete=models.CASCADE)
    file_type = models.CharField(max_length=50)
    song_title = models.CharField(max_length=50)
    is_favorite = models.BooleanField(default=False)

    def __str__(self):
        return self.song_title

        # form submitted without action redirect to detail
    def get_absolute_url(self):
        return reverse('music:detail', kwargs={'pk': self.album.id})

class ShoppingItem(models.Model):
    shopper = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
    album = models.ForeignKey(Album, on_delete=models.CASCADE, blank=True, default=1)
    quantity = models.PositiveIntegerField(blank=True, default=0)

    def __str__(self):
        return 'shopper ' + str(self.shopper) + ' - album ' + str(self.album) + ' - quantity '+str(self.quantity)

class OrderHistory(models.Model):
    order = models.CharField(max_length=50)
    shopper = models.ForeignKey(User, on_delete=models.CASCADE)
    total = models.DecimalField(decimal_places=2,max_digits=7, default=0)
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return str(self.order)+ ' ' + str(self.shopper) + ' bought '  + str(self.total) + ' on ' + str(self.date)

#powershell python manage.py migrate
------------------------------------
#django create super user
https://docs.djangoproject.com/en/1.8/intro/tutorial02/

super user chuanshuo created
-----------------------------
#django/admin
login django admin as chuanshuo

from django.contrib import admin
from .models import Album, Song, ShoppingItem, OrderHistory

admin.site.register(Album)
admin.site.register(Song)
admin.site.register(ShoppingItem)
admin.site.register(OrderHistory)

-----------------------------------------
#install django-rest-auth
https://django-rest-auth.readthedocs.io/en/latest/installation.html

# django-rest-auth configuration
#django settings
OLD_PASSWORD_FIELD_ENABLED = True
LOGIN_URL = 'http://localhost:3000/login'

#django urls
path('rest-auth/',include('rest_auth.urls')),
path('', include('django.contrib.auth.urls')),

-----------------------------------
#create registration template

https://chuanshuoge2.blogspot.com/2019/05/django-33-reset-password-by-email.html
https://chuanshuoge2.blogspot.com/2019/07/django-53-react-redux-reset-password-by.html

----------------------------------------------
#email setting
#django settings

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_USER = 'zchen2014chuanshuo@gmail.com'
EMAIL_HOST_PASSWORD = 'xxxx'

---------------------------------------
#enable cors
#power shell
pip install django-cors-headers

#django setting

INSTALLED_APPS = (
    ...
    'corsheaders',
)

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',
)

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True

https://chuanshuoge2.blogspot.com/2019/06/django-40-django-react-redux-ftech-token.html
-------------------------------------
#create rest api
#power shell
pip install djangorestframework
pip install django-filter

#django settings
INSTALLED_APPS = [
    ...
    'rest_framework',
]

#django urls
path('api/', include('music.api.urls')),

------------------------------------------
#django rest framework token authentication
#django settings

INSTALLED_APPS = [
...
    'rest_framework',
    'rest_framework.authtoken',
    'django_filters',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
                'rest_framework.authentication.TokenAuthentication',
                ),
    'DEFAULT_PERMISSION_CLASSES':(
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    ),
}

#powershell
python manage.py migrate

https://chuanshuoge2.blogspot.com/2019/05/django-38-rest-framework-authentication.html

--------------------------
#django/api/urls

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from music.api import apiview
from rest_framework.authtoken import views

app_name = 'musicAPI'

urlpatterns = [
    path('album_list/', apiview.AlbumList.as_view(), name='AlbumList'),
    path('shoppingItems/', apiview.ShoppingItemsList.as_view(), name='ShoppingItems'),
    path('user_list/', apiview.UserList.as_view(), name='UserList'),
    path('user_register/', apiview.UserRegister.as_view(), name='UserRegister'),
    path('song_list/', apiview.SongList.as_view(), name='SongList'),
    path('cart_checkout/', apiview.CartCheckout.as_view(), name='CartCheckout'),
    path('order_history/', apiview.OrderHistoryList.as_view(), name='OrderHistory'),
    path('album_detail/<int:pk>/', apiview.AlbumDetail.as_view(), name='AlbumDetail'),
    path('shoppingItem/<int:pk>/', apiview.ShoppingItemDetail.as_view(), name='ShoppingItemDetail'),
    path('orderDetail/<int:pk>/', apiview.OrderHistoryDetail.as_view(), name='OrderDetail'),
    path('api-token-auth/', views.obtain_auth_token, name='AuthToken'),
    path('update_password/', apiview.UpdatePassword.as_view(), name='UpdatePassword'),
]

urlpatterns = format_suffix_patterns(urlpatterns)

-------------------------------------------
#django/api/serializers

from rest_framework import serializers
from music.models import Album, Song, ShoppingItem, OrderHistory
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password

class MusicSerializer(serializers.ModelSerializer):
    class Meta:
        model = Album
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(MusicSerializer, self).__init__(*args, **kwargs)
        self.fields['album_logo'] = serializers.CharField(required=True)

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'email')

class SongSerializer(serializers.ModelSerializer):
    class Meta:
        model = Song
        fields = ('id', 'album', 'file_type', 'song_title', 'is_favorite')

class PasswordSerializer(serializers.Serializer):
    old_password = serializers.CharField(required=True)
    new_password = serializers.CharField(required=True)

    def validate_new_password(self, value):
        validate_password(value)
        return value

class ShoppingItemsSerializer(serializers.ModelSerializer):
    class Meta:
        model = ShoppingItem
        fields = ('id', 'shopper', 'album', 'quantity')

class OrderHistorySerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderHistory
        fields = ('id', 'order', 'shopper', 'total', 'date')

-----------------------------------------
#django/api/apiview

from music.models import Album, Song, ShoppingItem, OrderHistory
from music.api.serializers import MusicSerializer, UserSerializer, SongSerializer, PasswordSerializer, ShoppingItemsSerializer, OrderHistorySerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.http import Http404
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from rest_framework.decorators import authentication_classes, permission_classes
from django.contrib.auth.password_validation import validate_password
from django.core import exceptions
from django.forms.models import model_to_dict
from django.conf import settings
import stripe

class AlbumList(APIView):
    def get(self, request, format=None):
        username = request.GET.get('author')
        data_length = request.GET.get('data_length')

        #filter by author
        if username==None:
            albums = Album.objects.all()
        else:
            author_id = get_object_or_404(User, username=username).pk
            albums = Album.objects.filter(author=author_id).order_by('-date_posted')

        #filter by data length
        if data_length!=None:
            try:
                int(data_length)
            except ValueError:
                return Response('data length is invvalid', status=status.HTTP_406_NOT_ACCEPTABLE)
            else:
                albums = albums[:int(data_length)]

        serializer = MusicSerializer(albums, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = MusicSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class AlbumDetail(APIView):

    def get_object(self, pk):
        try:
            return Album.objects.get(pk=pk)
        except Album.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        album = self.get_object(pk)
        serializer = MusicSerializer(album)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        album = self.get_object(pk)

        #only owner can edit
        if album.author != request.user:
            return Response({"detail": "You do not have permission to perform this action."},
                            status= status.HTTP_403_FORBIDDEN)

        serializer = MusicSerializer(album, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        album = self.get_object(pk)

        # only owner can delete
        if album.author != request.user:
            return Response({"detail": "You do not have permission to perform this action."},
                            status=status.HTTP_403_FORBIDDEN)

        album.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

class UserList(APIView):
    def get(self, request, format=None):
        users = User.objects.all()
        serializer = UserSerializer(users, many=True)
        return Response(serializer.data)

@authentication_classes([])
@permission_classes([])
class UserRegister(APIView):
    def post(self, request, format=None):
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():

            #email has to be unique
            email = request.POST.get('email')
            unique_email = User.objects.filter(email=email).count()
            if(unique_email > 0):
                return Response({'email': 'email already exist'}, status=status.HTTP_406_NOT_ACCEPTABLE)

            #validate password
            password = request.POST.get('password')
            try:
                validate_password(password)
            except exceptions.ValidationError as e:
                return Response(e.messages, status=status.HTTP_406_NOT_ACCEPTABLE)
            else:
                pass
            #register new user
            serializer.save()
            #set password for new user
            username = serializer.data.get('username')
            newUser = User.objects.get(username=username)
            newUser.set_password(password)
            #save password for new user
            newUser.save()

            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class SongList(APIView):
    def get(self, request, format=None):
        songs = Song.objects.all()
        serializer = SongSerializer(songs, many=True)
        return Response(serializer.data)

class UpdatePassword(APIView):
    def get_object(self, queryset=None):
        return self.request.user

    def put(self, request, *args, **kwargs):
        currentUser = self.get_object()
        serializer = PasswordSerializer(data=request.data)

        if serializer.is_valid():
            # Check old password
            old_password = serializer.data.get("old_password")
            if not currentUser.check_password(old_password):
                return Response({"old_password": ["Wrong password."]},
                                status=status.HTTP_400_BAD_REQUEST)
            # set_password also hashes the password that the user will get
            currentUser.set_password(serializer.data.get("new_password"))
            currentUser.save()
            return Response(status=status.HTTP_204_NO_CONTENT)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class ShoppingItemsList(APIView):
    def get(self, request, format=None):
        #for privacy, only return shoppers items
        shoppingItems = ShoppingItem.objects.filter(shopper=request.user)
        serializer = ShoppingItemsSerializer(shoppingItems, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        #customer has item in cart
        try:
            revisitedItem = ShoppingItem.objects.get(shopper=request.user,  album=request.data.get('album'))
            revisitedItem.quantity= int(request.data.get('quantity')) + revisitedItem.quantity

            serializer = ShoppingItemsSerializer(data=model_to_dict( revisitedItem ))
            if serializer.is_valid():
                # update database
                revisitedItem.save()
                return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        #customer select new item
        except exceptions.ObjectDoesNotExist:
            serializer = ShoppingItemsSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class ShoppingItemDetail(APIView):

    def get_object(self, pk):
        try:
            return ShoppingItem.objects.get(pk=pk)
        except ShoppingItem.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        shoppingItem = self.get_object(pk)
        serializer = ShoppingItemsSerializer(shoppingItem)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        shoppintItem = self.get_object(pk)

        #only owner can edit
        if shoppintItem.shopper != request.user:
            return Response({"detail": "You do not have permission to perform this action."},
                            status= status.HTTP_403_FORBIDDEN)

        serializer = ShoppingItemsSerializer(shoppintItem, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        shoppingItem = self.get_object(pk)

        # only owner can delete
        if shoppingItem.shopper != request.user:
            return Response({"detail": "You do not have permission to perform this action."},
                            status=status.HTTP_403_FORBIDDEN)

        shoppingItem.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

class CartCheckout(APIView):
    def post(self, request, format=None):
        stripe.api_key = settings.STRIPE_SECRET_KEY
        token = request.data.get('token')
        cart = request.data.get('cart')

        try:
            customer=stripe.Customer.create(email=token['email'],source=token['id'])
            charge=stripe.Charge.create(
                amount=int(cart['price']*100),
                currency='cad',
                receipt_email= token['email'],
                customer=customer.id,
                metadata=cart['items'],
            )
            return Response(charge, status=status.HTTP_202_ACCEPTED)
        except Exception as e:
            return Response(e, status=status.HTTP_400_BAD_REQUEST)

class OrderHistoryList(APIView):
    def get(self, request, format=None):
        orders = OrderHistory.objects.filter(shopper=request.user)
        serializer = OrderHistorySerializer(orders, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = OrderHistorySerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class OrderHistoryDetail(APIView):
    def get_object(self, pk):
        try:
            return OrderHistory.objects.get(pk=pk)
        except OrderHistory.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        stripe.api_key = settings.STRIPE_SECRET_KEY
        order = self.get_object(pk)
        try:
            #order is a model boject, request is a python dictionary
            charge=stripe.Charge.retrieve(order.order)
            return Response(charge, status=status.HTTP_200_OK)
        except Exception as e:
            return Response(e, status=status.HTTP_400_BAD_REQUEST)

-------------------------------------------------------
#stripe
#power shell
pip install stripe

#django settings
STRIPE_SECRET_KEY = 'sk_test_xxxxxx'

https://chuanshuoge2.blogspot.com/2019/07/django-62-stripe-checkout.html