# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# + [markdown] id="sQ8jh5aiy8zo"
# # HOME ASSIGNMENT #6: CLOUD FUNCTION & STREAMLIT
#
# **Mục đích của bài Assignment**
# > *  [Optional] Data Deploy Cloud Function 
# > *  Tạo Data Apps với Streamlit
# > *  Thao tác với data bằng Pandas
# > *  Data Visualization
#
# **Các kiến thức áp dụng**
# * Slack API, JSON to DataFrame
# * GCP Cloud Function
# * Streamlit
# * Python Pandas
# * Python Data Visualization
#
# **Lời Khuyên**
# * Các bạn dành thời gian ôn lại và xâu chuỗi kiến thức
# * Review Assignment 1-5 cho ít nhất 2 bạn học viên khác
# -

# ls ..

# # TODO 1: Python Data Viz
# Hoàn tất các sets bài tập trên [Kaggle Data Visualization](https://www.kaggle.com/learn/data-visualization) - Nếu chưa hoàn thành trong [Assignment 5](https://github.com/anhdanggit/atom-assignments/blob/main/assignment_5/home_assignment_5.ipynb)

# +
# Copy các link Kaggle sau:
## 1. Link tới Kaggle Account của bạn -----> 
## 2. Link tới các bài tập
## DataViz 1: ---> https://www.kaggle.com/lee1368/exercise-hello-seaborn
## DataViz 2: ---> https://www.kaggle.com/lee1368/exercise-line-charts
## DataViz 3: ---> https://www.kaggle.com/lee1368/exercise-bar-charts-and-heatmaps
## DataViz 4: ---> https://www.kaggle.com/lee1368/exercise-scatter-plots
## DataViz 5: ---> https://www.kaggle.com/lee1368/exercise-distributions
## DataViz 6: ---> https://www.kaggle.com/lee1368/exercise-choosing-plot-types-and-custom-styles
## DataViz 7: ---> https://www.kaggle.com/lee1368/exercise-final-project

# + [markdown] id="t28PUQoNzy1k"
# # TODO 2 (OPTIONAL): DEPLOY GOOGLE CLOUD FUNCTION
# * Làm theo Lab của Week 6: [HERE](https://anhdang.gitbook.io/datacracy/atom/6-cloud-function-and-streamlit/6.2-lab-cloud-function-hands-on)
# * Click đôi vào các tab Markdown bên dưới để trả lời các câu hỏi ([Markdown Cheatsheet](https://guides.github.com/features/mastering-markdown/))
# -

# ## Screenshot Cloud Function on GCP
# > *Copy Screenshot vào folder img trong repo, và đổi link bên dưới*
#
# ![Cloud Function Deployed](/img/yourscreenshot.png)



# ## Screenshot Cloud Function Testing on GCP
# > *Copy Screenshot vào folder img trong repo, và đổi link bên dưới*
#
# ![Cloud Function Testing](../img/yourscreenshot.png)



# + [markdown] id="2QUVZlLm00PG"
# ## Screenshot Cloud Function Call on Postman
# > *Copy Screenshot vào folder img trong repo, và đổi link bên dưới*
#
# ![Cloud Function on Postman](../img/yourscreenshot.png)
# -



# + [markdown] id="u5c_Lx9MyzSF"
# ## Các lỗi gặp trong quá trình thực hiện 
# *Liên kê bên dưới các lỗi bạn gặp và các giải quyết*
#
# 1. 
# 2. 
# 3. 
# -



# + [markdown] id="bT_ziqVJ1COI"
# # TODO 3: HIỂU & DIAGRAM CODE STREAMLIT 
# Mình thường khuyên các bạn mới học code rằng:
#
# > Hãy code với một cây bút chì và tờ giấy
#
# Như thế nào?
#
# 1. Đầu tiên, là hình dung trong đầu: Bạn sẽ bắt đầu từ gì (`inputs`) và cho ra những gì (`output`)
# 2. Rồi, để đi từ inputs đến outputs thì bạn cần thực hiện những bước nào (các `functions`)
#
# Bạn có thể vẽ ra một diagram như vậy giúp bạn:
# * Nhìn bức tranh lớn, và không bị sa đà vào tiểu tiết, syntax
# * Rõ ràng hơn về flow 
# * Giúp bạn tối ưu flow trước, rồi sau đó code sẽ thuận lợi hơn
# * Rất hiệu quả để bạn debugs trong tương lai
#
# Tham khảo Diagram sau của [streamlit/data_glimpse.py](https://github.com/anhdanggit/atom-assignments/blob/main/streamlit/data_glimpse.py) và vẽ diagram theo cách hiểu của bạn cho [streamlit/datacracy_slack.py](https://github.com/anhdanggit/atom-assignments/blob/main/streamlit/datacracy_slack.py)
# -

# ## Diagram Data Glimpse Apps
# > Bên dưới là ví dụ Diagram của app [streamlit/data_glimpse.py](https://github.com/anhdanggit/atom-assignments/blob/main/streamlit/data_glimpse.py)
#
# ![data-glimpse-diagream](../img/streamlit-Data-Glimpse-Diagram.png)

# ## DataCracy Slack 
# > Là apps để tổng hợp lịch sử nộp bài, review và discussion của Datacracy Learners
# ![Datacracy-slack-streamlit](../img/dataCracy-slack-streamlit.png)

# ## Diagram DataCracy Slack Apps
# * Xem code của app [streamlit/datacracy_slack.py](https://github.com/anhdanggit/atom-assignments/blob/main/streamlit/datacracy_slack.py)
# > *Copy Diagram bạn vẽ vào folder img trong repo, và đổi link bên dưới*
#
# ![datacracy-slack-diagram](../img/IMG_0912.png)

# ## Giải thích 
# Xem code của app [streamlit/datacracy_slack.py](https://github.com/anhdanggit/atom-assignments/blob/main/streamlit/datacracy_slack.py):
#
# 1. Trong mỗi function (steps) trong Diagram của bạn, giải thích function làm những việc gì?
# 2. Liệt kê các logics được áp dụng để xử lý data?

'''
1. Trong mỗi function, chức năng của từng fucntion là:
load_user_df (channle/ msg): load data từ slack API
process_msg_data: từ bảng msg_df lấy ra những người đã review (reply_users),  thêm tên user và channel
2. Logic áp dụng để xử lý data
- Check invalid user: check trong user_df.user_id == user_id
- Lọc ra toàn bộ thông tin liên quan đến user_id đó (filter_user_id/msg/p_msg_df)
- Tính submission bằng cách lấy msg có latest ts groupby each channel
- Tính review bằng cách: review user_id ra khỏi bảng p_msg_df (bỏ những tin mình tự report), lọc theo channel assignment và người reviewer là learner
- Tính discussion: lấy message trong channel có 'discuss', sort by time (date + time)
- Tính summary:
    Đã nộp: len(submit_df)
    Được review" 100* reply_user_count / total đã nộp
    Đã review: len(review_df)
    Thảo luận: số chữ trong discuss_df
'''

# + id="UccXW_FH4Glg"

# -

# # TODO 4: VISUALIZATION ON STREAMLIT
# Áp dụng kiến thức đã học trong *TODO 1* + Pandas thực hiện các tasks sau:
#
# 1. Tổng hợp cho tất cả learners các chỉ số sau: 
#     * Số assignment đã nộp
#     * % bài được review 
#     * Số workcount đã thảo luận 
#     * Extract thứ trong tuần (weekday) của ngày nộp bài 
#     * Extract giờ trong ngày nộp bài (hour)
#     
# 4. Vẽ biểu đồ thể hiện phân phối (Distribution - [Kaggle Tutorial](https://www.kaggle.com/alexisbcook/distributions)) của các thông số trên và add vào app Streamlit

# +
import streamlit as st
import json
import requests
import sys
import os
import pandas as pd
import numpy as np
import re
from datetime import datetime as dt

# st.set_page_config(layout="wide")

# st.title('DataCracy ATOM Tiến Độ Lớp Học')
with open('env_variable.json','r') as j:
# with open('OneDrive/Documents/GitHub/atom-assignments/streamlit/env_variable.json','r') as j:

    json_data = json.load(j)

#SLACK_BEARER_TOKEN = os.environ.get('SLACK_BEARER_TOKEN') ## Get in setting of Streamlit Share
SLACK_BEARER_TOKEN = json_data['SLACK_BEARER_TOKEN']
DTC_GROUPS_URL = ('https://raw.githubusercontent.com/anhdanggit/atom-assignments/main/data/datacracy_groups.csv')
#st.write(json_data['SLACK_BEARER_TOKEN'])

# @st.cache
def load_users_df():
    # Slack API User Data
    endpoint = "https://slack.com/api/users.list"
    headers = {"Authorization": "Bearer {}".format(json_data['SLACK_BEARER_TOKEN'])}
    response_json = requests.post(endpoint, headers=headers).json() 
    user_dat = response_json['members']

    # Convert to CSV
    user_dict = {'user_id':[],'name':[],'display_name':[],'real_name':[],'title':[],'is_bot':[]}
    for i in range(len(user_dat)):
      user_dict['user_id'].append(user_dat[i]['id'])
      user_dict['name'].append(user_dat[i]['name'])
      user_dict['display_name'].append(user_dat[i]['profile']['display_name'])
      user_dict['real_name'].append(user_dat[i]['profile']['real_name_normalized'])
      user_dict['title'].append(user_dat[i]['profile']['title'])
      user_dict['is_bot'].append(int(user_dat[i]['is_bot']))
    user_df = pd.DataFrame(user_dict) 
    # Read dtc_group hosted in github
    dtc_groups = pd.read_csv(DTC_GROUPS_URL)
    user_df = user_df.merge(dtc_groups, how='left', on='name')
    return user_df

# @st.cache
def load_channel_df():
    endpoint2 = "https://slack.com/api/conversations.list"
    data = {'types': 'public_channel,private_channel'} # -> CHECK: API Docs https://api.slack.com/methods/conversations.list/test
    headers = {"Authorization": "Bearer {}".format(SLACK_BEARER_TOKEN)}
    response_json = requests.post(endpoint2, headers=headers, data=data).json() 
    channel_dat = response_json['channels']
    channel_dict = {'channel_id':[], 'channel_name':[], 'is_channel':[],'creator':[],'created_at':[],'topics':[],'purpose':[],'num_members':[]}
    for i in range(len(channel_dat)):
        channel_dict['channel_id'].append(channel_dat[i]['id'])
        channel_dict['channel_name'].append(channel_dat[i]['name'])
        channel_dict['is_channel'].append(channel_dat[i]['is_channel'])
        channel_dict['creator'].append(channel_dat[i]['creator'])
        channel_dict['created_at'].append(dt.fromtimestamp(float(channel_dat[i]['created'])))
        channel_dict['topics'].append(channel_dat[i]['topic']['value'])
        channel_dict['purpose'].append(channel_dat[i]['purpose']['value'])
        channel_dict['num_members'].append(channel_dat[i]['num_members'])
    channel_df = pd.DataFrame(channel_dict) 
    return channel_df

# @st.cache(allow_output_mutation=True)
def load_msg_dict():
    endpoint3 = "https://slack.com/api/conversations.history"
    headers = {"Authorization": "Bearer {}".format(SLACK_BEARER_TOKEN)}
    msg_dict = {'channel_id':[],'msg_id':[], 'msg_ts':[], 'user_id':[], 'latest_reply':[],'reply_user_count':[],'reply_users':[],'github_link':[],'text':[]}
    for channel_id, channel_name in zip(channel_df['channel_id'], channel_df['channel_name']):
        print('Channel ID: {} - Channel Name: {}'.format(channel_id, channel_name))
        try:
            data = {"channel": channel_id} 
            response_json = requests.post(endpoint3, data=data, headers=headers).json()
            msg_ls = response_json['messages']
            for i in range(len(msg_ls)):
                if 'client_msg_id' in msg_ls[i].keys():
                    msg_dict['channel_id'].append(channel_id)
                    msg_dict['msg_id'].append(msg_ls[i]['client_msg_id'])
                    msg_dict['msg_ts'].append(dt.fromtimestamp(float(msg_ls[i]['ts'])))
                    msg_dict['latest_reply'].append(dt.fromtimestamp(float(msg_ls[i]['latest_reply'] if 'latest_reply' in msg_ls[i].keys() else 0))) ## -> No reply: 1970-01-01
                    msg_dict['user_id'].append(msg_ls[i]['user'])
                    msg_dict['reply_user_count'].append(msg_ls[i]['reply_users_count'] if 'reply_users_count' in msg_ls[i].keys() else 0)
                    msg_dict['reply_users'].append(msg_ls[i]['reply_users'] if 'reply_users' in msg_ls[i].keys() else 0) 
                    msg_dict['text'].append(msg_ls[i]['text'] if 'text' in msg_ls[i].keys() else 0) 
                    ## -> Censor message contains tokens
                    text = msg_ls[i]['text']
                    github_link = re.findall('(?:https?://)?(?:www[.])?github[.]com/[\w-]+/?', text)
                    msg_dict['github_link'].append(github_link[0] if len(github_link) > 0 else None)
        except:
            print('====> '+ str(response_json))
    msg_df = pd.DataFrame(msg_dict)
    return msg_df

def process_msg_data(msg_df, user_df, channel_df):
    ## Extract 2 reply_users
    msg_df['reply_user1'] = msg_df['reply_users'].apply(lambda x: x[0] if x != 0 else '')
    msg_df['reply_user2'] = msg_df['reply_users'].apply(lambda x: x[1] if x != 0 and len(x) > 1 else '')
    ## Merge to have a nice name displayed
    msg_df = msg_df.merge(user_df[['user_id','name','DataCracy_role']].rename(columns={'name':'submit_name'}), \
        how='left',on='user_id')
    msg_df = msg_df.merge(user_df[['user_id','name']].rename(columns={'name':'reply1_name','user_id':'reply1_id'}), \
        how='left', left_on='reply_user1', right_on='reply1_id')
    msg_df = msg_df.merge(user_df[['user_id','name']].rename(columns={'name':'reply2_name','user_id':'reply2_id'}), \
        how='left', left_on='reply_user2', right_on='reply2_id')
    ## Merge for nice channel name
    msg_df = msg_df.merge(channel_df[['channel_id','channel_name','created_at']], how='left',on='channel_id')
    ## Format datetime cols
    msg_df['created_at'] = msg_df['created_at'].dt.strftime('%Y-%m-%d')
    msg_df['msg_date'] = msg_df['msg_ts'].dt.strftime('%Y-%m-%d')
    msg_df['msg_time'] = msg_df['msg_ts'].dt.strftime('%H:%M')
    msg_df['wordcount'] = msg_df.text.apply(lambda s: len(s.split()))
    return msg_df



# -

user_df = load_users_df()
channel_df = load_channel_df()
msg_df = load_msg_dict()

total = process_msg_data(msg_df, user_df, channel_df)
total.head()

# +
as_total = total[total.channel_name.str.contains('assignment')]
as_total = as_total[as_total.DataCracy_role.str.contains('Learner')]
as_by_channel = as_total.groupby(by=(['user_id','submit_name','channel_id','channel_name']))['github_link'].nunique()

as_by_user = as_by_channel.groupby(by=(['user_id','submit_name'])).count()
as_by_user = as_by_user.sort_values(ascending=False)
# as_by_user.set_axis(['user_id','username','assignments'],axis='columns')                                     
as_by_user = as_by_user.reset_index()
as_by_user.head()

# -

dis_total = total[total.channel_name.str.contains('discuss')]
dis_total = dis_total[dis_total.DataCracy_role.str.contains('Learner')]
user_wordcount = dis_total.groupby(by=(['user_id','submit_name'])).wordcount.sum()
user_wordcount = user_wordcount.sort_values(ascending=False)
user_wordcount = user_wordcount.reset_index()
user_wordcount.head()
len(user_wordcount)

as_total.head()

reviewed_total = as_total[(as_total.reply_users != 0) & \
                          ((as_total.reply_user1 != as_total.user_id) | (as_total.reply_user2 != as_total.user_id))]
reviewed_total=reviewed_total.loc[reviewed_total.groupby(by=(['user_id','submit_name','channel_id'])).msg_ts.idxmax()]
# reviewed_total = reviewed_total.reset_index().sort_values(by='channel_id',ascending=False)
# reviewed_total.rename(columns = {'channel_id':'no of be reviewed'})
reviewed_total = reviewed_total.groupby(by=(['user_id','submit_name'])).channel_id.nunique().reset_index()
reviewed_total = reviewed_total.rename(columns = {'channel_id':'time be reviewed'})
reviewed_total.head()
len(reviewed_total)

final = as_by_user.merge(user_wordcount, how='left',left_on=('user_id','submit_name'), right_on = ('user_id','submit_name'))
final = final.merge(reviewed_total, how='left',left_on=('user_id','submit_name'), right_on = ('user_id','submit_name'))
final = final.rename(columns={'github_link':'no of assignments'})
final['% be reviewed'] = final['time be reviewed']/final['no of assignments']*100
final['% be reviewed']=final['% be reviewed'].round(decimals=1)
final = final.sort_values(by='no of assignments',ascending=False)
final.head()

pd.plotting.register_matplotlib_converters()
import matplotlib.pyplot as plt
# %matplotlib inline
import seaborn as sns

sns.distplot(a=final['no of assignments'], kde=False)

sns.distplot(a=final['wordcount'], kde=False)

sns.distplot(a=final['% be reviewed'], kde=False)


sns.jointplot(x=final['no of assignments'], y=final['time be reviewed'], kind='kde')

sns.regplot(x=final['no of assignments'], y=final['time be reviewed'])

sns.regplot(x=final['no of assignments'], y=final['wordcount'])

new_as_total=as_total.loc[as_total.groupby(by=(['user_id','submit_name','channel_id'])).msg_ts.idxmax()]
ts_time = new_as_total.msg_ts.reset_index()
ts_time['dayofweek'] = ts_time.msg_ts.dt.dayofweek
ts_time['day_name'] = ts_time.msg_ts.dt.day_name()
ts_time['hour'] = ts_time.msg_ts.dt.hour
ts_time

ts_time.dt.day_name()

day_dis = sns.distplot(a=ts_time['dayofweek'], kde=False)
day_dis.set_xticklabels(['','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'])
for item in day_dis.get_xticklabels(): item.set_rotation(90)

sns.distplot(a=ts_time['hour'], kde=False)
plt.xticks([0,6, 12, 18,24])

# link file streamlit: https://github.com/lethuthao1368/atom-assignments/blob/main/streamlit/datacracy_slack-thaole.py
from PIL import Image
for i in range(4):
    k = Image.open(f'streamlit_{i+1}.png')
    display(k)




StarcoderdataJupyterScriptsDedupFiltered