---
# System prepended metadata

title: 'scenario 1: user has no activity after 15m10s'

---


## BE - TH Book Source
Epic: https://ruanggguru.atlassian.net/browse/RBGR-6610
Implementation:
- Ops provide 2 google sheets to manage the mapping from book source to question.
- BE - Create new endpoint to get book source give question serial
- why do we need the mapping?
    - requested by TH team, due to requirement by their law to show citation source of a problem set that we show
- where will it be shown?
    - will be shown as book source in question details
    - show source description only.
- how big the data?
    - around 10k, based on TH team

### related fields
2 tables
- question_serial
- book_source
- source_type
    - is an enum, 4 possible values
        - exam book
        - textbook
        - official exam
        - mock exam
- book title
- book description
    

Implementation details:
- Create worker to pull book source and question mapping to book source, the worker will pulling the data once a day 
    - [book source](https://docs.google.com/spreadsheets/d/10vnrvvPsMZ2TofxD7PjMWcDCwiu3SkDJ0Hqc2ls8yL4/edit#gid=570812937)
    - [question mapping](https://docs.google.com/spreadsheets/d/1zQP142rX-EK0DphwTSuZygXvBqGZmgE34Q3EJdZhol4/edit#gid=280677941)
### how to fetch data (**choosen option: 2**)
- option1: add fields to existing endpoint (`/question_detail`)
    - pros: 
        - reuse endpoint, not too many
        - single endpoint call from FE
    - cons: 
        - bloated endpoint
        - added complexity
        - added load (need to query new stuff)
    - implementation:
        - worker will periodically/on request fetch data from excel
- option2 (SELECTED): Create new endpoint to get book source given question serial
    - GET /question_detail/book_source/{question_serial}
    - pros:
        - we won't disrupt existing endpoint
        - won't add latency/load
    - cons:
        - more endpoint to maintain
    - implementation:
        - in RDA
        - fetch data from DB
            - `SELECT * from book_questions bq left join books b on b.serial = bq.book_serial where question_serial = "$question_serial"`
        - tables:
            - 1 table for books
            - 1 table for m-n relation
    
### how Ops will manipulate the data
- option 1
    - data entry came from gsheet
    - 3 columns????
        - book serial (ISBN?)
        - question_serial
        - action, enum:
            - CREATE
            - UPDATE
            - DELETE
    - saved on DB, triggered daily or by extension
    - service get whole table, data filtering and processing done in service
    - https://gitlab.com/ruangguru/source/-/blob/master/video-transfer/main.go
    - user will get error feedback via `Error message` column, error is shown per row
    

## bugs from last sprint
- wrong sitemap path
- issue on chatbot



## RBGR-6211 Roboguru RN Banner and Sorting
### [BE] RBGR-6318 Adjust thread list filter to add sortBy createdAt with answerable Thread
- add new sorting param called `CreatedAtGami` with value `ASC` and `DESC`
- new where param in mysql db
```
q.SortBy = "forum_thread.created_at"
db = db.Where("(forum_thread.answer_count < 2")
db = db.Where("forum_thread.discussion_by_expert_counter < 1")
```
- new where param in es db
```
db = db.Must(elastic.NewRangeQuery(rgForumThreadPrefix + "answer_count").Lt(2))
db = db.Must(elastic.NewRangeQuery(rgForumThreadPrefix + "discussion_by_expert_counter").Lt(1))
```

### [BE] RBGR-6322 Adjust modular component to show new banner (new sheet)
- Add new sheet (by ops or product)
- Add new modular component 
    - new id in endpoint modular component. sample
    ```
    curl 'https://roboguru.sirogu.com/api/v3/roboguru-discovery/modular-components' \
  -H 'authority: roboguru.sirogu.com' \
  -H 'accept: application/json' \
  -H 'accept-language: en,id-ID;q=0.9,id;q=0.8,en-US;q=0.7' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -H 'cookie: GCP_IAP_UID=104601912716404164306; _ga_2FWJ6H3WGT=GS1.1.1628577877.24.0.1628577877.60; _ga_Q1ZSM4QR0X=GS1.1.1631633114.2.1.1631633114.0; _hjid=12c1afdc-e7be-4993-80a5-1d8ab676da97; _hjSessionUser_2680642=eyJpZCI6IjRjMGRjYzM2LTViMjYtNTRmNS05MzA2LWU2YmIwYTY4MTkwMCIsImNyZWF0ZWQiOjE2MzcyMjgyNzM0MDcsImV4aXN0aW5nIjp0cnVlfQ==; _hjSessionUser_2674152=eyJpZCI6IjdlMzhlMGExLTM1YjMtNWQ3NS1iZDk2LTk0MDYyM2FjMDdlMSIsImNyZWF0ZWQiOjE2Mzc1Njk5MzA1NDMsImV4aXN0aW5nIjp0cnVlfQ==; role=student; isLoggedIn=false; userID=user5XDETP7KV11A; _roboguruSession=95544c52-4cea-4ee1-a386-306e26a56ff3; _rgSession=9002d4d5-5f68-43ae-9cea-31f4d8e2b033; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJydCI6ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpoYm05dUlqcDBjblZsTENKbGVIQWlPakUyTlRRME9Ea3dNRE1zSW5Wdll5STZJblZ6WlhJMVdFUkZWRkEzUzFZeE1VRWlMQ0p5SWpvaWMzUjFaR1Z1ZENJc0luUnZhMlZ1U1VRaU9pSXhOalV5T1RNNU1ERXpPREUzTWpVNE1qSTRJbjAuUDZHenBpWWljS0ZyWGVwbjZvQkIxRVhuVjZQamc0NmdzRWZpRFg5eVF2YyIsImFub24iOnRydWUsImV4cCI6MTY1Mzk3MDYwMywidW9jIjoidXNlcjVYREVUUDdLVjExQSIsInIiOiJzdHVkZW50IiwidG9rZW5JRCI6IjE2NTI5MzkwMTM4MTcyNTgyMjgifQ.7pJUXRuVx3BYiJzv8qsXRh6H2PxaP5xCReage3_6d0s; refreshToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhbm9uIjp0cnVlLCJleHAiOjE2NTQ0ODkwMDMsInVvYyI6InVzZXI1WERFVFA3S1YxMUEiLCJyIjoic3R1ZGVudCIsInRva2VuSUQiOiIxNjUyOTM5MDEzODE3MjU4MjI4In0.P6GzpiYicKFrXepn6oBB1EXnV6Pjg46gsEfiDX9yQvc; expireToken=1653970423000; _gid=GA1.2.3768689.1653884203; _gat_UA-196723136-1=1; _gat_UA-49650255-35=1; _gat_UA-49650255-1=1; _ga=GA1.2.625588034.1627638456; _ga_MZNBZXV2VM=GS1.1.1653884203.414.1.1653884235.0; _ga_KGEN8KBRBW=GS1.1.1653884203.437.1.1653884235.0; _ga_EN706YSJ4M=GS1.1.1653884203.377.1.1653884235.28' \
  -H 'country: id' \
  -H 'disable-node-proxy: false' \
  -H 'origin: https://roboguru.sirogu.com' \
  -H 'pragma: no-cache' \
  -H 'referer: https://roboguru.sirogu.com/forum/sebutkan-penerapan-polinomial-pada-kehidupan-sehari-hari-dan-berikan-contoh-soalnya_FRM-2RDK876E' \
  -H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: same-origin' \
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36' \
  -H 'with-auth: true' \
  --data-raw '{"id":"roboguru-new-banner","data":{"gradeSerial":"3GAWQ3PJRB","isDummy":false}}' \
  --compressed
    ```
    - create new modular file in CloudFunction. sample
    ```
    const { buildLayouts, getFromSheet } = require('../modules/roboguru-banner')

    module.exports = {
      fn: async (req, cache) => {
        const banners = await cache.fetchSource(getFromSheet({
          gsid: '1LCtuoarxSDWv7SIx1O7hgBi8g7TNivnc_862lFvaM1A',
          sid: "2044131940"
        }))
        return buildLayouts(req, banners);
      },
    };

    ```


## RBGR-4883 MT and Roboexpert can't be banned, RBGR-5464 Backfill Deleted Forum
### description
product & ops need function to backfill deleted content. There are multiple way to delete contents
- user delete their own content (only in old apps)
- MT report & delete content
- MT report & delete content + banned user
### requirements
- run regularly
- deleted content to anon
- deleted content to public
- query by
    - content type (question/answer)
    - user
    - deleted
    - deleted by user
    - answered by expert
    - verified
### request
- POST {rubel-forum}/backfill/deleted-content
- body 
    ```
    {
        query: {
            contentType: answer
            userSerial?: string
            isDeletedByCreator?: bool -> def: false 
            isByMT?: bool -> def: false
            isVerified?: bool -> def: false
        }
        to: user | anon
    }
    ```
### backfill
- delete
### worker
- build specific worker with defined interval to hit endpoint /backfill-deleted-content 
    - deleted

## RLO expiring coin

```

ruko-service-api ruko
rlo-coin rco
rg-notif-payment-center npc
corem

user -> web -> hit npc
npc -> hit ruko
ruko update db <- no longer used, lets use rlo-coin
rlo-coin get from db


```

### things to consider
- cut balance before session
- do something to expire the coins


### things to do
- RBGR-5103: P5  [BE] add expiry date column in db
    - not doing, already has expiry date column
- RBGR-5104: P0  [BE] take expiry date into account when deducting coins
    - mostly in rlo-api
    - expired, will always be at the end of day on the expiry day (23.59)
    - probably use cronjob to zero all expired coin
    - what if a user want/need a refund? no need, actually. P3, or even lets do this when we want to implement expiry by coin
        - opt1: expiring log -> for history purpose only, can be seen by ops 
            - is a table
            - pros1: can store life-time zeroing history
        - opt2: new column, `last balance` to store latest coin before zeroing
            - is a column
            - pros1: can store just enough data for recovery purposes
    - lets say no teacher working at midnight (we don't )
        - no need to handle. datarace wont happened
    - IF there's a poor teacher that accept tutoring session at midnight
        - let the data race happen. brief OPS team to refund the coin
        - do we want to restore money by default?
            - we want to NOT give the coin back
            - check expiry date before refunding. if its before expire time -> refund, else -> no refund
        
- RBGR-5105: P0  [BE] create configurable coin package
    - in rlo-coin
    - frontend hit rlo-api for expired date
    - FE hit rnpcr, npc (rg-notify-payment-center) hit rlo-api. rlo-api store expiry date in db
    - FE hit rnpcr, npc (rg-notify-payment-center) hit CF
- RBGR-5106: P2  [BE] create jarvis task to adjust coin package
    - in rlo-coin
    - ```
        cloudfunction
        gsheet -> modular-component -> rlo-api -> rnpc

        
        DBMSeet -> rlo-api -> r
        jenkins/jarvis -> rlo-api -> DBMS
        ```
    - take option Cloudfunction
    - there's also an option with envvar, via `rogu config edit`
    - can also be hardcoded TBH. lets start with hardcoding in rlo-api
    
- RBGR-5108: P0  [BE] get coin endpoint should also return its expiry
    - in rlo-coin
    - add new field in response
- RBGR-5109: P9999999999  [BE] fetch coin expiry date data
    - same as RBGR-5108. mark duplicate, or not doing
- RBGR-5111: P0  [BE] modify states to show coin expiry
    - in rg-chatbot-roboguru
    - ```
        wa  (whatsapp)
        rg-whatsapp-proxy (rwp)
        rg-chatbot-api (rca)
        rlo-api
        wa -> hit webhook on rwp -> publish event via pubsub -> subscribed by rca -> if need any rlo data, hit rlo-api

           
        ```
    - update existing callback to retrieve expiry data
    - update states to show that data
- RBGR-5115: P0  [BE] revamp coin deduction mechanism, deduct before session
    - in rlo-api
    - should we implement in rlo-coin? No.
        - no, no need more service to access rg_production db
- RBGR-5117: P5  [BE] add coin refund mechanism
    - we can make jarvis job for this, but should we?
    - no.
- RBGR-5118: P5  [BE] add jarvis job to refund coins
    - same argument as RBGR-5117
- RBGR-5132: P0  [BE] update expiry date when buying new coins
    - in rlo-coin
- RBGR-5136: P0  [BE] mechanism to expire coin (reset to 0)
    - in rlo-coin
    - new worker role, to reset expired coin every midnight
    - example can be seen in roboguru-discovery-api, role sitemap
- RBGR-5142: P0  [BE] get coin package endpoint should also return expiry date for that package
    - in rlo-coin
    - this task is specifically made to describe the making of an endpoint




## Verification Mode
rg-forum -> core service -> may expose implementation detail 
- have to have good abstraction

- 
rubel-forum -> product serviceS 
- labels definition 
- query definition -> e.g. what a popular thread is

we'll have 2 new endpoints, and update 1 endpoint in rubel-forum
1. GetVerificationModeThread <- azka
    - probably will reuse lots of things in answermode
2. FreeVerificationModeThread <- azka, grace
    - e.g. FreeAnswerMode,...
    - rubel-forum will wrap the api-call to rg-forum
    - soft delete label (`being-verified`)
    - in rg-forum -> not with sql `DELETE`, but with `UPDATE set deleted_at = NOW(), is_deleted = true`
3. UpdateAnswerLabel <- grace
    - add new label (`IneligibleForVerification`)
    - test by 
        - call endpoint via http or unittest
        - check whether the new label exists in `forum_discussion_label` in rg-forum


## Answer Mode

```sql=
\dt+ forum_thread_label

F1
F2
F3
F4
F5
...
Fn
Fn+1

1 user 


-- every 15 minutes

begin
select * from forum_thread ft left join forum_thread_label ftl on ft.serial = ftl.forum_thread_serial 
where 
    ft.forum_thread_label not in ('F1', 'F2') -- list selected
    AND (not exists ( -- reported
            select 1 from forum_thread_label ftl
            where ftl.deleted_at IS NULL
            AND forum_thread.serial = ftl.forum_thread_serial
            AND ftl.label_serial = 'LBL-2W7TIQF5')
    )
order by created_at
for update;

update forum_thread set is_deleted = 1, deleted_at = now() from forum_thread_label 
where is_deleted = 0
    and (
        now()-updated_at > duration('15m10s') 
        or now()-created_at > duration('60m')
    )
commit

```

expected scenarios:
# scenario 1: user has no activity after 15m10s
```
worker -> check 05.55
azka -> lock 06.00
worker -> check 06.10
azka -> do nothing 06.15
-- anyone is allowed to fetch that question
worker -> check 06.25 -> soft-delete
```
# scenario 2: user has activity before 15m10s
```
worker -> check 05.55
azka -> lock 06.00
worker -> check 06.10
azka -> do something 06.15 -> call endpoint
    -> update forum_thread_label set updated_at = now() 
       where forum_thread_serial = 'F1'
-- people still cant fetch that question
```


## RBGR-4154 Banner Automation
- Refactor cloudfunction `/modular-components`
- Use google sheet as data source
    - Create guidlines to fill gsheet
    - Create column to represent all value in banner
    - Cached ~1 hour

## RBGR-4175 Whatsapp Migration
- only modify template in yaml
- waiting for product team for update

## RBGR-4180 Update Video Concept Behaviors in Question Details

provide link, pointing to Apps
- similar usecase on `GetQuestionDetail` handler
- need new onelink from products, so product can track how big the traffic is
- to get the multiple video detail, can _certainly (from tafaquh's words)_ use rg-video's `ListVideo` function

How To
1. Get UserCode to check videoQuota exceed or not. Impacted to `isLock` in video
```json=
{
    "concepts": [
        "OTHERS"
    ],
    "deeplink": "ruangguru://roboguru-search-result?question=segitiga+sama+sisi\u0026grade=3GAWQ3PJRB\u0026subject=Matematika",
    "image": "https://img-video-tracking.ruangguru.com/20191107105227.png",
    "isLocked": false,
    "onelink": "https://ruangguru.onelink.me/blPk/b12c9c7f?question=segitiga+sama+sisi\u0026grade=3GAWQ3PJRB\u0026subject=Matematika",
    "serial": "RGV-39IJLB2GRT6W",
    "title": "Rasio Dua Bangun Sebangun 1"
}
```
2. Get list video details using `ListVideo` in `rg-videos`
3. Tidy up onelink, deeplink and other response

contract
```protobuf=
rpc GetVideoListDetail(GetVideoListDetailRequest)
  returns (GetVideoListDetailResponse) {
    option (google.api.http) = {
  get : "/videos/concept-question"
};
option (rg.gateway_url) = "/api/v3/roboguru-discovery/videos/concept-question";
option (rg.auth) = {
  enabled : true
  roles : "admin,student,teacher,employee"
};
option (rg.errors) = {
  name : "INTERNAL_ERROR"
  http_status : INTERNAL_SERVER_ERROR
  code : 91101
  message : "internal error"
};
option (rg.errors) = {
    name : "BAD_REQUEST"
    http_status : BAD_REQUEST
    code : 91102
    message : "bad request"
};
option (rg.errors) = {
    name: "FORBIDDEN"
    http_status : FORBIDDEN
    code: 993134
    message: "not allowed"
};
}

message GetVideoListDetailRequest {
   string questionSerial = 1;
   repeated string videoSerial = 2;
   string videoUUID = 3;
   string searchUUID = 4;
}

message GetVideoListDetailResponse {
    // choose one
   map<string, Video> videos = 1;
   repeated Video videos = 1; // recommend using this
}

```


## RBGR-3989 Performance Test W1 jan
### API to be tested
docs: https://ruanggguru.atlassian.net/wiki/spaces/SD/pages/944308510/Social+Learning+Team#Performance-Test
```
scope:
 - We need to test cache and without cache. Might to make unique parameter for each endpoint
 - We need to get at least 100rps 
 - If needeed, we can scale service dependency. ex: rg-user, roboapi
     -  if it's handled by other team we need to notify them
 - We need to test at least 5min to trigger auto scale
 - Define low, normal, high in profile
```
- roboguru-discovery-api
    - SearchQuestion
        - image
        - text
    - GetQuestionDetail
    - GetQuestionList
- rubel-forum
    - GetForumThreadList
    - GetForumThreadTrendingList
    - GetForumThreadDetailByThreadSerial
- rg-forum
    - GetDiscussionByThreadSerialPaging
- rg-content-review 
    - likedislike.GetByUserContentSerials
    - likedislike.GetByUserContentSerialsAndContentTypes

## RBGR-3523 Multi-question and Multi-photo
- Add new field on `SearchQuestion`, ex: `imageURLs: []string`
    - If this send by user it should merge images to 1 image. It's not developed right now but we need to make contract and handle it's request (if given imageURLs it will return bad request not yet implemented)

## RBGR-3991 Comment in Question Page
- roboguru-discovery-api
    - add endpoint to get list of rating with comment is not empty
        - filter by comment is not null
        - sort desc/asc by rating or date 
        - enable pagination
        
        
## RBGR-3988 Limit Notification
@tafaquh 

## RBGR-3512 [Web MT] Filter for Subscribers Question
@azka

## RBGR-3998 A/B Test WA RLO
@azka might breakdown later

## RBGR-3702 Fix RLO booking issues
@azka might breakdown later

## RBGR-3032 Revamp Answer Page & Feedback Mechanism
 - People: Azka, Ridwan
### Implementation
- Integrate with questionView/pageView endpoint
    - docs tracker api: [quip](https://quip.com/W9WqAHekpBJt/Tracking-Events)
        - kalo endpoint ga diprovide pake elasticsearch (harusnya sih udah ada, tapi kalo gaada ya setup etl nya pake datamesh)
    - eventName: `roboguruQuestion`
    - gaperlu terlalu realtime
        - invalidate misalnya 1 menit / question
            - key: `questionPageCount:QU-ASDSD -> 12345`
    - bulk fetch ketika ngehit tracker (jangan hit tracker dalam loop)
    - affected endpoints:
        - question detail
            - similar question
            ```
            {
              ...,
              question: {
               ...,
               viewCount: 500
              },
              similarQuestions: [
               ...,
               {
                 ...,
                 viewCount: 324
               }
              ]
            }
            ```
        - question list (homepage)
            ```
            {
              ...,
              questions: [
                  question: {
                   ...,
                   viewCount: 500
                  },
              ]
            }
            ```
        - search question
        ```
            {
              ...,
              question: [{
               ...,
               viewCount: 500
                 },
              ],
              ....
            }
            ```
 - Detect question answer from pembahasan
     - match keyword, one of the following (detail: https://ruanggguru.atlassian.net/wiki/spaces/ROB/pages/2328035640/PRD+Revamp+Answer+Page+and+Search+Result#As-user%2C-I-can-see-the-answer.)
         - Jadi
         - Oleh karena itu
         - Oleh sebab itu
         - Dengan Demikian
         - Maka dari itu
         - In conclusion
         - Therefore
         - To conclude
         - To sum up
         - In summary
         - Thus
         - hence
         - in other words
         - Sehingga
         - Dalam hal itu
         - Karena itu
         - Berdasarkan penjelasan diatas
     - on the last sentence on the last paragraph
         - if not found then return null
     - pembahasan dalam bentuk html
         - e.g. `Jadi 2^10 hasilnya <style class=.>1024</style>.`
     - possible algorithm
         - use last 1st child html tag && contains conclusion word (jadi, hence, ...)
         - render html into plain text. split by paragraph and by sentences (by double new line and by punctuation, respectively). in last paragraph, find special keyword.
             - will remove styling
             - how to handle image? cant be handled
         - 
     - on field `db: rg-questionbank.question.text`
 - Question Rating
     - Dari fe 
         - kalau ada yang love
             - langsung kasih rating 5
             - nembak 2 endopint
                 - rating 5
                 - like
         - kalau rating n >= 3
             - langsung kasih love
             - nembak 2 endopint
                 - rating n
                 - like
         - kalau rating n <3
             - ga kasih love
             - nembak 1 endopint
                 - rating n
     - implementation
         - rg-content-review already has everything we need. can we just use it? @cxBeu4wPQ4afLURnyse_SQ 
           ```go
           // POST
           type SetRatingRequest struct {
                ContentSerial string `json:"contentSerial" validate:"required"`
                Rating        int    `json:"rating""`
                Comment       string `json:"comment"`
                Source        string `json:"source" validate:"required"`
            }

            // GET
            authenticatedEmployeeRoute.GET("/rating/aggregates", ratingHandler.GetRatingAggByCourseSerials)
           ```
         - affected endpoints
            - question list (homepage)
            - question detail (homepage)
            - search question
        
         - rda
             - wrap robo-forum
         - robo-forum
             - wrap content_review for get
             - wrap content_review for post
           
         
 - Question Rating Backfill Migration
     - Convert existing love to 5 stars
     - Create onetime script to execute
    
 - Create endpoint give question rating
     - new contracts for :
    ```
    rpc SendQuestionRating(SendQuestionRatingRequest)
      returns (google.protobuf.Empty) {
        option (google.api.http) = {
          post : "/rating"
          body: "*"
        };
        option (rg.gateway_url) = "/api/v3/roboguru-discovery/rating";
        option (rg.auth) = {
          enabled : true
          roles : "admin,student,teacher,employee"
        };
        option (rg.errors) = {
          name : "INTERNAL_ERROR"
          http_status : INTERNAL_SERVER_ERROR
          code : 91101
          message : "internal error"
        };
        option (rg.errors) = {
          name : "BAD_REQUEST"
          http_status : BAD_REQUEST
          code : 91102
          message : "bad request"
        };
      }
    
    message SendQuestionRatingRequest {
      string questionSerial = 1;
      int rating = 2;
    }
    ```

## RBGR-3296 DS Support tracker, passage, e2e
 - People: Tafaquh
### Implementation
 - Generate passage on image generator
     - Join question with question passage, then run image generator
     ![](https://i.imgur.com/3MAUQPc.png)
     - Ask assassment team to scale db when generate image
 - Add searchUUID tracker to 4xx response from roboguruapi.searchQuestion
     - tracker name `message_command_roboguru` , `roboguru_unknown_subject` , `roboguru_short_question` , `roboguru_multiple_question` , `roboguru_question_not_found`

## RBGR-3299 Information Section in Roboguru Homepage
 - People: Tafaquh
### Implementation
 - Adjust information endpoint
     - api: new/rubelforum/information

## [BE] WA abc test:
 - Effort: 5 (2-3 day) 
 - People: 1
 
## [BE] Deleted Qns in Forum:
 - Effort: 8 (5 day)
 - Notes:
    user bisa punya question, question ini bisa di hapus. Ketika dihapus 
     - Data tetap ada di list
        - Hide identity user yang post
     - Data hilang dari pertanyaan saya
     - Data tetap ada dari jawaban saya apabila user me-reply question yang dihapus
     - Remove notifikasi terhadap pembuat question
      - on comment
      - on like
 - Concern:
  - Takut bakalan ganti2 requirement
  - QA bakal banyak regression
 - People: 1
### Implementation

#### Delete Question
 - Instead of we change the current delete function or add new delete endpoint. We can use update label instead.
     - ~~OPS 1 - DEL `{rg-forum}}/forum/thread/serial/{forumThreadSerial}` to UPDATE `{{rubel-forum}}/rubelforum/forum/question/status-label`~~
     - OPS 2 - Add new endpoint DEL `{rubel-forum}}/forum/thread/{forumThreadSerial}` proxy to {{rg-forum}} UPDATE label on rg-forum
     - When a user deletes a thread, the system must add a new label in `forum_thread_label` for a specific forum serial.
     - We must add a new label called `deleted` on table `forum_thread_label`
 - on thread response it should return anonymous 
     ```
         {
           data: {
           ...,
           userSerial: "",
           username: "anon",
           userTag: {},
           imageUrl: "" // avatar ijo
           }
         }
     ```
#### Adjust Thread History
- Add new condition on function `GetQuestionByMe` `rubel-forum` line 45
    ```
    query.FilterByLabels = query.FilterByLabels.Add(entity.LabelHidden, false)
	if q.FilterByCreatedByMe {
		query.FilterByLabels = query.FilterByLabels.Add(entity.LabelDeleted, false)
	}
    ```
- Add new condition based on deleted label `repository/question_repository/dto.go`
    ```
    func (t *pbQuestionResponse) ToEntity(cfg *entity.QuestionQueryConfig, pageSize int) *entity.QuestionsPaging
    ```

#### Adjust Thread CreatedByUser
- Change `username` to `anon` when label string list contains `deleted` label. The code is [here](https://gitlab.com/ruangguru/source/-/blob/master/rubel-forum/repository/question_repository/dto.go#L215)

    ```
    CreatedByUser: entity.CreatedByUser{
					Username:     thread.Username,
					Serial:       thread.CreatedBy,
					Subscription: entity.UserSubscription{},
					Tag:          entity.UserTag{},
					PhotoURL:     thread.ImageURL,
				},

    ```
#### Disable Notification
- Adjust endpoint for like. Disable send notif when userSerial thread is anonymous
    -  `GiveLikeDislikeForumThreadBySerial` forum-thread/service.go
    -  `CreateForumDiscussion` forum-discussion/service.go
- Notes:
    - Notify product team with case notification worker will works every 10 minutes, what if, in 10 minutes cycle, 
      some other user commented in minute 1, minute 2 and minute 3, and poster deleted the thread on minute 5? the notification
      will still be sent to poster on minute 10 due to existing notifcationLog before the thread is marked as deleted


## [BE] WA A/B/C/D Test Search Limit:
 - Effort: 2 day
 - Notes:
    user akan dilimit sesuai dengan variant limit yg didapat 
     - 2x
     - 3x
     - 4x
     - 5x
 - Concern:
 - People: 1
### Implementation

#### Adjust flagr
- Adjust flagr related function like `GetABTestVariantID` in `rda/discovery/repository.go` line 393, so that this function can use by any platform not only WA.
- Add flagr flag (enable/disable) on every a/b test.

#### Adjust search question limit
- Adjust limitation code, `s.cfg.WAQuestionQuota` should be change with variant limit. Use config as default limit.
    ```
    return quotaUsed < s.cfg.WAQuestionQuota, nil
    to
    return quotaUsed < WAQuestionQuotaVariantX, nil
    ```
    
## [BE] New Subject:
 - Effort: 4 week
 - Notes:
 - Concern:
     - Menunggu hasil model dari tim data
     - Yang bisa dilakukan migrate data `rubel-structure` ke `roboguru-structure`
 - People: 1
### Implementation

#### Migration Code
- Migrate rubel-structure to roboguru-structure (new table in rubel-forum)

#### New Endpoint to Create Subject
- The handler function will be called `CreateSubjectRoboguruStructure`. It will have body request like this
    - ensure consistency (non-duplicate) in subject content db when create new subject from roboguru
    ```
    {
      "subjectName": "Rumus jodoh",
      "parentSerial": "SMA"
      "subjectSerial"?: "JDH-123" // this will skip create subject content
    }
    ```
- The handler function will be called `CreateStructureRoboguruStructure`. It will have body request like this
    ```
    {
      "name": "Kelas jodoh",
      "parentSerial": "SMA"
      "parentSerial"?: "K0-JDH" // if empty, it means parent already on rubel-structure
    }
    ```

#### Adjust Rubel Forum Function
- CreateForumThread
- Function `GetAllSubjectWithParent` rubel-forum/client/rubel-structure line 34
- `GetForumThreadList` rubel-forum/handler/forum-thread.go line 135
- `GetQuestionList` roboguru-discovery-api/handler/discovery.go line 340
- `getStructureByGradeSerialAndSubjectName` roboguru-discovery-api/handler/discovery.go line 340
- `s.mappingGradeSubject()` roboguru-discovery-api/discovery/service.go line 179
- `rubel-forum/pkg/rubel-structure/service.go`
    
#### Adjust BA Endpoint
- `CreateForumThread` rubel-forum/handler/forum-thread.go line 56 to create forum thread
    - no issue. just make sure the curriculum serial and subject serial are the same with rubel-structure.
- `GetLessonsByGroupSerial` rubel-forum/handler/class.go line 104 to get subject serial based on curriculum serial and source: BA

#### Migrate Grade and Subject from CloudFunction
- Currently, web use CF to get grade & subject
    - migrate to `/rubelforum/groups` and `/rubelforum/lesson`
    - support param `showLesson` in `/rubelforum/groups` to list all subject for each groups
- Affected code
    - `rubel-forum/client/group:GetForumStudentRubelStructure` and all code that depends on this function
    - `rubel-forum/client/class/service.go` and all code that depends on this function

## [Web, BE] Forum TH:
 - Effort: 8
 - Notes:
   - Depedencies
      - tracking
      - question-bank
      - video
      - user
      - roboguru-api
      - notification
      - rubel-structure
      - rg-content-review
      - rg-user-banned
      - rg-censor
      - content-authorization
      - rg-info-api
      - rg-socmed-feed
      - rg-gam-progress
      - rg-gam-user
      - general-service ?
   - Deployment service, pubsub, tracking, db, redis TH
      - rg-forum + db
      - rubel-forum + db + redis (dipake rg-forum juga)
      - roboguru-discovery-api + db + redis
      - roboguru-forum + db
      - roboguru-rules ?
      - pubsub
      - elasticsearch
         - roboguru question detail
         - roboguru question list
         - forum question list
   - Setting cloud function
      - Grade & Class
      - Banner
      - Forum info
      - App info ?
   - Storage
      - Icon
      - Image/Asset
   - Configuration file TH (config)
      - MT Tag
      - Onelink post to forum ?
      - Onelink question detail ?
      - Concept value (kalo di id OTHERS/lainnya)
      - Gamification
      - Deeplink
      - Feedback
   - Bucket uploader ?
 - People: 1 (Taf)
 
## [Android, Web, BE] Sharing Soal - Banner:
  - Effort: 1
  - Notes:
      - Cloudfunction
      - Deeplink
      - S3
        - Upload asset
        
## [BE] Refactor Roboguru Chatbot:
  - Notes:
      - ALL DONE
      - Nemu satu bug (not really) di trackingnya. Beda format dengan yang lama, sudah difix mas wahyu dan sedang ditest sama QA
## [BE] Improve Filter Query:
https://ruanggguru.atlassian.net/wiki/spaces/SD/pages/2095972373/Slow+query+count+forum+threads+in+Web+MT#Benchmark-Solution

  - Effort: 5
  - Notes:
      - Support VN & TH
      - Scope dikecilin ke count aja (?)
          - ga perlu support pagination dulu
          - ga perlu support mapping es ke struct
  - People: 1 (Azka) -> jadi pak R@m@
  
## [BE] Improve rg-forum for qna:
https://ruangguru.slack.com/archives/CL9NANPC0/p1631941254013900?thread_ts=1631857537.002400&cid=CL9NANPC0
> Yap2 udah aman.
> Quick post mortermnya:
> - rg-forum dan rg-content-review pake DB yang sama
> - Dari Live hit ke rg-forum > GetDiscussionDetailByThreadSerial -> rg-content-review > GetLikeDislike
> - Query rg-content-review > GetLikeDislike ada IN nya buat masing2 discussion
> - Di Live gak ada pagination, akibatnya query rg-content-review > GetLikeDislike banyak dihit dan banyak IN clausenya
> - Gak ada cache di rg-forum > GetDiscussionDetailByThreadSerial  
> Action item:
> - Tambah cache di rg-forum > GetDiscussionDetailByThreadSerial (dengan invalidation yang bagus, jangan timer based. Dan itu gak write intensive, harusnya masih aman kalau clear cache tiap ada update)
    > - Tambah circuit breaker di rg-forum > GetDiscussionDetailByThreadSerial. In case berat, set LikeDislikenya jadi 0 (gak perlu hit rg-content-review)
> - Di Live tambah Pagination -> need app release

   - Effort: 3 (lowest solution) -> 13 (ideal solution)
   - Notes:
      - (opt) Performance 
          - Enable cache
              - Invalidate when there's modify in thread or thread's discussion
          - Create ES for rgforum & rg-content-review
              - Country: ID, VN, TH
      - rg-content-review not breaking endpoint
          - (opt) untuk switching whether to use content-review
              - hit prometheus metric
              - config
          - soft error when rg-forum hit rg-content-review
     
## [Android, BE] Image compressor:
   - Effort: 2
   - Notes:
      - S3 Bucket
         - Bikin baru untuk forum
      - Kalo bisa dari FE yang ngeconvert host s3 ke imgix


## [BE] FBM VN Chatbot:
 - Effort: 
 - Notes:
     - banyak kebutuhan dari FBM adapter yg jadi blocker. e.g:
         - blm support quick-reply
         - panic ketika user kirim gambar
         - etc. (belum ketemu)
     - bisa konek ke qiscus
     
 - Concern:
     - Salah copas reply karena gk paham bahasanya
 - People: 1
### Implementations
 - Ganti reply ke VN
     - ada beberapa yang langsung ganti, tapi ada banyak jg yang perlu diubah flownya
 - Get FB user's first name from FB API
     - Bikin method custom di utils roboguru callback `GetFacebookName`
         - params: 
             - `tenant_name` (didapat dari callback, blocked by CMN-1151)
             - `user_id` (didapat dari callback, FB UID)
         - return:
             - first_name
             - error
     - Ambil FB Access Token dari messenger proxy (`{{rg-messenger-proxy}}/api/v3/rg-messenger-proxy/config/roboguru-vn`)
     - Request ke FB API, parse response, return
         - `https://graph.facebook.com/4640110679361604?fields=first_name&access_token={access_token}`
     - Method dipangggil saat state root dan input subject (notes: di root belum bisa, masih WIP dari chatbot-lib)
         - cache kalo udah manggil sekali, klo udah ada di cache gk usah dipanggil lagi
 - Qiscus Callback
     - masih perlu diskusi diskusi buat mahamin flownya
     - yang sudah ada bayangan:
         - untuk beberapa state yg perlu ke qiscus, tinggal diarahin kesana
         - pas baliknya ke state tertentu
 - FB Persistent Button
     - Setup dari FB pagenya langsung (kayaknya)
     - Bikin setiap state yang bakal dituju dari setiap button punya * (wildcard) source state supaya bisa diarahin dari mana aja
 - Deployment? setup dependencies baru kah buat VN? atau pake ID aja
 
## [BE] RLO WA Fund
 - Effort: medium-high
 - Notes:
     - extends existing rg-chatbot-roboguru flow
     - adds ability to chat with choosen teacher directly via whatsapp(selected via rlo matchmaking mechanism)
### Implementations
 - add new states in rg-chatbot-roboguru
 - (atleast in development state) use yaml to store states
 - if its impossible to utilize yaml in production 
     - > create script to translate yaml into mysql insert query
     - if possible use this in production as source of truth (replacing mysql)
 - separates callback implementations by product (roboguru & rlo)
 - disable call within teacher app when chat is coming from WA
 - new callbacks
     - show payment options
         - show different links depends on what package is selected
     - check coin
         - call rg-user for phonenumber-account mapping
         - call rlo-coin to check current user's coin balance
         - new rlo-coin endpoint (migrate from corem)
           ```
           ### Response ###
           GET /coin
           Authorization: Bearer <TOKEN>
           
           ### Response ###
           HTTP1.1 200 OK
           {
               data: {
                   balance: 9999999
               }
           }
           ```
           
  
     - select grades + subject (cant utilize existing subject selection)
         - user select grade + subject, store in userAttr
     - match making
         - call rlo-api, based on which grade and subject is selected
     - learning session
         - user can chat with teacher
         - when user chat -> forward to sendbird from rg-chatbot-roboguru
         - when teacher chat -> rlo-api got event from sendbird, trigger send message endpoint in chatbot
     - feedback
         - very similar to the one in roboguru
         - send to rlo-api instead of roboguru-discovery-api
 - create new endpoint to send received message from rlo-api
     - perhaps can utilize the one provided with qiscus?
     - or exists already?
     - new endpoint example
       ```
       ### Request ###
       POST /message
       Authorization: Bearer <TOKEN>
       {
           data: {
               destination: 08123456789         #destination phone number
               tenant: roboguru-1               #which tenant to send to
               message: ini adalah gambar seekor kucing loh dek
               media_url: https://en.wikipedia.org/wiki/File:VAN_CAT.png #(not sure 'bout this) teacher can send pictures (maybe explanation?)
           }
       }

       ### Response ###
       HTTP1.1 200 OK
       ```
 - will refactor some parts of existing roboguru implementation into a more reusable form

## [BE] MT Web Typing
 - ALL DONE FROM BE
 - WAITING FOR BUGS FROM FE
 - atleast 1 bug is found
     - in release mechanism, but i forgot what the cause & effect
     - solved temporarily by refresh


## [BE] Question Details - Banner:
  - Effort: 
  - Notes:
      - Cloudfunction
      - Deeplink
      - S3
        - Upload asset

### Implementation
- Nambah method di file `roboguru-question-detail` untuk handle yang WEB
    - req.headers.platform == 'ios' or 'android' -> cek apakah bisa equal web
    - give different layout banner (ask product)

