— 가계부 웹어플리케이션
1.프로젝트 이름/설명
나의 accounting 관리하기 : 웹사이트서비스를 통해서 구매내역을 등록하여 등록된 정보를 이용하여 구매상황을 도표로 분석 및 엑셀파일로 정리
2. 기획 의도
▶1~4주차에 배운 html javascript ajax통신 python 웹크롤링 flask 등을 활용
▶사용자의 자금관리에 대한 자각을 위해 주제 선정
3.개발해야하는 기능
- 로그인기능 → 다른기능 완성후 추가
2. 구매내역 등록하기
3. 등록된 내역 분석 하여 화면에 보여주기
4. 데이터를 가지고 엑셀에 저장 하는 기능
4.개발계획
5주차 : 구체적인 계획 및 시작
6주차: html 폼 만들기 / 데이터 등록하기 기능 / 엑셀DB파일생성
7주차: 웹페이지에 시각화 하여 분석 / 추가: 로그인 기능
- >시간여유가 된다면 조회기능 추가 👌
2019.20.21 개발일지
전체적인 계획 완료 : https://ovenapp.io/project/V3yBtIVRolWq3hioDiU4OrgwLBE9tvjY#zt5jK
- navbar 에 해당되는 페이지 ( 전체, 이번달, 거래 입력, 엑셀파일) html 생성
- 거래 입력 에 해당되는 페이지에서 input 값 및 화면에 띄우기
- 전체 가계부 -> 달력 디자인 필요 : full calendar 사용 예정
# 가져올때 달력 안에 날짜에 해당되는 DB를 고려하여 디자인 예정 #
2019.12.22 개발일지
조금이라도 진행하고자 꾸역꾸역 진행 …
달력 -> table 만들어 직접 구현 ..
#전체 해당되는 값을 어떻게 화면에 보여줄지 고민이다 .
2019.12.23
팀에서 개인으로 바뀌게 되었다. .
의문점 : pycharm 에서 열면 적용 되는데 그냥 html 파일 자체를 열면 적용안됨
2019.12.26
ajax통신 완료 -> 거래 등록시 mongodb에 저장 및 불러오기 성공!
데이터 차트로 불러오기 (라이브러리 사용 Chart.js)
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js"></script>
전체가계부 비교 화면 구현 해당 년에 월별로 가격 비교
#생각보다 금방할거 같다 …
2020.01.04 수업
2020. 01 .06
엑셀 파일 활용해서 프로젝트에 적용하기
function excel(){
$.ajax({
// GET 형식으로 값을 불러온다
type: "GET",
url: "/excel",
data: {}, success: function(response){ alert('파일이 생성되었습니다' ) }
})}
@app.route('/excel',methods=['GET'])
def exeting():
result = list(db.counting.find({}, {'_id': 0}))
row = 4
work_book = load_workbook('prac01.xlsx')
work_sheet = work_book['prac']
sum=0
cafe=0
food=0
medical=0
other=0
for s in result:
print(s['somedate'])
work_sheet.cell(row=row, column=1, value=s['somedate']) #날짜 엑셀에 적용
work_sheet.cell(row=row, column=2, value=s['content']) # 내용 엑셀에 적용
work_sheet.cell(row=row, column=3, value=s['tag']) # 태그 엑셀에 적용
work_sheet.cell(row=row, column=4, value=s['money']) #돈 엑셀에 적용
sum+=int(s['money'])
if(s['tag']=='카페'):
cafe+=int(s['money'])
if (s['tag'] == '카페'):
cafe += int(s['money'])
if (s['tag'] == '음식'):
food += int(s['money'])
if (s['tag'] == '의료비 및 보험비'):
medical += int(s['money'])
if (s['tag'] == '기타'):
other += int(s['money'])
row=row+1 work_sheet.cell(row=4, column=6, value=sum) # 총 금액 엑셀에 적용 #태그별 금액
work_sheet.cell(row=7, column=6, value=cafe)
work_sheet.cell(row=7, column=7, value=food)
work_sheet.cell(row=7, column=8, value=medical)
work_sheet.cell(row=7, column=9, value=other)
work_book.save('prac01.xlsx') return jsonify({'result': 'success', 'shops': result})
counting 데이터베이스에 있는 값들을 엑셀에 각 자리에 맞춰서 데이터를 적용 및 생성
2020 01 10
for (var i = nextcontent; i < nextcontent+5; i++)
{ if(i>articles.length || i<0)break;
// console.log(nextcontent)
// console.log(articles[nextcontent]['content'])
make_card(articles[i]['somedate'],articles[i]['money'],articles[i]['content'],articles[i]['tag'],
count) count++;
}
}
2020 01 20
function ready(){
$.ajax({
type: "GET",
url: "/money",
data: {}, success: function(response){
let articles = response['shops'];
console.log(response['shops'])
for (var i = 0; i < articles.length; i++)
{
console.log(articles[i]['tag'])
if(articles[i]['tag']=='음식')
{
console.log("태그 음식")
food+=parseInt(articles[i]['money'])
console.log(food)
}
if(articles[i]['tag']=='기타')
{
other+=parseInt(articles[i]['money'])
}
if(articles[i]['tag']=='카페')
{
cafe+=parseInt(articles[i]['money'])
}
if(articles[i]['tag']=='의료비 및 보험비')
{
medical+=parseInt(articles[i]['money'])
} }
var ctx = document.getElementById("myChart").getContext('2d');
/*
- Chart를 생성하면서,
- ctx를 첫번째 argument로 넘겨주고,
- 두번째 argument로 그림을 그릴때 필요한 요소들을 모두 넘겨줍니다.
*/
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["음식", "카페", "의료", "기타"],
datasets: [{
label: '# of Votes',
data: [food, cafe, medical, other],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)', ],
borderColor: [
'rgba(255,99,132,1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)', ],
borderWidth: 1
}]
},
options: {
maintainAspectRatio: true, // default value. false일 경우 포함된 div의 크기에 맞춰서 그려짐.
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
} })}
GET 으로 money 라는 restAPI를 했을 경우 Chart 안에 종류별로 적용
(파이선에서는 단지 값들만 호출)
@app.route('/money', methods=['GET'])
def getting():
# 모든 document 찾기 & _id 값은 출력에서 제외하기
# result =list(db.counting.find({},{'_id':0}))
result =list(db.counting.find({},{'_id':0}))
# shops라는 키 값으로 내려주기
return jsonify({'result': 'success', 'shops': result})
2020 01 12
로그인 / 회원가입 추가
jwt 라이브러리를 이용하여 로그인 구현
import jwtSECRET_KEY = 'apple' # 토근시 필요한 보안 - 아무거나 입력 원하는거@app.route('/register') # 회원이 아니라 면을 클릭하게 되면
def register():
return render_template('register.html')
@app.route('/api/register', methods=['POST']) #회원가입시 db.counting.insert_one(imformation)
def api_register():
id_receive = request.form['id_give']
pw_receive = request.form['pw_give']
pw_hash = hashlib.sha256(pw_receive.encode('utf-8')).hexdigest() db.temp.insert_one({'id':id_receive,'pw':pw_hash}) return jsonify({'result': 'success'})
@app.route('/api/login', methods=['POST'])
#로그인 완료시 id와 pwd 비교 성공하면 html 에서 index.html로 이동
def api_login():
id_receive = request.form['id_give']
pw_receive = request.form['pw_give'] # 회원가입 때와 같은 방법으로 pw를 암호화합니다.
pw_hash = hashlib.sha256(pw_receive.encode('utf-8')).hexdigest() # id, 암호화된pw을 가지고 해당 유저를 찾습니다.
result = db.temp.find_one({'id':id_receive,'pw':pw_hash}) # 찾으면 JWT 토큰을 만들어 발급합니다.
if result is not None:
# JWT 토큰에는, payload와 시크릿키가 필요합니다.
# 시크릿키가 있어야 토큰을 디코딩(=풀기) 해서 payload 값을 볼 수 있습니다.
# 아래에선 id와 exp를 담았습니다. 즉, JWT 토큰을 풀면 유저ID 값을 알 수 있습니다.
# exp에는 만료시간을 넣어줍니다. 만료시간이 지나면, 시크릿키로 토큰을 풀 때 만료되었다고 에러가 납니다.
payload = {
'id': id_receive,
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=3600)
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256').decode('utf-8') # token을 줍니다.
return jsonify({'result': 'success','token':token})
# 찾지 못하면
else:
return jsonify({'result': 'fail', 'msg':'아이디/비밀번호가 일치하지 않습니다.'})
@app.route('/api/id', methods=['GET'])
def api_valid():
# 토큰을 주고 받을 때는, 주로 header에 저장해서 넘겨주는 경우가 많습니다.
# header로 넘겨주는 경우, 아래와 같이 받을 수 있습니다.
token_receive = request.headers['token_give'] # try / catch 문?
# try 아래를 실행했다가, 에러가 있으면 except 구분으로 가란 얘기입니다. try:
# token을 시크릿키로 디코딩합니다.
# 보실 수 있도록 payload를 print 해두었습니다. 우리가 로그인 시 넣은 그 payload와 같은 것이 나옵니다.
payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
print(payload) # payload 안에 id가 들어있습니다. 이 id로 유저정보를 찾습니다.
# 여기에선 그 예로 닉네임을 보내주겠습니다.
userinfo = db.temp.find_one({'id':payload['id']},{'_id':0})
return jsonify({'result': 'success','id':userinfo['id']})
except jwt.ExpiredSignatureError:
# 위를 실행했는데 만료시간이 지났으면 에러가 납니다.
return jsonify({'result': 'fail', 'msg':'로그인 시간이 만료되었습니다.'})
토큰값을 받으면 id 값을 받아온다
$.ajax({
type: "GET",
url: "/api/id",
headers: { 'token_give' : $.cookie('mytoken') },
data: {},
success: function(response){
if (response['result'] == 'success'){
// 올바른 결과값을 받으면 id을 입력해줍니다.
id =response['id']
}
}
})
$.ajax({
type: "POST",
url: "/api/login",
data: { id_give:$('#userid').val(), pw_give:$('#userpw').val() },
success: function(response){
if (response['result'] == 'success'){
// 로그인이 정상적으로 되면, 토큰을 받아옵니다.
// 이 토큰을 mytoken이라는 키 값으로 쿠키에 저장합니다.
$.cookie('mytoken', response['token']);
alert('로그인 완료!')
location.href='index.html'
} else{
// 로그인이 안되면 에러메시지를 띄웁니다.
alert(response['msg'])
}
}
})
회원가입
그 후 , 해당 아이디에 해당하는 값들만 분석하여 해당 고객의 데이터를 화면에 보여줌
# 다음 해야 할 일은 고객마다 다른 엑셀파일 생성 해줘야 한다.
2020 01 13
전체적인 디자인을 변경 -> 제공하는 디자인 다운 받아 해당 프로젝트에 맞게 변경
2020 01 14
사용자에 따른 엑셀파일 저장 완료
def exeting():
result = list(db.counting.find({}, {'_id': 0}))
row = 4
token_receive = request.headers['token_give'] #payload를 찍어보면 id 값이 존재한다
payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256']) # 해당 아이디 값을 받아서 엑셀 저장 이름에 넣는다.
id = payload['id'] + '.xlsx'....work_book.save(id)
로그인에서 받은 token 을 request 하여 payload 안에 id 값을 적용
2020 01 18 프로젝트 마무리
#프로젝트
밑에 부분이 짤려서 아쉽지만 아쉬운대로 완료 !!
#취지& 설명 : 금전적인 부분을 혼자서 가계부를 쓰려는 사람들에게 도움을 주는 사이트를 만들어 보자 ( 스파르타 코딩 5기 수업 안에서 배운 내용을 바탕으로 기초적인 기술과 능력을 활용 )
#기술 설명 :
전체적인 디자인 >
. 부트스트랩 이용
사용자별로 데이터 chart화 >
. chart.js 이용
데이터 등록 및 적용 >
. ajax rest api 이용 하여 mongodb에 저장 및 활용
엑셀 파일 생성 기능>
. openpyxl 라이브러리 이용
로그인 기능 / 회원가입 기능 >
- 이범규님 코드를 참고
- jwt 라이브러리 이용
- id 값을 가지고 mongodb에서 각 아이디 별로 데이터 분류
모든 기능을 EC2에 연결 및 가비아 서비스를 이용하여 counting.shop이라는 도메인으로 활성화
#어려웠던부분 & 극복 방법
나중에 로그인 기능을 하게 되어 로그인 후 다시 적용 하는 부분이 어려웠다.
하지만 튜터님의 힘을 이용하여 해결
#8주간 후기
끝날때 까지 끝난게 아닌거 같은 코딩의 8주가 되었다.