OpenTRFoundation / OpenTR

OpenTR website
https://opentr.foundation/
Apache License 2.0
4 stars 6 forks source link

GitHub kullanıcılarının Türkiye bazlı özet istatistikleri #28

Closed aliok closed 4 months ago

aliok commented 11 months ago

https://github.com/OpenTRFoundation/OpenTR/issues/20 görevindeki araştırmalar kullanılarak, GitHub'dan konumunu Türkiye olarak seçmiş kullanıcıları bulabiliriz.

https://docs.github.com/en/rest/search/search?apiVersion=2022-11-28#search-users adresinde kullanıcı arama API'sı ile ilgili bilgiler mevcut. Aynı zamanda https://docs.github.com/en/search-github/searching-on-github/searching-users adresinde, search query'nin detayı yazıyor.

Çıktı şeması:

{
  "type": "object",
  "required": [
    "total_count",
    "incomplete_results",
    "items"
  ],
  "properties": {
    "total_count": {
      "type": "integer"
    },
    "incomplete_results": {
      "type": "boolean"
    },
    "items": {
      "type": "array",
      "items": {
        "title": "User Search Result Item",
        "description": "User Search Result Item",
        "type": "object",
        "properties": {
          "login": {
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "node_id": {
            "type": "string"
          },
          "avatar_url": {
            "type": "string",
            "format": "uri"
          },
          "gravatar_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "html_url": {
            "type": "string",
            "format": "uri"
          },
          "followers_url": {
            "type": "string",
            "format": "uri"
          },
          "subscriptions_url": {
            "type": "string",
            "format": "uri"
          },
          "organizations_url": {
            "type": "string",
            "format": "uri"
          },
          "repos_url": {
            "type": "string",
            "format": "uri"
          },
          "received_events_url": {
            "type": "string",
            "format": "uri"
          },
          "type": {
            "type": "string"
          },
          "score": {
            "type": "number"
          },
          "following_url": {
            "type": "string"
          },
          "gists_url": {
            "type": "string"
          },
          "starred_url": {
            "type": "string"
          },
          "events_url": {
            "type": "string"
          },
          "public_repos": {
            "type": "integer"
          },
          "public_gists": {
            "type": "integer"
          },
          "followers": {
            "type": "integer"
          },
          "following": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "bio": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "site_admin": {
            "type": "boolean"
          },
          "hireable": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "text_matches": {
            "title": "Search Result Text Matches",
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "object_url": {
                  "type": "string"
                },
                "object_type": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "property": {
                  "type": "string"
                },
                "fragment": {
                  "type": "string"
                },
                "matches": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "text": {
                        "type": "string"
                      },
                      "indices": {
                        "type": "array",
                        "items": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "blog": {
            "type": [
              "string",
              "null"
            ]
          },
          "company": {
            "type": [
              "string",
              "null"
            ]
          },
          "suspended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "avatar_url",
          "events_url",
          "followers_url",
          "following_url",
          "gists_url",
          "gravatar_id",
          "html_url",
          "id",
          "node_id",
          "login",
          "organizations_url",
          "received_events_url",
          "repos_url",
          "site_admin",
          "starred_url",
          "subscriptions_url",
          "type",
          "url",
          "score"
        ]
      }
    }
  }
}

Örnek çıktı:

{
  "total_count": 12,
  "incomplete_results": false,
  "items": [
    {
      "login": "mojombo",
      "id": 1,
      "node_id": "MDQ6VXNlcjE=",
      "avatar_url": "https://secure.gravatar.com/avatar/25c7c18223fb42a4c6ae1c8db6f50f9b?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png",
      "gravatar_id": "",
      "url": "https://api.github.com/users/mojombo",
      "html_url": "https://github.com/mojombo",
      "followers_url": "https://api.github.com/users/mojombo/followers",
      "subscriptions_url": "https://api.github.com/users/mojombo/subscriptions",
      "organizations_url": "https://api.github.com/users/mojombo/orgs",
      "repos_url": "https://api.github.com/users/mojombo/repos",
      "received_events_url": "https://api.github.com/users/mojombo/received_events",
      "type": "User",
      "score": 1,
      "following_url": "https://api.github.com/users/mojombo/following{/other_user}",
      "gists_url": "https://api.github.com/users/mojombo/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/mojombo/starred{/owner}{/repo}",
      "events_url": "https://api.github.com/users/mojombo/events{/privacy}",
      "site_admin": true
    }
  ]
}

Üstteki örnek çıktıda mevcut değil ama, şehir bazlı yer bilgisi de geliyor.

Buradan, kullanıcıları arayıp, şu bilgileri kaydedebiliriz:

Bu bilgileri kaydetmek bazen tek başına işimize yaramayacak. Çünkü, zaten şu tarz çıkarımları doğrudan sonuç sayısına bakarak, yani tek tek tüm kullanıcıları çekmeden de yapabiliriz:

Ancak, alt kırılımlar yapmak istediğimizde, tüm kombinasyonları tek tek aramak yerine, tüm kullanıcıları çekip, sonra da bu bilgileri kullanarak alt kırılımlar yapmak daha mantıklı olabilir. Örneğin, GitHub'ın Türkiye içinde gösterdiği yer bilgileri nedir bilmiyoruz. Ülke bazında mı, il bazında mı, ilçe bazında mı? Bu sayede şu tarz çıkarımları da yapabiliriz:

Son olarak, GraphQL API'sindeki kısıtlamalar nedeniyle, kullanıcıların bilgilerini tek tek çekmemiz gerekecek. Çünkü GraphQL API'sinde, kullanıcı arama sonuçları hep star sayısına göre sıralı geliyor. Yani, t=0'da arattığımızda gelen kullanıcının star sayısı t=1'de değişirse, tekrar karşımıza çıkabilir. Ya da, tam tersi olarak hiç karşımıza çıkmayabilir.

Yeni kullanıcıları çekmek için bir process

flowchart LR
subgraph " "
    Start(Başla) --> GetLastFile(Son dosyayı bul)

    subgraph " "
      GetLastFile --> |Dosya var| ReadFile(Dosyayı oku)

      ReadFile --> |Dosya tamamlanmış| CheckPreviousCompletionDate
      ReadFile --> |Dosya tamamlanmamış| GetLastUserId

      CheckPreviousCompletionDate(Önceki tamamlanma zamanına bak) --> |Son 24 saatte| CreateNewFile
      CheckPreviousCompletionDate --> |Son 24 saatte değil| Exit[[Bitir]]

      GetLastFile --> |Dosya yok| CreateNewFile(Yeni dosya oluştur)
      CreateNewFile --> GetLastUserId
    end

    subgraph " "
      GetNextUserSet -->|Yeni kişiler var| WriteUserSummariesToFile[Kişi özet bilgilerini dosyaya yaz]
      WriteUserSummariesToFile --> UpdateLastUserId(Son bakılan kişi id'sini dosyada güncelle)
      GetNextUserSet -->|Yeni kişi yok| MarkFileAsComplete[Dosyayı tamamlanmış olarak işaretle]
      %%UpdateLastUserId --> EndFindingNewUsersIteration(Yeni kullanıcı bulma iterasyonu sonu)
      %%MarkFileAsComplete --> EndFindingNewUsersIteration
    end

    GetLastUserId(Son bakılan kişinin idsini dosyadan oku) --> GetNextUserSet(Sonraki kullanıcı setini al)
    UpdateLastUserId --> EndFindingNewUsersIteration(Yeni kullanıcı bulma iterasyonu sonu)
    MarkFileAsComplete --> EndFindingNewUsersIteration
    EndFindingNewUsersIteration --> Start

    Exit:::terminal
    classDef terminal fill:#f88

end

GitHub Actions kullanan ve rate limit olayını hesapa katan process

flowchart LR
subgraph " "
    Cron(Cron 10 dakikada bir) --> CheckOpenPR
    CheckOpenPR --> |Açık PR var| Exit0[[Bitir]]
    CheckOpenPR --> |Açık PR yok| Start(Başla)
    Start --> |Rate limit var| GetLastFile(Son dosyayı bul)
    Start --> |Rate limit yok| Exit1[[Bitir]]

    subgraph " "
      GetLastFile --> |Dosya var| ReadFile(Dosyayı oku)

      ReadFile --> |Dosya tamamlanmış| CheckPreviousCompletionDate
      ReadFile --> |Dosya tamamlanmamış| GetLastUserId

      CheckPreviousCompletionDate(Önceki tamamlanma zamanına bak) --> |Son 24 saatte değil| CreateNewFile
      CheckPreviousCompletionDate --> |Son 24 saatte| Exit2[[Bitir]]

      GetLastFile --> |Dosya yok| CreateNewFile(Yeni dosya oluştur)
      CreateNewFile --> GetLastUserId
    end

    subgraph " "
      GetNextUserSet -->|Yeni kişiler var| WriteUserSummariesToFile[Kişi özet bilgilerini dosyaya yaz]
      WriteUserSummariesToFile --> MarkLastUserId
      MarkLastUserId --> CheckRateLimit
      CheckRateLimit --> |Rate limit var| GetNextUserSet
      CheckRateLimit --> |Rate limit yok| UpdateLastUserId
      UpdateLastUserId --> SaveFile1
      SaveFile1 --> CommitFile1
      CommitFile1 --> Exit3

      %% UpdateLastUserId(Son bakılan kişi id'sini dosyada güncelle)

      GetNextUserSet -->|Yeni kişi yok| MarkFileAsComplete[Dosyayı tamamlanmış olarak işaretle]
      MarkFileAsComplete --> SaveFile2
      SaveFile2 --> CommitFile2
      CommitFile2 --> OpenPR
      OpenPR --> Exit4
    end

    GetLastUserId(Son bakılan kişinin idsini dosyadan oku) --> GetNextUserSet(Sonraki kullanıcı setini al)

    Exit0:::terminal
    Exit1:::terminal
    Exit2:::terminal
    Exit3:::terminal
    Exit4:::terminal
    classDef terminal fill:#f88

end

Sorunlar:

aliok commented 11 months ago

Find users via various criteria. This method returns up to 100 results per page.

https://docs.github.com/en/free-pro-team@latest/rest/search/search?apiVersion=2022-11-28#search-users

The REST API has a custom rate limit for searching. For authenticated requests, you can make up to 30 requests per minute for all search endpoints except for the "Search code" endpoint.

https://docs.github.com/en/free-pro-team@latest/rest/search/search?apiVersion=2022-11-28#rate-limit

User access token requests are limited to 5,000 requests per hour and per authenticated user.

https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limits-for-requests-from-personal-accounts

When using GITHUB_TOKEN, the rate limit is 1,000 requests per hour per repository. https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limits-for-requests-from-github-actions

Dakikada 30 request, saatte 1800 request yapıyor, ama bu işi otomatiğe bağlatıp GitHub Actions kullanmak istersek, saatte 1000 request yapabiliriz.

Saatte en fazla 1000*100 = 100K kullanıcı çekebiliriz. Şu anda profilinde Turkey veya Türkiye yazan aşağı yukarı 100K kullanıcı var. Network ve veri işlemeyi de hesaba katarsak, 2-3 saatte bu verinin tamamını çekebiliriz.

Yani günlük veri yenilemesi mümkün. O yüzden, kullanıcıların profilini sonradan Türkiye yapması veya Türkiye iken başka birşey yapması çok da önemli değil günlük veri gelecekse.

aliok commented 4 months ago

Bu tamamlandi: http://state.opentr.foundation/