GeoIP2のデータベースを手に入れたのでgradioで描画しpythonanywhereを使おうとしたけどreder.comでデプロイした話

試したい方はこちらから
https://geoip2-demo.onrender.com/

## データベースの入手

データベースは無料で入手できますが、登録が必要です。
まずは以下からサインアップしてください。

https://www.maxmind.com/en/geolite2/signup

登録には以下のフォームを埋める必要がありますが、適当でも大丈夫です。すぐに使えるようになりました。

* Full name
* Company
* Industry
* Country Intended use
* How you plan to use the geolocation data
* Email address Please provide a valid email address. This will become your username.
* Phone (optional)
* How do you plan to use the data?

メールアドレスにパスワード設定のメールが来るのでパスワードを設定します。

https://www.maxmind.com/en/accounts/886434/license-key

そしてデータベースをダウンロードします。

https://www.maxmind.com/en/accounts/886434/geoip/downloads


## gradioで描画する

gradioとgeoip2をインストールします。

```
pip install gradio geoip2
```

IPアドレスのASを知るデータベースとどこにIPアドレスがあるかのデータベースは別れていますので別々に読み込みます。
subdivisionsはあったりなかったり、あっても日本語がなかったりします。なかったら英語を使うようにします。

```python
import geoip2.database
import gradio as gr

asn_db = "./GeoLite2-ASN.mmdb"
city_db = "./GeoLite2-City.mmdb"


def get_asn(ip_address: str):
    with geoip2.database.Reader(asn_db) as reader:
        return reader.asn(ip_address)


def get_city(ip_address: str):
    with geoip2.database.Reader(city_db) as reader:
        return reader.city(ip_address)


def main(ip_address):
    asn = get_asn(ip_address)
    city = get_city(ip_address)
    return {
        "AS管理組織": asn.autonomous_system_organization,
        "大陸": city.continent.names["ja"],
        "国": city.country.names["ja"],
        "緯度": city.location.latitude,
        "経度": city.location.longitude,
        "精度": city.location.accuracy_radius,
        "登録国": city.registered_country.names["ja"],
        "細分": list(map(lambda x: x.names["ja"] or x.names["en"], city.subdivisions)),
        "IPアドレス": city.traits.ip_address,
    }

```

gradioで描画します。

```python
demo = gr.Interface(fn=main, inputs="text", outputs="json")
demo.launch(share=True)
```

これで好きなIPアドレスを入れて遊ぶことができます。

これを早速pythonanywhereで動かそうと思いましたが以下の問題が発生しました。

* gradioを直接動かしても反応がない
* gradioにはgunicornの後ろで動かす方法がない
* ならばfastapiの後ろで動かそうとしてもpythonanywhereではサポートしていない(https://www.pythonanywhere.com/forums/topic/32484/)

というわけで代替サービスを探したところrender.comにいきつきました。
ただこれもgradioを直接動かせないのでfastapiの後ろで動かします。

gradioのところをいかに書き直します。

```python
from fastapi import FastAPI

app = FastAPI()

io = gr.Interface(fn=main, inputs="text", outputs="json")
app = gr.mount_gradio_app(app, io, path="/")
```

それでも以下のエラーが置きます。

> "POST /run/predict HTTP/1.1" 422 Unprocessable Entity
> {"detail":[{"type":"missing","loc":["body","event_id"],"msg":"Field required","input":{"data":["1.2.3.4"],"event_data":null,"fn_index":0,"session_hash":"w1mt4r43ag"},"url":"https://errors.pydantic.dev/2.1.2/v/missing"}]}

https://github.com/gradio-app/gradio/issues/4591

pydanticのバージョン違いのようです。

requirements.txtに以下を追記します。

> pydantic==1.10.11


最終的なコードは以下のとおりです。

.
├── GeoLite2-ASN.mmdb
├── GeoLite2-City.mmdb
├── main.py
└── requirements.txt

```python:main.py
import geoip2.database
import gradio as gr
from fastapi import FastAPI

asn_db='./GeoLite2-ASN.mmdb'
city_db='./GeoLite2-City.mmdb'

def get_asn(ip_address:str):
  with geoip2.database.Reader(asn_db) as reader:
    return reader.asn(ip_address)

def get_city(ip_address:str):
  with geoip2.database.Reader(city_db) as reader:
    return reader.city(ip_address)


def main(ip_address):
  asn=get_asn(ip_address)
  city=get_city(ip_address)
  return {
  "AS管理組織":asn.autonomous_system_organization, 
  "大陸":city.continent.names['ja'],
  "国":city.country.names['ja'],  
  "緯度":city.location.latitude,
  "経度":city.location.longitude,
  "精度":city.location.accuracy_radius,
  "登録国":city.registered_country.names['ja'],
  "細分":list(map(lambda x:x.names['ja'] or x.names['en'],city.subdivisions)),
  "IPアドレス":city.traits.ip_address,
  }


app = FastAPI()

io = gr.Interface(fn=main, inputs="text", outputs="json")
app = gr.mount_gradio_app(app, io, path="/")
```

```text:requirements.txt
pydantic==1.10.11
geoip2
gradio
fastapi
uvicorn
```

render.comの設定には名前を入力し、Start Commandには以下のコマンドを入れて、その他はデフォルトにしてください。
> uvicorn main:app --host 0.0.0.0

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230043/90867653-1cfb-32e0-ee87-87f0e1cc6875.png)

デプロイするとgradioが使えるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230043/887fc350-e2df-ed01-8585-71882c04014d.png)