рд╡рд┐рдХрд┐рдкреАрдбрд┐рдпрд╛ рд╕реЗ рдЕрдк-рдЯреВ-рдбреЗрдЯ COVID-19 рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде рдкрд╛рдпрдерди рдлреНрд▓рд╛рд╕реНрдХ рд╕реЗрд╡рд╛ рдмрдирд╛рдПрдВ

рдЫрд╡рд┐


рд╣рдорд╛рд░реА рдЯреАрдо рдХрдИ рджреЗрд╢реЛрдВ, рд╢рд╣рд░реЛрдВ рдФрд░ рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЗ рд▓рд┐рдП рд╡реИрд╢реНрд╡рд┐рдХ рдбреЗрдЯрд╛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕реВрдЪрдирд╛ рд╕реЗрд╡рд╛ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░ рд░рд╣реА рд╣реИ ред рдЗрд╕ рд╡рд░реНрд╖ рдХреЗ рдлрд░рд╡рд░реА рдХреЗ рдЕрдВрдд рддрдХ, рджреБрдирд┐рдпрд╛ рднрд░ рдореЗрдВ рдХреЛрд░реЛрдирд╛рд╡рд╛рдпрд░рд╕ рдХреЗ рддреЗрдЬреА рд╕реЗ рдкреНрд░рд╕рд╛рд░ рдиреЗ рд╣рдореЗрдВ рдЕрдкрдиреЗ рдЖрд╡реЗрджрди рдореЗрдВ рд╕реНрдерд┐рддрд┐ рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХреЗ рд▓рд┐рдП рдЕрддрд┐рд░рд┐рдХреНрдд рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░реЗрд░рд┐рдд рдХрд┐рдпрд╛ред рд╡реЗрдм рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдореЗрдВ рдбреЗрдЯрд╛ рдХреА рдХрд▓реНрдкрдирд╛ рдХрд░рдиреЗ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЗрд╕ рдХрд╛рд░реНрдп рдХрд╛ рдореБрдЦреНрдп рдШрдЯрдХ рд▓реЛрдХрдкреНрд░рд┐рдп рдлреНрд▓рд╛рд╕реНрдХ рд╡реЗрдм рдлреНрд░реЗрдорд╡рд░реНрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкрд╛рдпрдерди рдореЗрдВ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рдПрдХ рдорд╛рдЗрдХреНрд░реЛрд╕реЗрд╡рд╛ рдерд╛ред


рд╕реЗрд╡рд╛ рдирд┐рдпрдорд┐рдд рд░реВрдк рд╕реЗ рд╡рд┐рднрд┐рдиреНрди рд╕реНрд░реЛрддреЛрдВ рд╕реЗ рдбреЗрдЯрд╛ рдЕрдкрдбреЗрдЯ рдХрд░рддреА рд╣реИ, рдФрд░ рдЕрдиреБрд░реЛрдз рдкрд░, рд╡реЗрдм рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдореЗрдВ рд╡рд┐рдЬрд╝реБрдЕрд▓рд╛рдЗрдЬрд╝реЗрд╢рди рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рджрд╛рди рдХрд░рддреА рд╣реИред рдореБрдЦреНрдп рдбреЗрдЯрд╛ рд╕реНрд░реЛрдд рджреЗрд╢реЛрдВ рдФрд░ рдХреНрд╖реЗрддреНрд░реЛрдВ рдореЗрдВ рд╡рд╛рдпрд░рд╕ рдХреЗ рдкреНрд░рд╕рд╛рд░ рдкрд░ рд╡рд┐рдХрд┐рдкреАрдбрд┐рдпрд╛ рдкреГрд╖реНрда рд╣реИрдВред рдЗрди рдкреГрд╖реНрдареЛрдВ рдкрд░ рд╕рдВрдХреЗрддрдХ рдХреЗ рд╕рд╛рде рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдХреЛ рдЬрд▓реНрджреА рд╕реЗ рдЕрдкрдбреЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рд╕рдВрдХреНрд░рдордг рдХреЗ рдкреНрд░рд╕рд╛рд░ рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХреЗ рд▓рд┐рдП рд╕реЗрд╡рд╛ рдХреЗ рдбреЗрдЯрд╛ рд╕реНрд░реЛрдд рдХреЗ рд░реВрдк рдореЗрдВ рдЙрддреНрдХреГрд╖реНрдЯ рд╣реИрдВред


рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рдЧреНрд░рд╛рд╣рдХ рдХреЗ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдкреАрдЖрдИ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдФрд░ рдЕрджреНрдпрддрди рдХрд░рдиреЗ рд╕реЗ, рд╕реЗрд╡рд╛ рдХреЗ рдореБрдЦреНрдп рдШрдЯрдХреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реВрдВрдЧрд╛ред рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХреЛрдб github рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдореЗрдВ рдЙрдкрд▓рдмреНрдз рд╣реИ ред


, Python Flask . , , . , COVID-19.


Flask


Python , ORM Python SQLAlchemy. Routitude PostgreSQL.


:


pip install requirements.txt

URI :


export COVID19API_DB_URI=<  URI, : postgresql://localhost/covid19api>

. :


/api                     API              
    covid.py            API c   COVID-19  
/datasources           
    /test                 
        test_covid.py       COVID-19  
    covid_wiki.py         COVID-19    
    utils.py               
/mirgations               SQLAlchemy
    ...                 Flask-Migrate (Alembic)
/test                  
    test_app.py         (API)
app.py                      HTTP 
appvars.py               
config.py              
manage.py              CLI     
models.py             ORM       
requirements.txt        

, :


  • , ;
  • ORM COVID-19;
  • ;
  • API , .

. github , , .



. :



:



html read_html Pandas. . . , , . , , . , . Pandas DataFrame, , html .


datasources/utils.py


import pandas as pd

def get_wiki_table_df(page_url, match_string):
    response = requests.get(page_url)
    tables = pd.read_html(response.content)
    df = None
    for table in tables:
        df = table
        if match_string in str(df):
            break
    return df

. . , COVID-19 .


datasources/covid_wiki.py


def get_report_countries():
    url = (
        'https://en.wikipedia.org/wiki/'
        '2019%E2%80%9320_coronavirus_pandemic_by_country_and_territory'
    ) 
    df = utils.get_wiki_table_df(url, 'Locations[b]')
    df = pd.DataFrame(
        df.values[:, 1:5], 
        columns=['country', 'confirmed', 'deaths', 'recovered']
    )
    df = df[~df['country'].isna()]
    df['country'] = df['country'].apply(lambda x: utils.clean_territory_name(x))
    df.drop(df[df['country'].str.len() > 40].index, inplace=True)
    df = utils.wiki_table_df_numeric_column_clean(df, [
        'confirmed', 'deaths', 'recovered'
    ])
    df['state'] = None
    check_report(df)
    return df

html , , , .


datasources/test/test_covid.py


from unittest import TestCase
from datasources import covid_wiki

class TestCovid(TestCase):
    def test_get_wiki_report(self):
        report = covid_wiki.get_report_countries()
        self.assertTrue('Russia' in list(report['country']))
        self.assertTrue(report.shape[0] > 0)

:


nosetests datasources

SQLAlchemy ORM Flask


, , . Python , , , . - (Object-Relational Mapping, ORM). Python ORM SQLAlchemy. , , , SQLAchemy . Alembic. , Flask , Flask-Migrate. appvars.py manage.py.


ORM Model SQLAlchemy. SQLAchemy . , . ORM .


models.py


class CovidWiki(db.Model):
    __tablename__ = 'covid_wiki'

    territory_id = Column(
        db.VARCHAR(length=256), nullable=False, primary_key=True
    )
    update_time = Column(db.TIMESTAMP(), nullable=False)
    country = Column(db.VARCHAR(length=128), nullable=False)
    state = Column(db.VARCHAR(length=128), nullable=True)
    confirmed = Column(db.INTEGER(), nullable=True)
    deaths = Column(db.INTEGER(), nullable=True)
    recovered = Column(db.INTEGER(), nullable=True)

Index('ix_covid_wiki_country', CovidWiki.country)
Index('ix_covid_wiki_state', CovidWiki.state)

, , Alembic:


python manage.py db init

migrations . ORM :


python manage.py db migrate -m covid_wiki

, , , , , , . migrations/versions. , , python, . 2 тАФ upgrate downgrade. ORM , , .


, . :


python manage.py db upgrade

.


COVID-19


ORM , pandas DataFrame , , , . , . , , SQLAlchemy.


models.py


def update_data_by_dataframe(self, df):
    report = df.to_dict(orient='records')
    report_last = self.get_wiki_last_report()
    for value in report:
        territory_id = self.get_id(value['country'], value['state'])
        value['territory_id'] = territory_id
        changed = (
            (len(report_last) == 0) or
            (territory_id not in report_last) or
            (utils.get_covid_values_sum(value) !=
             utils.get_covid_values_sum(report_last[territory_id]))
        )
        if not changed:
            continue
        logging.info(f"Updating data for territory: {territory_id}")
        data = dict(value)
        for name in utils.STAT_NAMES:
            value = data[name]
            if np.isnan(value):
                data[name] = None
                continue
            data[name] = int(value)
        data['update_time'] = datetime.datetime.now()
        report = CovidWiki(**data)
        db.session.merge(report)
        db.session.commit()

. .


models.py


def update_data(self):
    logging.info('Updating countries data')
    self.update_data_by_dataframe(covid_wiki.get_report_countries())
    logging.info('Updating Russian states data')
    self.update_data_by_dataframe(covid_wiki.get_report_ru())
    logging.info('Updating USA states data')
    self.update_data_by_dataframe(covid_wiki.get_report_us())

Flask Flask-Script . , , manager.command . .


manage.py


@manager.command
def update_covid_data():
    CovidWiki().update_data()

, , :


python manage.py update_covid_data

- , cron.


API COVID-19 Flask


. Flask . , SQLAlchemy . , COVID-19:


  • ;
  • ;
  • .

api/covid.py


def get_covid_countries_report():
    data = db.session.query(CovidWiki).filter(CovidWiki.state.is_(None)).all()
    return [v.to_dict() for v in data]

def get_covid_states_report_by_country(country):
    data = db.session.query(CovidWiki).filter(and_(
        CovidWiki.state.isnot(None),
        func.lower(CovidWiki.country) == country.lower(),
    )).all()
    return [v.to_dict() for v in data]

def get_covid_total_stats():
    def to_dict(v):
        return {'confirmed': v[0], 'deaths': v[1], 'recovered': v[2]}

    curr = db.session.query(
        func.sum(CovidWiki.confirmed),
        func.sum(CovidWiki.deaths),
        func.sum(CovidWiki.recovered),
        func.max(CovidWiki.update_time)
    ).filter(CovidWiki.state.is_(None)).one()
    return {
        'data': to_dict(curr),
        'last_update_time': utils.datetime2string(curr[3], time=True)
    }

API. , , .


app.py


@app.route('/covid/countries')
def get_covid_countries_report():
    report = covid_api.get_covid_countries_report()
    check_data(report)
    return jsonify(report)

@app.route('/covid/states/<string:country>')
def get_covid_states_report_by_country(country):
    report = covid_api.get_covid_states_report_by_country(country)
    check_data(report)
    return jsonify(report)

@app.route('/covid/total')
def get_covid_total_stats():
    report = covid_api.get_covid_total_stats()
    check_data(report)
    return jsonify(report)

Flask .


python app.py

, , , curl.


curl http://localhost:5000/covid/total

. test, :


nosetests

-. Python gunicorn uWSGI.



, . API , . , .


рдкрд░ Routitude, рд╣рдо рдирдХреНрд╢рд╛ рдФрд░ рдбреИрд╢рдмреЛрд░реНрдб рдкрд░ COVID -19 рд╕рдВрдХреНрд░рдордг рдХреА рд╡рд░реНрддрдорд╛рди рджрд░реЛрдВ рдХреЛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕ рд╕реЗрд╡рд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рд╕рднреА рд╕реНрд░реЛрдд рд╕рд╛рдордЧреНрд░реА рдЧрд┐рддреБрдм рднрдВрдбрд╛рд░ рдореЗрдВ рдЙрдкрд▓рдмреНрдз рд╣реИрдВ ред рдХрд┐рд╕реА рднреА рд╕реБрдзрд╛рд░, рдмрдЧ рдлрд┐рдХреНрд╕, рдирдИ рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдФрд░ рдбреЗрдЯрд╛ рдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИред рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рд╕реБрдзрд╛рд░ рдХреЗ рд▓рд┐рдП рд▓реЗрдЦ рдФрд░ рд╕реБрдЭрд╛рд╡реЛрдВ рдкрд░ рдХрд┐рд╕реА рднреА рдЯрд┐рдкреНрдкрдгреА рд╕реЗ рдореБрдЭреЗ рдЦреБрд╢реА рд╣реЛрдЧреАред



All Articles