ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 할일목록(TODO) 앱 5 - chart.js 로 그룹핑한 데이터 그래프로 그리기
    프로젝트/할일목록(TODO) 앱 2023. 8. 9. 01:34
    728x90

    https://www.chartjs.org/docs/latest/getting-started/

     

    Getting Started | Chart.js

    Getting Started Let's get started with Chart.js! Alternatively, see the example below or check samples. Create a Chart In this example, we create a bar chart for a single dataset and render it on an HTML page. Add this code snippet to your page: You should

    www.chartjs.org

    https://www.chartjs.org/docs/latest/general/colors.html

     

    Colors | Chart.js

    Colors Charts support three color options: for geometric elements, you can change background and border colors; for textual elements, you can change the font color. Also, you can change the whole canvas background. Default colors If a color is not specifie

    www.chartjs.org

     

     

    * 주의할점

    토큰은 언제나 변경되므로 새로 발급받은 다음에 수업을 진행하도록 한다. 

    반드시 관리자 토큰을 사용해야 한다.

    CORS 에러가 나면 아래와 같이 서버쪽 코드를 변경해서 브라우저 요청(live server)을 허용하도록 한다.

    const corsOptions = {
        origin: '*',
        credentials: true 
    }

     

    * TODO 그룹핑 후 정렬하기 - 리팩토링 

    router.get('/group/:field', isAuth, isAdmin, expressAsyncHandler(async (req, res, next) => {
       
        if(req.params.field === 'category' || req.params.field === 'isDone'){
            const docs = await Todo.aggregate([
                {
                    $group: {
                        _id: `$${req.params.field}`,
                        count: { $sum: 1 }
                    }
                },
                { $sort : { _id : 1} }
            ])
            console.log(`Number Of Group: , ${docs.length}`) // 그룹 갯수 
            // docs.sort((d1, d2) => d1._id - d2._id) // _id 기준으로 정렬
            res.json({ code: 200, docs })
        }else{
            res.status(400).json({ code: 400, message: 'you gave wrong field to group documents !'})
        }
    }))
    router.get('/group/mine/:field', isAuth, expressAsyncHandler(async (req, res, next) => { // 사용자 대쉬보드
        if(req.params.field === 'category' || req.params.field === 'isDone'){
            const docs = await Todo.aggregate([
                {
                    $match: { author: new ObjectId(req.user._id) } // 내가 작성한 할일목록 필터링
                },
                {
                    $group: {
                        _id: `$${req.params.field}`,
                        count: { $sum: 1 }
                    }
                },
                { $sort : { _id : 1} }
            ])
            console.log(`Number Of Group: , ${docs.length}`) // 그룹 갯수 
            // docs.sort((d1, d2) => d1._id - d2._id) // _id 기준으로 정렬
            res.json({ code: 200, docs })
        }else{
            res.status(400).json({ code: 400, message: 'you gave wrong field to group documents !'})
        }
    }))
    router.get('/group/date/:field', isAuth, isAdmin, expressAsyncHandler(async (req, res, next) => {
       
        if(req.params.field === 'createdAt' || 
            req.params.field === 'lastModifiedAt' ||
            req.params.field === 'finishedAt'){
            const docs = await Todo.aggregate([
                {
                    $group: {
                        _id: { year: { $year: `$${req.params.field}`}, month: { $month: `$${req.params.field}` }},
                        count: { $sum: 1 }
                    }
                },
                { $sort : { _id : 1} }
            ])
            console.log(`Number Of Group: ${docs.length}`) // 그룹 갯수
            // docs.sort((d1, d2) => d1._id - d2._id)
            res.json({ code: 200, docs})
            }else{
            res.status(400).json({ code: 400, message: 'you gave wrong field to group documents !' })
            }
        
    }))
    router.get('/group/mine/date/:field', isAuth, expressAsyncHandler(async (req, res, next) => {
        if(req.params.field === 'createdAt' || 
               req.params.field === 'lastModifiedAt' ||
               req.params.field === 'finishedAt'){
                const docs = await Todo.aggregate([
                    {
                        $match: { author: new ObjectId(req.user._id) }
                    },
                    {
                        $group: {
                            _id: { year: { $year: `$${req.params.field}`}, month: { $month: `$${req.params.field}` }},
                            count: { $sum: 1 }
                        }
                    },
                    { $sort : { _id : 1} }
                ])
                console.log(`Number Of Group: ${docs.length}`) // 그룹 갯수
                // docs.sort((d1, d2) => d1._id - d2._id)
                res.json({ code: 200, docs})
               }else{
                res.status(400).json({ code: 400, message: 'you gave wrong field to group documents !' })
               }
    }))

    server > src > routes > todos.js 파일의 그룹핑 코드에서 $group 파이프라인 이후 정렬을 위하여 $sort 파이프라인을 적용하는 코드를 추가하였다. _id 기준으로 정렬하기 위하여 위와 같이 코드를 수정하였다. 

     

    * 전체 할일목록 카테고리별 그룹핑

    const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NjM5N2E1YzliYTBmNzE1ZjFlZjZjNmUiLCJuYW1lIjoic3VuQGdtYWlsLmNvbSIsInVzZXJJZCI6InN1bnJpc2UiLCJpc0FkbWluIjp0cnVlLCJjcmVhdGVkQXQiOiIyMDI0LTA1LTA3VDAwOjQ4OjI4LjM3N1oiLCJpYXQiOjE3MTUwNDMxNDksImV4cCI6MTcxNTEyOTU0OSwiaXNzIjoic3VucmlzZSJ9.2fBFKwlA_X5K-SVYXfxIAzpRy_xQcUCIbAZB22nolZw'
    
    fetch('http://localhost:5000/api/todos/group/category', {
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
        }
    }) // GET 방식의 요청 
    .then(res => res.json())
    .then(data => {
        const group = data.docs 
    
      const ctx = document.getElementById('myChart');
    
      new Chart(ctx, {
        type: 'bar',
        data: {
          labels: group.filter(item => item._id).map(item => item._id),
          datasets: [{
            label: '# of category',
            data: group.filter(item => item._id).map(item => item.count),
            borderWidth: 1,
            backgroundColor: '#FFD700',
          }]
        },
        options: {
          scales: {
            y: {
              beginAtZero: true
            }
          },
          plugins: {
            colors: {
              enabled: false
            }
          }
        }
      })
    })

    chart.js - 전체 할일목록 카테고리별 그룹핑

    const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NjM5N2E1YzliYTBmNzE1ZjFlZjZjNmUiLCJuYW1lIjoic3VuQGdtYWlsLmNvbSIsInVzZXJJZCI6InN1bnJpc2UiLCJpc0FkbWluIjp0cnVlLCJjcmVhdGVkQXQiOiIyMDI0LTA1LTA3VDAwOjQ4OjI4LjM3N1oiLCJpYXQiOjE3MTUwNDMxNDksImV4cCI6MTcxNTEyOTU0OSwiaXNzIjoic3VucmlzZSJ9.2fBFKwlA_X5K-SVYXfxIAzpRy_xQcUCIbAZB22nolZw'
    
    fetch('http://localhost:5000/api/todos/group/isDone', {
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
        }
    }) // GET 방식의 요청 
    .then(res => res.json())
    .then(data => {
        const group = data.docs 
    
      const ctx = document.getElementById('myChart');
    
      new Chart(ctx, {
        type: 'bar',
        data: {
          labels: group.filter(item => item._id !== undefined && item._id !== null).map(item => item._id ? "종료" : "진행중"),
          datasets: [{
            label: '# of category',
            data: group.filter(item => item._id !== undefined && item._id !== null).map(item => item.count),
            borderWidth: 1,
            backgroundColor: '#FFD700',
          }]
        },
        options: {
          scales: {
            y: {
              beginAtZero: true
            }
          },
          plugins: {
            colors: {
              enabled: false
            }
          }
        }
      })
    })

    chart.js - 전체 할일목록 할일종료별 그룹핑

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/date/createdAt', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'bar',
            data: {
              labels: group.map(item => `${item._id.year}년 ${item._id.month}`),
              datasets: [{
                label: '# of Todos',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFD700',
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    chart.js - 전체 할일목록 생성날짜별 그룹핑

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/date/lastModifiedAt', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'bar',
            data: {
              labels: group.map(item => `${item._id.year}년 ${item._id.month}`),
              datasets: [{
                label: '# of Todos',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFD700',
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    chart.js - 전체 할일목록 업데이트 날짜별 그룹핑

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/date/finishedAt', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'bar',
            data: {
              labels: group.map(item => `${item._id.year}년 ${item._id.month}`),
              datasets: [{
                label: '# of Todos',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFD700',
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    chart.js - 전체 할일목록 종료날짜별 그룹핑

     

    만약 날짜별 정렬이 제대로 되어 있지 않으면 프론트쪽 코드에서 then 메서드 안에서 아래 코드를 추가한다. 

    const group = data.docs
    console.log(group)
    
    group.sort((a, b) => {
      if(a._id.year > b._id.year) return 1
      else if(a._id.year < b._id.year) return -1
      if(a._id.month > b._id.month) return 1
      else if(a._id.month < b._id.month) return -1
      else 0
    })
    
    // 차트 그리기

     

     

     

    * 특정 사용자의 카테고리별 그룹핑

    관리자가 아닌 특정 사용자의 토큰으로 교체해서 서버에 요청을 보내도록 한다. 

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/mine/category', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'bar',
            data: {
              labels: group.map(item => item._id),
              datasets: [{
                label: '# of Todos By',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFB1C1',
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    chart.js - 특정 사용자의 카테고리별 TODO

     

    * 특정 사용자의 할일종료 여부별 그룹핑

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/mine/isDone', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'bar',
            data: {
              labels: group.map(item => item._id ? "종료": "진행중"),
              datasets: [{
                label: '# of Todos By',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFB1C1',
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    chart.js - 특정 사용자의 할일종료 여부 그룹핑

     

    * 특정 사용자의 TODO 생성날짜별 그룹핑

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/mine/date/createdAt', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'bar',
            data: {
              labels: group.map(item => `${item._id.year}년 ${item._id.month}월`),
              datasets: [{
                label: '# of Todos',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFB1C1',
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    chart.js - 특정 사용자의 TODO 생성날짜별 그룹핑

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/mine/date/lastModifiedAt', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'bar',
            data: {
              labels: group.map(item => `${item._id.year}년 ${item._id.month}월`),
              datasets: [{
                label: '# of Todos',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFB1C1',
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    특정 사용자의 TODO 업데이트 날짜별 그룹핑

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/mine/date/finishedAt', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'bar',
            data: {
              labels: group.map(item => `${item._id.year}년 ${item._id.month}월`),
              datasets: [{
                label: '# of Todos',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFB1C1',
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    특정 사용자의 할일종료 날짜별 그룹핑

     

    * 도넛 그래프 그리기

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/date/finishedAt', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'doughnut',
            data: {
              labels: group.map(item => `${item._id.year}년 ${item._id.month}일`),
              datasets: [{
                label: '# of Todos',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: ['orange', 'purple', 'skyblue', 'green'  ]
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    chart.js - 전체 할일목록 종료 날짜별 도넛 그래프

     

    * 라인 그래프 그리기

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>TODO 분석그래프</title>
    </head>
    <body>
      <div>
        <canvas id="myChart"></canvas>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
      <script>
        const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg'
        const ctx = document.getElementById('myChart');
    
        fetch('http://127.0.0.1:5000/api/todos/group/date/finishedAt', {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token}`
          }
        })
        .then(res => res.json())
        .then(data => {
          const group = data.docs
          console.log(group)
          
          // 차트 그리기
          new Chart(ctx, {
            type: 'line',
            data: {
              labels: group.map(item => `${item._id.year}년 ${item._id.month}일`),
              datasets: [{
                label: '# of Todos',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFD700',
                borderColor: '#FFD700',
              }]
            },
            options: {
              scales: {
                y: {
                  beginAtZero: true
                }
              },
              plugins: {
                colors: {
                  enabled: true
                }
              }
            }
          });
    
        })
      </script>
    </body>
    </html>

    chart.js - 전체 할일목록 종료 날짜별 라인 그래프

     

    * 년, 월 오름차순 정렬하기 

    server > src > routes > todos.js 파일의 해당 부분을 아래와 같이 수정한다.

    router.get('/group/date/:field', isAuth, expressAsyncHandler(async (req, res, next) => { // 어드민 페이지
      if(!req.user.isAdmin){
        res.status(401).json({ code: 401, message: 'You are not authorized to use this service !'})
      }else{
        if(req.params.field === 'createdAt' || req.params.field === 'lastModifiedAt' || req.params.field === 'finishedAt'){
          const docs = await Todo.aggregate([
            {
              $group: {
                _id: { year: { $year: `$${req.params.field}` }, month: { $month: `$${req.params.field}` } },
                count: { $sum: 1 }
              }
            },
            { $sort : { _id : 1 } } // 날짜 오름차순 정렬
          ])
          
          console.log(`Number Of Group: ${docs.length}`) // 그룹 갯수
          docs.sort((d1, d2) => d1._id - d2._id)
          res.json({ code: 200, docs})
        }else{
          res.status(204).json({ code: 204, message: 'No Content'})
        }
      }
    }))
    router.get('/group/mine/date/:field', isAuth, expressAsyncHandler(async (req, res, next) => { // 어드민 페이지
      if(req.params.field === 'createdAt' || req.params.field === 'lastModifiedAt' || req.params.field === 'finishedAt'){
        const docs = await Todo.aggregate([
          {
            $match: { author: new ObjectId(req.user._id) }
          },
          {
            $group: {
              _id: { year: { $year: `$${req.params.field}` }, month: { $month: `$${req.params.field}` } },
              count: { $sum: 1 }
            }
          },
          { $sort : { _id : 1 } } // 날짜 오름차순 정렬
        ])
        
        console.log(`Number Of Group: ${docs.length}`) // 그룹 갯수
        docs.sort((d1, d2) => d1._id - d2._id)
        res.json({ code: 200, docs})
      }else{
        res.status(204).json({ code: 204, message: 'No Content'})
      }
    }))

    그룹핑 결과를 날짜별로 오름차순 정렬한다.

    chart.js - 전체 할일목록 종료 날짜별 라인 그래프 (오름차순 정렬)

     

    * 프로그램적으로 로그인후 그룹핑하기

    랜덤으로 생성한 사용자의 이메일, 비밀번호를 몽고 DB 데이터베이스에서 찾아서 로그인하도록 한다. 

    const BASE_URL = '127.0.0.1:5000'
    async function login(email, password){
        const userJSON = await fetch(`http://${BASE_URL}/api/users/login`, {
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'POST',
            body: JSON.stringify({
                email, password 
            })
        })
        const user = await userJSON.json() 
        return user 
    }
    async function getGroups(field, token){
        const groupJSON = await fetch(`http://${BASE_URL}/api/todos/group/mine/${field}`, {
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`
            },
        })
        const group = await groupJSON.json() 
        return group.docs 
    }
    async function fetchData(){
        const user = await login("enwrvuf@gmail.com", "ajtgnhhpesywh")
        const group = await getGroups("category", user.token)
        return group 
    }
    fetchData()
    .then(group => {
        console.log(group)
    
        // 차트 그리기
        const ctx = document.getElementById('myChart');
        new Chart(ctx, {
            type: 'line',
            data: {
            labels: group.map(item => item._id),
            datasets: [{
                label: '# of Todos',
                data: group.map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFD700',
                borderColor: '#FFD700',
            }]
            },
            options: {
            scales: {
                y: {
                beginAtZero: true
                }
            },
            plugins: {
                colors: {
                enabled: true
                }
            }
            }
        });
    })

     

    * 관리자 사용자로 전체목록 그룹핑하고 그래프로 보여주기

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <div>
            <canvas id="myChart"></canvas>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
        <script>
            async function login(email, password){
                const user = await fetch('http://127.0.0.1:5000/api/users/login', {
                                headers: {
                                    'Content-Type': 'application/json'
                                },
                                method: 'POST',
                                body: JSON.stringify({
                                    email, password
                                })
                            }).then(res => res.json())
                return user 
            }
            async function getGroups(field, token){
                const group = await fetch(`http://127.0.0.1:5000/api/todos/group/${field}`, {
                                    headers: {
                                        'Content-Type': 'application/json',
                                        'Authorization': `Bearer ${token}`
                                    }
                                }).then(res => res.json())
                return group
            }
            async function fetchData(){
                const user = await login("youna@gmail.com", "yona1234") // 로그인
                let group = await getGroups('category', user.token)
                group = group.docs
                return group 
            }
            fetchData()
            .then(group => {
                console.log(group)
    
                // 차트 그리기
                const ctx = document.getElementById('myChart');
                new Chart(ctx, {
                    type: 'line',
                    data: {
                    labels: group.filter(item => item._id).map(item => item._id),
                    datasets: [{
                        label: '# of Todos',
                        data: group.filter(item => item._id).map(item => item.count),
                        borderWidth: 1,
                        backgroundColor: '#FFD700',
                        borderColor: '#FFD700',
                    }]
                    },
                    options: {
                    scales: {
                        y: {
                        beginAtZero: true
                        }
                    },
                    plugins: {
                        colors: {
                        enabled: true
                        }
                    }
                    }
                });
    
            })
        </script>
    </body>
    </html>

    몽고디비에 10000개의 TODO 를 생성해서 전체 할일목록을 카테고리별로 그룹핑해보았다.

     

    * 유니버셜 API 테스트 코드

    const BASE_URL = 'http://127.0.0.1:5000' // 도메인 주소 
    const email = 'enwrvuf@gmail.com' // 사용자 연락처
    const password = 'ajtgnhhpes123@' // 사용자 비밀번호
    const graphType = 'line' // 그래프 종류
    const field = 'createdAt' // 그룹핑 기준 
    
    async function login(email, password){
        const userJSON = await fetch(`${BASE_URL}/api/users/login`, {
            headers: {
                'Content-Type': 'application/json'
            },
            method: 'POST',
            body: JSON.stringify({
                email, password 
            })
        })
        const user = await userJSON.json() 
        return user 
    }
    async function getGroups(field, user){
        let base_url = `${BASE_URL}/api/todos/group`
        if(!user.isAdmin){
            base_url += '/mine'
        }
        if(field === 'createdAt' || field === 'lastModifiedAt' || field === 'finishedAt'){
            base_url += '/date'
        }
        const groupJSON = await fetch(`${base_url}/${field}`, {
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${user.token}`
            }
        })
        const group = await groupJSON.json()
        return group.docs 
    }
    async function fetchData(email, password, field){
        const user = await login(email, password) // 로그인 
        const group = await getGroups(field, user) // 그룹핑
        return group 
    }
    function displayChart(type, group){
        const ctx = document.getElementById('myChart')
        new Chart(ctx, {
            type,
            data: {
            labels: group
            .filter(item => item._id !== null && item._id !== undefined && item._id !== '')
            .map(item => item._id.year ? `${item._id.year}년 ${item._id.month}월` 
                 : typeof item._id === 'boolean' ? (item._id === true ? "종료" : "진행중") 
                 : item._id),
            datasets: [{
                label: '# of Todos',
                data: group
                .filter(item => item._id !== null && item._id !== undefined && item._id !== '')
                .map(item => item.count),
                borderWidth: 1,
                backgroundColor: '#FFD700',
                borderColor: '#FFD700',
            }]
            },
            options: {
            scales: {
                y: {
                beginAtZero: true
                }
            },
            plugins: {
                colors: {
                enabled: true
                }
            }
            }
        });
    
    }
    
    fetchData(email, password, field)
    .then(group => {
        console.log(group)
        displayChart(graphType, group)
    })

     

    * 가로방향 그래프 그리기

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <div>
            <canvas id="myChart"></canvas>
        </div>
        <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
        <script>
            const email = "ycukkza@gmail.com" // 사용자 연락처
            const password = "lvnqlofkwmddh"  // 사용자 비밀번호
            const graphType = 'bar'           // 그래프 종류
            const field = 'category'          // 그룹핑 기준
    
            async function login(email, password){
                const user = await fetch('http://127.0.0.1:5000/api/users/login', {
                                headers: {
                                    'Content-Type': 'application/json'
                                },
                                method: 'POST',
                                body: JSON.stringify({
                                    email, password
                                })
                            }).then(res => res.json())
                return user 
            }
            async function getGroups(field, user){
                let baseUrl = 'http://127.0.0.1:5000/api/todos/group'
                if(!user.isAdmin){
                    baseUrl += '/mine'
                }
                if(field === 'createdAt' || field === 'lastModifiedAt' || field === 'finishedAt'){
                    baseUrl += '/date'
                }
                const group = await fetch(`${baseUrl}/${field}`, {
                                    headers: {
                                        'Content-Type': 'application/json',
                                        'Authorization': `Bearer ${user.token}`
                                    }
                                }).then(res => res.json())
                return group
            }
            async function fetchData(email, password, field){
                const user = await login(email, password) // 로그인
                let group = await getGroups(field, user)
                group = group.docs
                return group 
            }
            function displayChart(type, group){
                // 차트 그리기
                const ctx = document.getElementById('myChart');
                new Chart(ctx, {
                    type,
                    data: {
                    labels: group.filter(item => item._id !== null && item._id !== undefined && item._id !== '').map(item => item._id.year ? `${item._id.year}년 ${item._id.month}월` : typeof item._id === 'boolean' ? (item._id === true ? "종료": "진행중") : item._id),
                    datasets: [{
                        label: '# of Todos',
                        data: group.filter(item => item._id !== null && item._id !== undefined && item._id !== '').map(item => item.count),
                        borderWidth: 1,
                        backgroundColor: '#FFD700',
                        borderColor: '#FFD700',
                    }]
                    },
                    options: {
                        indexAxis: 'y', // 가로방향 그래프
                    scales: {
                        y: {
                        beginAtZero: true
                        }
                    },
                    plugins: {
                        colors: {
                        enabled: true
                        }
                    }
                    }
                });
            }
            fetchData(email, password, field)
            .then(group => {
                console.log(group)
                displayChart(graphType, group)
            })
        </script>
    </body>
    </html>

    728x90
Designed by Tistory.