송용우

Merge commit '0babf3c1' into feature/rest_api

Showing 38 changed files with 570 additions and 100 deletions
MIT License
Copyright (c) 2020 Yong-Woo Song
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
# Jaksimsamil
![issue badge](https://img.shields.io/github/issues/FacerAin/OSS-Jaksimsamil)
![fork badge](https://img.shields.io/github/forks/FacerAin/OSS-Jaksimsamil)
![star badge](https://img.shields.io/github/stars/FacerAin/OSS-Jaksimsamil)
![license badge](https://img.shields.io/github/license/FacerAin/OSS-Jaksimsamil)
## Project Overview
> **Jaksaimsamil Algorithm Study Helper Service**
>
> 작심삼일 알고리즘 문제풀이 도우미 서비스<br/>
>
> > 알고리즘 문제 풀이 스터디를 꾸준히 할 수 있게 돕는 웹 서비스입니다.
> > <br> [링크](http://facerain.dcom.club)에서 직접 사용해 보세요!
![그림1](https://user-images.githubusercontent.com/16442978/85690047-236d1d00-b70e-11ea-8d2b-480593c0daf3.png)
![그림2](https://user-images.githubusercontent.com/16442978/85690058-2536e080-b70e-11ea-98cd-45fdf04084ce.png)
## Features (ver.1.0.0)
- 회원가입/로그인 제공
- Online Judge 연동 가능 (Baekjoon)
- 나의 학습 현황 한눈에 보기
- 추천 문제 제공
- Slack 알리미
## Upcoming Features
- 친구 추가
- 친구와의 경쟁
- 그룹 추가
- 그룹 추천
- 개선된 문제 추천 (사용자 실력 맞춤형)
## Usages
#### 회원
1. 로그인하여 서비스에 접속 할 수 있습니다.
2. 서비스가 처음이라면, 회원가입을 하세요.
<br>
#### 설정
1. 백준 아이디를 등록하고 동기화하세요. [상세]()
2. 슬랙 HOOK URL을 등록하세요. [상세]()
3. 일일 목표량을 등록하세요.
## Getting Started
1. Clone
```
git clone https://github.com/FacerAin/OSS-Jaksimsamil.git
```
2. Install MongoDB(Ubuntu)
```
sudo apt-get update
sudo apt-get install -y mongodb-org
sudo service mongod start
```
3. Set Serverfile
```
cd Jaksimsamil-server
touch .env
---TYPE THIS IN FILE----
SERVER_PORT= ###
MONGO_URL= ###
JWT_SECRET= ###
```
4. Start Node Server
```
cd Jaksimsamil-server
sudo npm install
npm start
```
[링크](/jaksimsamil-server/README.md)에서 API 제공 목록을 볼 수 있습니다.
<br>
5. Set Front-end page
```
cd Jaksimsamil-server
sudo npm install
npm start #Start React
```
## Contributing
컨트리뷰션은 언제나 환영입니다. 다음 절차를 지켜주세요!
1. Fork the Project
2. Create your Feature Branch
3. Commit our changes
4. Push to Branch
5. Open a Pull Request
## License
- MIT LICENCE
......
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
This diff could not be displayed because it is too large.
......@@ -16,7 +16,7 @@
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",
"react-scripts": "^3.4.3",
"redux": "^4.0.5",
"redux-actions": "^2.6.5",
"redux-devtools-extension": "^2.13.8",
......
......@@ -5,6 +5,7 @@ import LoginPage from './pages/LoginPage';
import RegisterPage from './pages/RegisterPage';
import HomePage from './pages/HomePage';
import SettingPage from './pages/SettingPage';
import ChallengePage from './pages/ChallengePage';
function App() {
return (
......@@ -13,6 +14,7 @@ function App() {
<Route component={LoginPage} path="/login" />
<Route component={RegisterPage} path="/register" />
<Route component={SettingPage} path="/setting" />
<Route component={ChallengePage} path="/challenge" />
</>
);
}
......
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import palette from '../../lib/styles/palette';
const ChallengeForm = () => {
return <div></div>;
};
/*
Todo
챌린지 이름
챌린지 기간 (Start - End)
챌린지 세션 정보 (일 간격과 목표 문제)
그룹 원 정보.
*/
export default ChallengeForm;
......@@ -8,6 +8,10 @@ const categories = [
text: '홈',
},
{
name: 'challenge',
text: '챌린지',
},
{
name: 'setting',
text: '설정',
},
......
......@@ -3,42 +3,111 @@ import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import palette from '../../lib/styles/palette';
import AuthForm from '../auth/AuthForm';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
background: palette.gray[2],
padding: theme.spacing(8),
},
paper: {
padding: theme.spacing(2),
padding: theme.spacing(8),
margin: 'auto',
textAlign: 'center',
color: theme.palette.text.secondary,
},
}));
const HomeForm = () => {
const HomeForm = ({ PSdata, goalNum }) => {
const classes = useStyles();
return (
return PSdata ? (
<div className={classes.root}>
<Grid container spacing={3}>
<Grid container spacing={5}>
<Grid item xs={12}>
<Paper className={classes.paper}>xs=12</Paper>
<Paper className={classes.paper}>
<h1>{PSdata.recommend_data.problem_number}</h1>
<h1>{PSdata.recommend_data.problem_title}</h1>
<a
href={'http://www.boj.kr/' + PSdata.recommend_data.problem_number}
>
바로가기
</a>
<h3>오늘의 추천 문제</h3>
</Paper>
</Grid>
<Grid item xs={6}>
<Paper className={classes.paper}>
<h1>{PSdata.presentNum + '/' + goalNum}</h1>
<h3>오늘 문제</h3>
</Paper>
</Grid>
<Grid item xs={6}>
<Paper className={classes.paper}>xs=6</Paper>
<Paper className={classes.paper}>
<h1>{PSdata.latestSolve.problem_number}</h1>
<h1>{PSdata.latestSolve.problem_title}</h1>
<h3>마지막으로 문제</h3>
</Paper>
</Grid>
<Grid item xs={4}>
<Paper className={classes.paper}>
<h1>{PSdata.weekNum}</h1>
<h3>7</h3>
</Paper>
</Grid>
<Grid item xs={4}>
<Paper className={classes.paper}>
<h1>{PSdata.monthNum}</h1>
<h3>30</h3>
</Paper>
</Grid>
<Grid item xs={4}>
<Paper className={classes.paper}>
<h1>{PSdata.totalNum}</h1>
<h3>전체</h3>
</Paper>
</Grid>
</Grid>
</div>
) : (
<div className={classes.root}>
<Grid container spacing={5}>
<Grid item xs={12}>
<Paper className={classes.paper}>
<h1></h1>
<h3>오늘의 추천 문제</h3>
</Paper>
</Grid>
<Grid item xs={6}>
<Paper className={classes.paper}>xs=6</Paper>
<Paper className={classes.paper}>
<h1></h1>
<h3>오늘</h3>
</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
<Grid item xs={6}>
<Paper className={classes.paper}>
<h1></h1>
<h3>마지막 </h3>
</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
<Grid item xs={4}>
<Paper className={classes.paper}>
<h1></h1>
<h3>7</h3>
</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
<Grid item xs={4}>
<Paper className={classes.paper}>
<h1></h1>
<h3>30</h3>
</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
<Grid item xs={4}>
<Paper className={classes.paper}>
<h1></h1>
<h3>전체</h3>
</Paper>
</Grid>
</Grid>
</div>
......
......@@ -9,13 +9,16 @@ const useStyles = makeStyles((theme) => ({
margin: theme.spacing(1),
},
},
button: {
margin: theme.spacing(1),
},
}));
const BJIDForm = ({ onChange, onBJIDSubmit, profile, onSyncBJIDSubmit }) => {
const classes = useStyles();
return (
<div>
<form onSubmit={onBJIDSubmit}>
<form>
<TextField
name="userBJID"
onChange={onChange}
......@@ -23,11 +26,21 @@ const BJIDForm = ({ onChange, onBJIDSubmit, profile, onSyncBJIDSubmit }) => {
placeholder="백준 아이디"
label="백준 아이디"
/>
<Button variant="outlined" type="submit">
등록
</Button>
</form>
<Button variant="outlined" onClick={onSyncBJIDSubmit}>
<Button
className={classes.button}
variant="outlined"
onClick={onBJIDSubmit}
color="primary"
>
등록
</Button>
<Button
className={classes.button}
variant="outlined"
onClick={onSyncBJIDSubmit}
color="secondary"
>
동기화
</Button>
</div>
......
......@@ -10,13 +10,16 @@ const useStyles = makeStyles((theme) => ({
margin: theme.spacing(1),
},
},
button: {
margin: theme.spacing(1),
},
}));
const GoalNumForm = ({ onChange, profile, onGoalNumSubmit }) => {
const classes = useStyles();
return (
<div>
<form onSubmit={onGoalNumSubmit}>
<form>
<TextField
name="goalNum"
type="number"
......@@ -28,10 +31,15 @@ const GoalNumForm = ({ onChange, profile, onGoalNumSubmit }) => {
shrink: true,
}}
/>
<Button variant="outlined" type="submit">
등록
</Button>
</form>
<Button
className={classes.button}
onClick={onGoalNumSubmit}
color="primary"
variant="outlined"
>
등록
</Button>
</div>
);
};
......
......@@ -13,11 +13,12 @@ const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
background: palette.gray[2],
padding: theme.spacing(8),
},
paper: {
padding: theme.spacing(8),
margin: 'auto',
textAlign: 'center',
padding: 30,
},
}));
......@@ -45,14 +46,10 @@ const SettingForm = ({
</LoadingParentStyle>
) : (
<div className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Paper className={classes.paper}>
<h3>{profile.username}</h3>
</Paper>
</Grid>
<Grid container item xs={12}>
<Grid container spacing={5}>
<Grid container item xs={6}>
<Paper className={classes.paper} elevation={3}>
<h1>백준 아이디</h1>
<BJIDForm
profile={profile}
onChange={onChange}
......@@ -62,8 +59,9 @@ const SettingForm = ({
</Paper>
</Grid>
<Grid container item xs={12}>
<Grid container item xs={6}>
<Paper className={classes.paper} elevation={3}>
<h1>슬랙 Hook URL</h1>
<SlackForm
profile={profile}
onChange={onChange}
......@@ -72,8 +70,9 @@ const SettingForm = ({
</Paper>
</Grid>
<Grid container item xs={12}>
<Grid container item xs={6}>
<Paper className={classes.paper} elevation={3}>
<h1>일일 목표</h1>
<GoalNumForm
profile={profile}
onChange={onChange}
......
......@@ -10,13 +10,16 @@ const useStyles = makeStyles((theme) => ({
margin: theme.spacing(1),
},
},
button: {
margin: theme.spacing(1),
},
}));
const SlackForm = ({ onChange, profile, onSlackURLSubmit }) => {
const classes = useStyles();
return (
<div>
<form onSubmit={onSlackURLSubmit}>
<form>
<TextField
name="slackWebHookURL"
onChange={onChange}
......@@ -24,10 +27,16 @@ const SlackForm = ({ onChange, profile, onSlackURLSubmit }) => {
placeholder="슬랙 Webhook URL"
label="슬랙 Webhook URL"
/>
<Button variant="outlined" type="submit">
등록
</Button>
</form>
<Button
className={classes.button}
onClick={onSlackURLSubmit}
variant="outlined"
type="submit"
color="primary"
>
등록
</Button>
</div>
);
};
......
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { withRouter } from 'react-router-dom';
import ChallengeForm from '../../components/challenge/ChallengeForm';
const ChallengeContainer = () => {
return <div></div>;
};
export default ChallengeContainer;
......@@ -2,22 +2,35 @@ import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { withRouter } from 'react-router-dom';
import HomeForm from '../../components/home/HomeForm';
import { getPROFILE } from '../../modules/profile';
import { getPROFILE, initializeProfile } from '../../modules/profile';
const HomeContainer = ({ history }) => {
const dispatch = useDispatch();
const { user, profile } = useSelector(({ user, profile }) => ({
user: user.user,
profile: profile,
}));
useEffect(() => {
if (!user) {
alert('로그인이 필요합니다 ');
history.push('/login');
} else {
let username = user.username;
dispatch(getPROFILE({ username }));
return () => {
dispatch(initializeProfile());
};
}
}, [dispatch, user, history]);
useEffect(() => {
console.log(profile);
}, [profile.solvedBJ]);
}, [profile]);
useEffect(() => {
if (user) {
let username = user.username;
dispatch(getPROFILE({ username }));
}
}, [dispatch, user]);
return <HomeForm />;
return <HomeForm PSdata={profile.solvedBJ_date} goalNum={profile.goalNum} />;
};
export default withRouter(HomeContainer);
......
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { withRouter } from 'react-router-dom';
import {
changeField,
......@@ -14,6 +15,7 @@ import SettingForm from '../../components/setting/SettingForm';
const SettingContainer = ({ history }) => {
const [isLoading, setLoading] = useState(false);
const dispatch = useDispatch();
const { user, profile, loading } = useSelector(
({ user, profile, loading }) => ({
......@@ -63,7 +65,7 @@ const SettingContainer = ({ history }) => {
useEffect(() => {
if (!user) {
alert('로그인이 필요합니다 ');
history.push('/');
history.push('/login');
} else {
let username = user.username;
dispatch(getPROFILE({ username }));
......@@ -81,16 +83,18 @@ const SettingContainer = ({ history }) => {
}, [dispatch, loading]);
return (
<SettingForm
type="setting"
onChange={onChange}
onBJIDSubmit={onBJIDSubmit}
onSyncBJIDSubmit={onSyncBJIDSubmit}
onSlackURLSubmit={onSlackURLSubmit}
onGoalNumSubmit={onGoalNumSubmit}
profile={profile}
isLoading={isLoading}
></SettingForm>
<div>
<SettingForm
type="setting"
onChange={onChange}
onBJIDSubmit={onBJIDSubmit}
onSyncBJIDSubmit={onSyncBJIDSubmit}
onSlackURLSubmit={onSlackURLSubmit}
onGoalNumSubmit={onGoalNumSubmit}
profile={profile}
isLoading={isLoading}
></SettingForm>
</div>
);
};
......
import React from 'react';
import HeaderContainer from '../containers/common/HeaderContainer';
import ChallengeContainer from '../containers/challenge/ChallengeContainer';
const ChallengePage = () => {
return (
<div>
<HeaderContainer />
<ChallengeContainer />
</div>
);
};
export default ChallengePage;
# Jaksimsamil API Documentation
## Overview
- TBA
## URL
- TBA
## Usage
- TBA
## Example
- TBA
## API Table
| group | description | method | URL | Detail | Auth |
| ------- | --------------------------------- | ------ | ----------------------- | -------- | --------- |
| user | 유저 등록 | POST | api/user | 바로가기 | JWT Token |
| user | 유저 삭제 | DELETE | api/user:id | 바로가기 | JWT Token |
| user | 특정 유저 조회 | GET | api/user:id | 바로가기 | None |
| user | 전체 유저 조회 | GET | api/user | 바로가기 | JWT Token |
| friend | 유저 친구 등록 | POST | api/friend | 바로가기 | JWT Token |
| friend | 유저의 친구 조회 | GET | api/friend:id | 바로가기 | None |
| profile | 유저가 푼 문제 조회(백준) | GET | api/profile/solvedBJ:id | 바로가기 | None |
| profile | 유저가 푼 문제 동기화(백준) | PATCH | api/profile/syncBJ | 바로가기 | None |
| profile | 유저 정보 수정 | POST | api/profile/setprofile | 바로가기 | JWT TOKEN |
| profile | 유저 정보 받아오기 | POST | api/profile/getprofile | 바로가기 | JWT |
| profile | 추천 문제 조회 | POST | api/profile/recommend | 바로가기 | None |
| notify | 슬랙 메시지 전송 요청 (성취여부) | POST | api/notify/goal | 바로가기 | Jwt Token |
| notify | 슬랙 메시지 전송 요청 (문제 추천) | POST | api/notify/recommend | 바로가기 | None |
| auth | 로그인 | POST | api/auth/login | 바로가기 | None |
| auth | 로그아웃 | POST | api/auth/logout | 바로가기 | JWT Token |
| auth | 회원가입 | POST | api/auth/register | 바로가기 | None |
| auth | 로그인 확인 | GET | api/auth/check | 바로가기 | None |
# Jaksimsamil Server Documentation
## Overview
- KOA 프레임워크 기반의 REST-API로 동작합니다.
- API 문서는 아래를 참고해주세요.
## Usage
- Starting Server
```
npm install
npm update
node index.js
```
## Example
```
POST http://facerain.dcom.club/profile/getprofile
{
username: 'syw5141',
}
```
## API Table
| group | description | method | URL | Detail | Auth |
| ------- | -------------------------------------- | ------ | ----------------------- | -------------------------------------- | --------- |
| profile | 유저가 푼 문제 조회(백준) | GET | api/profile/solvedBJ:id | [바로가기](/src/api/profile/README.md) | None |
| profile | 유저가 푼 문제 동기화(백준) | PATCH | api/profile/syncBJ | [바로가기](/src/api/profile/README.md) | None |
| profile | 유저 정보 수정 | POST | api/profile/setprofile | [바로가기](/src/api/profile/README.md) | JWT TOKEN |
| profile | 유저 정보 받아오기 | POST | api/profile/getprofile | [바로가기](/src/api/profile/README.md) | JWT |
| profile | 추천 문제 조회 | POST | api/profile/recommend | [바로가기](/src/api/profile/README.md) | None |
| profile | 친구 추가 | POST | /api/profile/addfriend | [바로가기](/src/api/profile/README.md) | JWT TOKEN |
| notify | 슬랙 메시지 전송 요청 (목표 성취 여부) | POST | api/notify/goal | [바로가기](/src/api/notify/README.md) | Jwt Token |
| notify | 슬랙 메시지 전송 요청 (문제 추천) | POST | api/notify/recommend | [바로가기](/src/api/notify/README.md) | None |
| auth | 로그인 | POST | api/auth/login | [바로가기](/src/api/auth/README.md) | None |
| auth | 로그아웃 | POST | api/auth/logout | [바로가기](/src/api/auth/README.md) | JWT Token |
| auth | 회원가입 | POST | api/auth/register | [바로가기](/src/api/auth/README.md) | None |
| auth | 로그인 확인 | GET | api/auth/check | [바로가기](/src/api/auth/README.md) | None |
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
......@@ -5,7 +5,7 @@
"license": "MIT",
"dependencies": {
"axios": "^0.19.2",
"bcrypt": "^4.0.1",
"bcrypt": "^3.0.0",
"body-parser": "^1.19.0",
"cheerio": "^1.0.0-rc.3",
"cookie-parser": "^1.4.5",
......@@ -15,11 +15,11 @@
"iconv": "^3.0.0",
"joi": "^14.3.1",
"jsonwebtoken": "^8.5.1",
"koa": "^2.12.0",
"koa": "^2.13.0",
"koa-bodyparser": "^4.3.0",
"koa-morgan": "^1.0.1",
"koa-router": "^9.0.1",
"mongoose": "^5.9.17",
"mongoose": "^5.9.20",
"morgan": "^1.10.0",
"node-schedule": "^1.3.2",
"path": "^0.12.7",
......@@ -29,11 +29,11 @@
},
"devDependencies": {
"babel-eslint": "^10.1.0",
"eslint": "^7.1.0",
"eslint": "^7.3.1",
"nodemon": "^2.0.4"
},
"scripts": {
"start": "node src",
"start": "node ./index.js",
"start:dev": "nodemon --watch src/ src/index.js"
}
}
......
This diff could not be displayed because it is too large.
const mongoose = require("mongoose");
const { Schema } = mongoose;
const GroupSchema = new Schema({
members: { type: [String] },
});
const ChallengeSchema = new Schema({
challengeName: { type: String, required: true },
startDate: { type: Object, required: true },
endDate: { type: Object, required: true },
durationPerSession: { type: String, required: true }, // '1d' means one day per session, '2w' means 2 weeks per session, '3m' means 3 months per session.
goalPerSession: { type: Number, required: true }, // number of problems for one session
groups: { type: [GroupSchema], required: true }, // groups attending challenge, group of only one member supposed to be single
});
ChallengeSchema.statics.findByChallengeName = function (challengeName) {
return this.findOne({ challengeName: challengeName });
};
ChallengeSchema.methods.addNewGroup = function (group) {
this.groups.push(group);
return this.save();
};
ChallengeSchema.methods.removeGroup = function (group_id) {
const idx = this.groups.findIndex((item) => item._id === group_id);
this.groups.splice(idx, 1);
return this.save();
};
ChallengeSchema.methods.getChallengeName = function () {
return this.challengeName;
};
ChallengeSchema.methods.getStartDate = function () {
return this.startDate;
};
ChallengeSchema.methods.getEndDate = function () {
return this.endDate;
};
ChallengeSchema.methods.getDurationPerSession = function () {
return this.durationPerSession;
};
ChallengeSchema.methods.getGoalPerSession = function () {
return this.goalPerSession;
};
ChallengeSchema.methods.getGroups = function () {
return this.groups;
};
ChallengeSchema.methods.serialize = function () {
return this.toJSON();
};
const Challenge = mongoose.model("Challenge", ChallengeSchema);
module.exports = Challenge;
const mongoose=require('mongoose');
const {Schema}=mongoose;
const ProblemSchema=new Schema({
problemNum: {type: Number, required: true, unique: true},
problemTitle: {type: String, required: true},
solvedacLevel: {type: Number},
sumbitNum: {type: Number, required: true},
correctNum: {type: Number, required: true},
category: {type:[String]}
});
ProblemSchema.statics.findByProblemNum=function(problemNum){
return this.findOne({problemNum:problemNum});
}
ProblemSchema.methods.addCategory=function(category){
this.category.push(category);
return this.save();
}
ProblemSchema.methods.removeCategory=function(category){
const idx=this.category.findIndex(item=>item===category);
this.splice(idx,1);
return this.save();
}
ProblemSchema.methods.getProblemNum=function(){
return this.problemNum;
}
ProblemSchema.methods.getProblemTitle=function(){
return this.problemTitle;
}
ProblemSchema.methods.getSolvedacLevel=function(){
return this.solvedacLevel;
}
ProblemSchema.methods.getSumbitNum=function(){
return this.sumbitNum;
}
ProblemSchema.methods.getCorrectNum=function(){
return this.correctNum;
}
ProblemSchema.methods.getCategory=function(){
return this.category;
}
ProblemSchema.methods.serialize=function(){
return this.toJSON();
}
const Problem=mongoose.model('Problem',ProblemSchema);
module.exports=Problem;
\ No newline at end of file
let moment = require("moment");
const problem_set = require("../data/problem_set");
const compareBJ = require("./compareBJ");
exports.analyzeBJ = function (solvedBJ) {
try {
if (solvedBJ) {
......@@ -7,6 +8,7 @@ exports.analyzeBJ = function (solvedBJ) {
let presentDate_str = presentDate.format("YYYYMMDD");
let latestDate = moment(solvedBJ[0].solved_date, "YYYYMMDD");
let difflatest = presentDate.diff(latestDate, "days");
let latestSolve = solvedBJ[0];
let solvedBJbyDATE = {};
for (let i = 0; i < solvedBJ.length; i++) {
......@@ -23,12 +25,44 @@ exports.analyzeBJ = function (solvedBJ) {
presentDate_str in solvedBJbyDATE
? solvedBJbyDATE[presentDate_str].length
: 0;
let weekNUM = 0;
let monthNUM = 0;
let totalNUM = 0;
for (let i = 0; i < solvedBJ.length; i++) {
let diffDate = presentDate.diff(
moment(solvedBJ[i].solved_date, "YYYYMMDD"),
"days"
);
if (diffDate <= 7) {
weekNUM++;
monthNUM++;
totalNUM++;
} else if (diffDate <= 31) {
monthNUM++;
totalNUM++;
} else {
totalNUM++;
}
}
let unsolved_data = compareBJ.compareBJ(
solvedBJ,
problem_set.problem_set
);
let recommend_data = compareBJ.randomItem(unsolved_data);
let returnOBJ = {
latestDate: latestDate.format("YYYYMMDD"),
difflatest: difflatest,
latestNum: latestNum,
presentNum: presentNum,
weekNum: weekNUM,
monthNum: monthNUM,
totalNum: totalNUM,
solvedBJbyDATE: solvedBJbyDATE,
latestSolve: latestSolve,
recommend_data: recommend_data,
};
return returnOBJ;
......
......@@ -14,7 +14,6 @@ exports.compareBJ = function (solvedBJ_new, problem_set) {
new_obj.push(problem_set[i]);
}
}
console.log(new_obj);
return new_obj;
} catch (e) {
console.log(e);
......
......@@ -23,4 +23,4 @@ const test = async (userid) => {
/*
*/
test("jwseo001");
test("thak00");
......
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.