├── .github
└── workflows
│ └── main-test.yml
├── .gitignore
├── .vscode
├── launch.json
└── settings.json
├── LICENSE
├── Makefile
├── Procfile
├── README.md
├── alembic.ini
├── alembic
├── env.py
├── script.py.mako
└── versions
│ ├── .keep
│ ├── 0144c1da0fe6_add_reward.py
│ ├── 017a2ab5d597_add_is_category.py
│ ├── 021af3771390_add_editor_for_comment.py
│ ├── 0281d8fd326a_update_user_model.py
│ ├── 031d89b0d6ea_add_submission_archive_desc_text.py
│ ├── 055c3cdb6b04_update.py
│ ├── 05c3a952ea62_add_description_editor_field.py
│ ├── 06369b058ac1_update_fields.py
│ ├── 0683eb57352d_add_draft_editor.py
│ ├── 096f816e6921_add_submission_contributors.py
│ ├── 09f028729ac8_add_is_deleted.py
│ ├── 0f8b2edf3460_update_keywords.py
│ ├── 13285c952687_add_feed.py
│ ├── 13401d048f7d_fix_json_model.py
│ ├── 14e2a7adb2c6_add_some_top_flags.py
│ ├── 1541eb7cdb55_add_shard_to_timeline_of_my_comment_.py
│ ├── 1cfd11f59759_update_model.py
│ ├── 1da7e93fd803_add_profession_topics_json.py
│ ├── 1df1637c47e7_add_unsubscription.py
│ ├── 1e95c68901bb_add_submission_archive_description_.py
│ ├── 2149dde81b01_update_user_model.py
│ ├── 25b463cbeb95_update_model.py
│ ├── 2668718cdb60_add_comment_upvotes.py
│ ├── 2d600f126b2a_add_submission_keywords_field.py
│ ├── 3081fd03a6fb_update_answer_upvote_model.py
│ ├── 3142c63f83f5_add_feedback_status_field.py
│ ├── 3325c7bab9af_add_rpeorts.py
│ ├── 36266ab34428_add_uuids.py
│ ├── 37da2a0a005c_payment_model.py
│ ├── 3870f5780e72_add_description_text.py
│ ├── 3ad68389b887_add_draft_editor.py
│ ├── 3b7fbbbf91d1_payment_model.py
│ ├── 3b8106237cf3_update_flag.py
│ ├── 3e61d9fd232c_remove_deprecated_fields.py
│ ├── 3f9f62883bbc_update_articles_model.py
│ ├── 4300e970447a_remove_view_counters.py
│ ├── 43be589a3aca_add_subject_user_uuid.py
│ ├── 44b4ccd5cdfc_update_answer_model.py
│ ├── 45c3d27894ca_add_uuids.py
│ ├── 473e1d2a75a4_not_nullable_uuids.py
│ ├── 49c39ab108d8_add_keywords_extracted_at_field.py
│ ├── 4c0ee489cfa1_update_question_model.py
│ ├── 4f3bbe1eeaa6_add_editor_field_to_archives.py
│ ├── 4fab480583ba_add_field_for_prerendered_answer_body.py
│ ├── 584772b747a3_update_nullable.py
│ ├── 5a29cea6c435_add_article_body_text_field.py
│ ├── 5d2c2140d1ad_update_articles_model.py
│ ├── 5e5dc2de75fe_payment_model.py
│ ├── 5ff8d34a8126_update_user_model.py
│ ├── 6162e247214e_add_derived_fields.py
│ ├── 61871dbc7ba7_add_featured_at.py
│ ├── 621513063323_add_keywords_for_question.py
│ ├── 63627588f0c9_add_answer_suggest_edit_model.py
│ ├── 690f409863df_update_invitation_link_model.py
│ ├── 69f40011f79e_add_sub_topic.py
│ ├── 6a3707257d81_update_invitation_model.py
│ ├── 6a66054d75b6_add_feedbacks.py
│ ├── 6aefea467152_payment_model.py
│ ├── 6b93fa0e2613_update_bookmark.py
│ ├── 6c3071d8d022_payment_model.py
│ ├── 6d8ddfacf048_add_user_feed_setting_field.py
│ ├── 7699728838be_payment_model.py
│ ├── 77bf3df9f84d_update_invitation_model.py
│ ├── 7895dbceddfa_add_application_model.py
│ ├── 78bb836eff0a_update_user_model.py
│ ├── 7b009fd9bbda_order_outgoing_incoming_rewards_in_model.py
│ ├── 816363389257_update_channel_model.py
│ ├── 820e2f9cfb02_add_karma.py
│ ├── 8211a967ea0a_payment_model.py
│ ├── 831dfdf03a18_update_model.py
│ ├── 842e9e21589b_add_gif_avatar_url.py
│ ├── 8b1e740e7911_update_user_model.py
│ ├── 8c4461051c79_update_user_model.py
│ ├── 8d11e4de766f_fix_model.py
│ ├── 8eb6a05d5d19_update_channel_model.py
│ ├── 91aa83eae955_add_user_keywords.py
│ ├── 92e8bf7639ca_add_profession_topics.py
│ ├── 92fae726b416_add_location_url.py
│ ├── 975224f8aced_add_description_format_fields_for_.py
│ ├── 98555907d799_add_created_at_field_for_site.py
│ ├── 98fd84b5c1ef_add_zhihu_url.py
│ ├── 9990700dc85b_add_answer_bookmarks.py
│ ├── 9a630f4121fc_add_task_model.py
│ ├── 9a8a72e3fa0c_add_submission_ardchive_topic_uuids.py
│ ├── 9cc77f9fff00_add_category_topic_for_site.py
│ ├── 9e6cfa47d06b_add_editor_to_question.py
│ ├── 9fee55d72d95_update_answer_upvote_model.py
│ ├── a2c4da7f3afc_add_editor_to_comment.py
│ ├── a4e5d081e50a_add_articles.py
│ ├── a55cb56957e2_add_webhook.py
│ ├── a5cefdf035a6_update_user_model.py
│ ├── a8802e4a70f4_update.py
│ ├── a8c68d11ccf0_add_notifications.py
│ ├── ae37fb780460_update_site_model.py
│ ├── b087a350779c_add_submission_subscription.py
│ ├── b15af42b3583_add_body_prerendered_text_for_article.py
│ ├── b1c1a85fb152_add_phone_number.py
│ ├── b30886ac002d_update_channel_model.py
│ ├── b501e111ef53_add_audit_log_model.py
│ ├── b838801fce91_update_answer_model.py
│ ├── b86c5b5477fd_update_user_model.py
│ ├── b8d3e3432d39_add_comment_body_text_field.py
│ ├── b9652e802bb7_add_question_archive.py
│ ├── b9fc0a230cde_update.py
│ ├── bc9b289d6c53_add_keywords_for_answer.py
│ ├── be76b1afbc63_add_forms.py
│ ├── bf88da60488f_add_subject_to_channel.py
│ ├── c07f2ab88c8b_add_claimed_welcome_test_rewards_with_.py
│ ├── c26f33a78370_update_user_model.py
│ ├── c376381a1d91_add_question_upvotes.py
│ ├── c590109a43f8_regenerated_data_model.py
│ ├── c803c015f56b_add_article_prerendered.py
│ ├── c8fd907cf166_remove_user_about_editor.py
│ ├── c96147f8c295_regenerated_data_model.py
│ ├── ccddb38c98c2_add_question_upvotes.py
│ ├── cd1d0246bf82_update_site_model.py
│ ├── ce37e212bb64_update.py
│ ├── d1396c067972_remove_content_json.py
│ ├── d19bd4e72e97_update_forms.py
│ ├── d706a80ce0a8_add_submission_model.py
│ ├── d7239240b702_update.py
│ ├── d727019ca7f0_add_submission_suggestion_accepted_diff_.py
│ ├── d81294903342_update_user_model.py
│ ├── d9e2d499d67a_add_content_visibility_flag_for_article.py
│ ├── da17aa8c10be_add_nullable_false.py
│ ├── daa73c481ef0_update_phone_number.py
│ ├── db11135cb499_update_comment_model.py
│ ├── dffd3046e10c_add_submission_suggestion.py
│ ├── e0bb8baabaa5_update_user_model.py
│ ├── e3ca93163b20_add_suggestion_comment.py
│ ├── e476333cd0b1_update_model.py
│ ├── e8574b6cdddb_update_nullable.py
│ ├── ebc3062bc724_update_answer_upvote_model.py
│ ├── ebc55177cad6_update_user_model.py
│ ├── ecce4b4966ab_remove_is_placed_at_question_top.py
│ ├── eda6a2fac15c_update_bookmark.py
│ ├── efd4a8bf854e_add_per_site_karma.py
│ ├── f3822eef0de4_add_content_visibility_flag_for_answer.py
│ ├── f3925b9ee72e_update_index.py
│ ├── f43df48a00e2_update_user_model.py
│ ├── f66a6a2cbb25_update_time_model.py
│ ├── f6a5ec609bf7_add_body_prerendered_text_field.py
│ ├── f8e5c9d0d48a_update_model.py
│ ├── f911fc3c1871_add_archive_model.py
│ ├── f9cc4868db52_add_submission_suggestion_topic_uuids.py
│ ├── fa7a9c06c679_add_about_in_user_profile.py
│ ├── fcdd9de59683_add_audit_log_field.py
│ ├── fd40a9df4c39_update_model.py
│ ├── fe7fda109125_update_question_model.py
│ ├── fed07815fb47_add_view_times.py
│ └── ffcc3b882cf6_add_verified_telegram_user_id.py
├── app.json
├── chafan_core
├── __init__.py
├── app
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── api_v1
│ │ │ ├── __init__.py
│ │ │ ├── api.py
│ │ │ └── endpoints
│ │ │ │ ├── __init__.py
│ │ │ │ ├── activities.py
│ │ │ │ ├── answer_suggest_edits.py
│ │ │ │ ├── answers.py
│ │ │ │ ├── applications.py
│ │ │ │ ├── article_columns.py
│ │ │ │ ├── articles.py
│ │ │ │ ├── audit_logs.py
│ │ │ │ ├── bot.py
│ │ │ │ ├── channels.py
│ │ │ │ ├── coin_deposits.py
│ │ │ │ ├── coin_payments.py
│ │ │ │ ├── comments.py
│ │ │ │ ├── discovery.py
│ │ │ │ ├── drafts.py
│ │ │ │ ├── feedbacks.py
│ │ │ │ ├── form_responses.py
│ │ │ │ ├── forms.py
│ │ │ │ ├── invitation_links.py
│ │ │ │ ├── login.py
│ │ │ │ ├── me.py
│ │ │ │ ├── messages.py
│ │ │ │ ├── notifications.py
│ │ │ │ ├── people.py
│ │ │ │ ├── profiles.py
│ │ │ │ ├── questions.py
│ │ │ │ ├── reactions.py
│ │ │ │ ├── reports.py
│ │ │ │ ├── rewards.py
│ │ │ │ ├── search.py
│ │ │ │ ├── sitemaps.py
│ │ │ │ ├── sites.py
│ │ │ │ ├── submission_suggestions.py
│ │ │ │ ├── submissions.py
│ │ │ │ ├── tasks.py
│ │ │ │ ├── topics.py
│ │ │ │ ├── upload.py
│ │ │ │ ├── users.py
│ │ │ │ ├── webhooks.py
│ │ │ │ └── ws.py
│ │ ├── deps.py
│ │ └── health.py
│ ├── aws.py
│ ├── aws_ses.py
│ ├── aws_sns.py
│ ├── cached_layer.py
│ ├── common.py
│ ├── config.py
│ ├── crud
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── crud_activity.py
│ │ ├── crud_answer.py
│ │ ├── crud_answer_suggest_edit.py
│ │ ├── crud_application.py
│ │ ├── crud_article.py
│ │ ├── crud_article_column.py
│ │ ├── crud_audit_log.py
│ │ ├── crud_channel.py
│ │ ├── crud_coin_deposit.py
│ │ ├── crud_coin_payment.py
│ │ ├── crud_comment.py
│ │ ├── crud_feedback.py
│ │ ├── crud_form.py
│ │ ├── crud_form_response.py
│ │ ├── crud_invitation.py
│ │ ├── crud_invitation_link.py
│ │ ├── crud_message.py
│ │ ├── crud_notification.py
│ │ ├── crud_profile.py
│ │ ├── crud_question.py
│ │ ├── crud_report.py
│ │ ├── crud_reward.py
│ │ ├── crud_site.py
│ │ ├── crud_submission.py
│ │ ├── crud_submission_suggestion.py
│ │ ├── crud_topic.py
│ │ ├── crud_user.py
│ │ └── crud_webhook.py
│ ├── data_broker.py
│ ├── email-templates
│ │ ├── build
│ │ │ ├── feedback_status_update.html
│ │ │ ├── notifications.html
│ │ │ ├── reset_password.html
│ │ │ └── verification_code.html
│ │ └── src
│ │ │ ├── feedback_status_update.mjml
│ │ │ ├── notifications.mjml
│ │ │ ├── reset_password.mjml
│ │ │ └── verification_code.mjml
│ ├── email_utils.py
│ ├── endpoint_utils.py
│ ├── feed.py
│ ├── limiter.py
│ ├── limiter_middleware.py
│ ├── main.py
│ ├── materialize.py
│ ├── metrics
│ │ └── __init__.py
│ ├── model_utils.py
│ ├── models
│ │ ├── __init__.py
│ │ ├── activity.py
│ │ ├── answer.py
│ │ ├── answer_suggest_edit.py
│ │ ├── application.py
│ │ ├── archive.py
│ │ ├── article.py
│ │ ├── article_archive.py
│ │ ├── article_column.py
│ │ ├── audit_log.py
│ │ ├── channel.py
│ │ ├── coin_deposit.py
│ │ ├── coin_payment.py
│ │ ├── comment.py
│ │ ├── feed.py
│ │ ├── feedback.py
│ │ ├── form.py
│ │ ├── form_response.py
│ │ ├── invitation.py
│ │ ├── invitation_link.py
│ │ ├── message.py
│ │ ├── notification.py
│ │ ├── profile.py
│ │ ├── question.py
│ │ ├── question_archive.py
│ │ ├── report.py
│ │ ├── reward.py
│ │ ├── site.py
│ │ ├── submission.py
│ │ ├── submission_archive.py
│ │ ├── submission_suggestion.py
│ │ ├── task.py
│ │ ├── topic.py
│ │ ├── user.py
│ │ └── webhook.py
│ ├── mq.py
│ ├── reactions.py
│ ├── recs
│ │ ├── __init__.py
│ │ ├── indexed_layer.py
│ │ ├── indexing.py
│ │ └── ranking.py
│ ├── schemas
│ │ ├── __init__.py
│ │ ├── activity.py
│ │ ├── answer.py
│ │ ├── answer_archive.py
│ │ ├── answer_suggest_edit.py
│ │ ├── application.py
│ │ ├── article.py
│ │ ├── article_archive.py
│ │ ├── article_column.py
│ │ ├── audit_log.py
│ │ ├── channel.py
│ │ ├── coin_deposit.py
│ │ ├── coin_payment.py
│ │ ├── comment.py
│ │ ├── event.py
│ │ ├── feedback.py
│ │ ├── form.py
│ │ ├── form_response.py
│ │ ├── invitation_link.py
│ │ ├── message.py
│ │ ├── mq.py
│ │ ├── msg.py
│ │ ├── notification.py
│ │ ├── preview.py
│ │ ├── profile.py
│ │ ├── question.py
│ │ ├── question_archive.py
│ │ ├── question_page.py
│ │ ├── reaction.py
│ │ ├── report.py
│ │ ├── reward.py
│ │ ├── richtext.py
│ │ ├── security.py
│ │ ├── site.py
│ │ ├── submission.py
│ │ ├── submission_archive.py
│ │ ├── submission_suggestion.py
│ │ ├── task.py
│ │ ├── token.py
│ │ ├── topic.py
│ │ ├── user.py
│ │ └── webhook.py
│ ├── search.py
│ ├── security.py
│ ├── task.py
│ ├── task_utils.py
│ ├── text_analysis.py
│ ├── view_counters.py
│ ├── webhook_utils.py
│ └── ws_connections.py
├── db
│ ├── __init__.py
│ ├── base.py
│ ├── base_class.py
│ ├── init_db.py
│ ├── session.py
│ └── simple_session.py
├── scheduled
│ ├── __init__.py
│ ├── deliver_notifications.py
│ ├── lib.py
│ └── refresh_search_index.py
├── tests
│ ├── .gitignore
│ ├── __init__.py
│ ├── app
│ │ ├── api
│ │ │ ├── __init__.py
│ │ │ └── api_v1
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_answers.py
│ │ │ │ ├── test_comments.py
│ │ │ │ ├── test_login.py
│ │ │ │ ├── test_profiles.py
│ │ │ │ ├── test_questions.py
│ │ │ │ ├── test_sites.py
│ │ │ │ └── test_users.py
│ │ ├── crud
│ │ │ ├── __init__.py
│ │ │ └── test_crud_user.py
│ │ ├── test_email_utils.py
│ │ └── test_search.py
│ ├── conftest.py
│ └── utils
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── utils.py
└── utils
│ ├── __init__.py
│ ├── base.py
│ ├── constants.py
│ └── validators.py
├── dev-requirements.txt
├── mypy.ini
├── poetry.lock
├── pyproject.toml
├── requirements.txt
└── scripts
├── check.py
├── dramatiq_worker.sh
├── format.sh
├── initial_data.py
├── lint.sh
├── reset_app_state.sh
├── run-unit-tests.sh
├── schedule-runner.py
└── upgrade-db.py
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "server",
9 | "type": "python",
10 | "request": "launch",
11 | "module": "uvicorn",
12 | "args": [
13 | "--host",
14 | "dev.cha.fan",
15 | "--port",
16 | "4582",
17 | "app.main:app"
18 | ],
19 | "jinja": true
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.defaultInterpreterPath": ".venv/bin/python",
3 | "python.formatting.provider": "black",
4 | "python.testing.pytestArgs": [
5 | "chafan_core"
6 | ],
7 | "python.testing.unittestEnabled": false,
8 | "python.testing.pytestEnabled": true
9 | }
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Zhen Zhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: alembic upgrade head; uvicorn chafan_core.app.main:app --port=$PORT --host=0.0.0.0 --log-level=$LOG_LEVEL
2 | worker: bash scripts/dramatiq_worker.sh
3 |
--------------------------------------------------------------------------------
/alembic/script.py.mako:
--------------------------------------------------------------------------------
1 | """${message}
2 |
3 | Revision ID: ${up_revision}
4 | Revises: ${down_revision | comma,n}
5 | Create Date: ${create_date}
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | ${imports if imports else ""}
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = ${repr(up_revision)}
14 | down_revision = ${repr(down_revision)}
15 | branch_labels = ${repr(branch_labels)}
16 | depends_on = ${repr(depends_on)}
17 |
18 |
19 | def upgrade():
20 | ${upgrades if upgrades else "pass"}
21 |
22 |
23 | def downgrade():
24 | ${downgrades if downgrades else "pass"}
25 |
--------------------------------------------------------------------------------
/alembic/versions/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/alembic/versions/.keep
--------------------------------------------------------------------------------
/alembic/versions/017a2ab5d597_add_is_category.py:
--------------------------------------------------------------------------------
1 | """Add is_category
2 |
3 | Revision ID: 017a2ab5d597
4 | Revises: 43be589a3aca
5 | Create Date: 2021-04-17 23:46:34.275362
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '017a2ab5d597'
14 | down_revision = '43be589a3aca'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('topic', sa.Column('is_category', sa.Boolean(), server_default='false', nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('topic', 'is_category')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/021af3771390_add_editor_for_comment.py:
--------------------------------------------------------------------------------
1 | """Add editor for comment
2 |
3 | Revision ID: 021af3771390
4 | Revises: 9cc77f9fff00
5 | Create Date: 2021-03-22 11:52:13.152673
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '021af3771390'
14 | down_revision = '9cc77f9fff00'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('comment', sa.Column('editor', sa.String(), server_default='wysiwyg', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('comment', 'editor')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/0281d8fd326a_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: 0281d8fd326a
4 | Revises: b838801fce91
5 | Create Date: 2020-12-30 22:36:07.441756
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '0281d8fd326a'
14 | down_revision = 'b838801fce91'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('site', sa.Column('addable_member', sa.Boolean(), server_default='true', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('site', 'addable_member')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/031d89b0d6ea_add_submission_archive_desc_text.py:
--------------------------------------------------------------------------------
1 | """Add submission archive desc text
2 |
3 | Revision ID: 031d89b0d6ea
4 | Revises: 9a8a72e3fa0c
5 | Create Date: 2021-06-10 00:04:34.131388
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '031d89b0d6ea'
14 | down_revision = '9a8a72e3fa0c'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('submissionarchive', sa.Column('description_text', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('submissionarchive', 'description_text')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/055c3cdb6b04_update.py:
--------------------------------------------------------------------------------
1 | """Update
2 |
3 | Revision ID: 055c3cdb6b04
4 | Revises: a8802e4a70f4
5 | Create Date: 2021-12-04 01:39:12.058936
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | from sqlalchemy.dialects import postgresql
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '055c3cdb6b04'
14 | down_revision = 'a8802e4a70f4'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('submissionsuggestion', 'accepted_diff_base',
22 | existing_type=postgresql.JSON(astext_type=sa.Text()),
23 | nullable=True)
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.alter_column('submissionsuggestion', 'accepted_diff_base',
30 | existing_type=postgresql.JSON(astext_type=sa.Text()),
31 | nullable=False)
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/05c3a952ea62_add_description_editor_field.py:
--------------------------------------------------------------------------------
1 | """Add description editor field
2 |
3 | Revision ID: 05c3a952ea62
4 | Revises: 021af3771390
5 | Create Date: 2021-03-23 18:08:14.520950
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '05c3a952ea62'
14 | down_revision = '021af3771390'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('question', sa.Column('description_editor', sa.String(), server_default='wysiwyg', nullable=False))
22 | op.add_column('submission', sa.Column('description_editor', sa.String(), server_default='wysiwyg', nullable=False))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('submission', 'description_editor')
29 | op.drop_column('question', 'description_editor')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/0683eb57352d_add_draft_editor.py:
--------------------------------------------------------------------------------
1 | """Add draft_editor
2 |
3 | Revision ID: 0683eb57352d
4 | Revises: 017a2ab5d597
5 | Create Date: 2021-04-24 16:01:51.364264
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '0683eb57352d'
14 | down_revision = '017a2ab5d597'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('answer', sa.Column('draft_editor', sa.String(), server_default='wysiwyg', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('answer', 'draft_editor')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/096f816e6921_add_submission_contributors.py:
--------------------------------------------------------------------------------
1 | """Add submission_contributors
2 |
3 | Revision ID: 096f816e6921
4 | Revises: 7b009fd9bbda
5 | Create Date: 2021-06-11 00:13:54.449906
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '096f816e6921'
14 | down_revision = '7b009fd9bbda'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('submission_contributors',
22 | sa.Column('submission_id', sa.Integer(), nullable=True),
23 | sa.Column('user_id', sa.Integer(), nullable=True),
24 | sa.ForeignKeyConstraint(['submission_id'], ['submission.id'], ),
25 | sa.ForeignKeyConstraint(['user_id'], ['user.id'], )
26 | )
27 | # ### end Alembic commands ###
28 |
29 |
30 | def downgrade():
31 | # ### commands auto generated by Alembic - please adjust! ###
32 | op.drop_table('submission_contributors')
33 | # ### end Alembic commands ###
34 |
--------------------------------------------------------------------------------
/alembic/versions/09f028729ac8_add_is_deleted.py:
--------------------------------------------------------------------------------
1 | """Add is_deleted
2 |
3 | Revision ID: 09f028729ac8
4 | Revises: e476333cd0b1
5 | Create Date: 2021-01-17 01:47:43.985225
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '09f028729ac8'
14 | down_revision = 'e476333cd0b1'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_foreign_key(None, 'activity', 'site', ['site_id'], ['id'])
22 | op.add_column('answer', sa.Column('is_deleted', sa.Boolean(), server_default='false', nullable=False))
23 | op.alter_column('answer', 'body',
24 | existing_type=sa.VARCHAR(),
25 | nullable=False)
26 | # ### end Alembic commands ###
27 |
28 |
29 | def downgrade():
30 | # ### commands auto generated by Alembic - please adjust! ###
31 | op.alter_column('answer', 'body',
32 | existing_type=sa.VARCHAR(),
33 | nullable=True)
34 | op.drop_column('answer', 'is_deleted')
35 | op.drop_constraint(None, 'activity', type_='foreignkey')
36 | # ### end Alembic commands ###
37 |
--------------------------------------------------------------------------------
/alembic/versions/13401d048f7d_fix_json_model.py:
--------------------------------------------------------------------------------
1 | """Fix json model
2 |
3 | Revision ID: 13401d048f7d
4 | Revises: b501e111ef53
5 | Create Date: 2021-03-07 19:15:20.524346
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '13401d048f7d'
14 | down_revision = 'b501e111ef53'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.drop_column('profile', 'introduction')
22 | op.add_column('user', sa.Column('education_experiences', sa.JSON(), nullable=True))
23 | op.add_column('user', sa.Column('work_experiences', sa.JSON(), nullable=True))
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_column('user', 'work_experiences')
30 | op.drop_column('user', 'education_experiences')
31 | op.add_column('profile', sa.Column('introduction', sa.VARCHAR(), autoincrement=False, nullable=True))
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/14e2a7adb2c6_add_some_top_flags.py:
--------------------------------------------------------------------------------
1 | """Add some top flags
2 |
3 | Revision ID: 14e2a7adb2c6
4 | Revises: 77bf3df9f84d
5 | Create Date: 2021-01-11 19:26:26.565203
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '14e2a7adb2c6'
14 | down_revision = '77bf3df9f84d'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('answer', sa.Column('is_placed_at_question_top', sa.Boolean(), server_default='false', nullable=False))
22 | op.add_column('question', sa.Column('is_placed_at_home', sa.Boolean(), server_default='false', nullable=False))
23 | op.add_column('question', sa.Column('is_placed_at_site_top', sa.Boolean(), server_default='false', nullable=False))
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_column('question', 'is_placed_at_site_top')
30 | op.drop_column('question', 'is_placed_at_home')
31 | op.drop_column('answer', 'is_placed_at_question_top')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/1541eb7cdb55_add_shard_to_timeline_of_my_comment_.py:
--------------------------------------------------------------------------------
1 | """Add shard to timeline of my comment feature
2 |
3 | Revision ID: 1541eb7cdb55
4 | Revises: 820e2f9cfb02
5 | Create Date: 2021-01-31 19:25:00.347776
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '1541eb7cdb55'
14 | down_revision = '820e2f9cfb02'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('comment', sa.Column('shared_to_timeline', sa.Boolean(), server_default='false', nullable=False))
22 | op.alter_column('site', 'moderator_id',
23 | existing_type=sa.INTEGER(),
24 | nullable=False)
25 | # ### end Alembic commands ###
26 |
27 |
28 | def downgrade():
29 | # ### commands auto generated by Alembic - please adjust! ###
30 | op.alter_column('site', 'moderator_id',
31 | existing_type=sa.INTEGER(),
32 | nullable=True)
33 | op.drop_column('comment', 'shared_to_timeline')
34 | # ### end Alembic commands ###
35 |
--------------------------------------------------------------------------------
/alembic/versions/1cfd11f59759_update_model.py:
--------------------------------------------------------------------------------
1 | """Update model
2 |
3 | Revision ID: 1cfd11f59759
4 | Revises: f911fc3c1871
5 | Create Date: 2021-01-16 01:20:08.017817
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '1cfd11f59759'
14 | down_revision = 'f911fc3c1871'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('answer', 'body',
22 | existing_type=sa.VARCHAR(),
23 | nullable=True)
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.alter_column('answer', 'body',
30 | existing_type=sa.VARCHAR(),
31 | nullable=False)
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/1da7e93fd803_add_profession_topics_json.py:
--------------------------------------------------------------------------------
1 | """Add profession_topics_json
2 |
3 | Revision ID: 1da7e93fd803
4 | Revises: c8fd907cf166
5 | Create Date: 2021-11-25 10:44:22.800753
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '1da7e93fd803'
14 | down_revision = 'c8fd907cf166'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('profession_topics_json', sa.JSON(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'profession_topics_json')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/1df1637c47e7_add_unsubscription.py:
--------------------------------------------------------------------------------
1 | """Add unsubscription
2 |
3 | Revision ID: 1df1637c47e7
4 | Revises: 473e1d2a75a4
5 | Create Date: 2021-01-30 23:29:41.735494
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '1df1637c47e7'
14 | down_revision = '473e1d2a75a4'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('enable_deliver_unread_notifications', sa.Boolean(), server_default='true', nullable=False))
22 | op.add_column('user', sa.Column('unsubscribe_token', sa.String(), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('user', 'unsubscribe_token')
29 | op.drop_column('user', 'enable_deliver_unread_notifications')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/1e95c68901bb_add_submission_archive_description_.py:
--------------------------------------------------------------------------------
1 | """Add submission archive description editor field
2 |
3 | Revision ID: 1e95c68901bb
4 | Revises: 5a29cea6c435
5 | Create Date: 2021-06-04 20:37:13.027754
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '1e95c68901bb'
14 | down_revision = '5a29cea6c435'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('submissionarchive', sa.Column('description_editor', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('submissionarchive', 'description_editor')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/2149dde81b01_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: 2149dde81b01
4 | Revises: 0281d8fd326a
5 | Create Date: 2020-12-31 13:30:57.201424
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '2149dde81b01'
14 | down_revision = '0281d8fd326a'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('flags', sa.String(), nullable=True))
22 | op.add_column('user', sa.Column('sent_invitataions', sa.Integer(), server_default='0', nullable=False))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('user', 'sent_invitataions')
29 | op.drop_column('user', 'flags')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/25b463cbeb95_update_model.py:
--------------------------------------------------------------------------------
1 | """Update model
2 |
3 | Revision ID: 25b463cbeb95
4 | Revises: 831dfdf03a18
5 | Create Date: 2021-01-09 10:37:32.455225
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '25b463cbeb95'
14 | down_revision = '831dfdf03a18'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.drop_constraint('coinpayment_event_json_payee_id_key', 'coinpayment', type_='unique')
22 | op.create_unique_constraint(None, 'coinpayment', ['event_json', 'payee_id', 'payer_id'])
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_constraint(None, 'coinpayment', type_='unique')
29 | op.create_unique_constraint('coinpayment_event_json_payee_id_key', 'coinpayment', ['event_json', 'payee_id'])
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/2d600f126b2a_add_submission_keywords_field.py:
--------------------------------------------------------------------------------
1 | """Add submission keywords field
2 |
3 | Revision ID: 2d600f126b2a
4 | Revises: b8d3e3432d39
5 | Create Date: 2021-04-01 11:49:51.274291
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '2d600f126b2a'
14 | down_revision = 'b8d3e3432d39'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('submission', sa.Column('keywords', sa.JSON(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('submission', 'keywords')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/3081fd03a6fb_update_answer_upvote_model.py:
--------------------------------------------------------------------------------
1 | """Update answer upvote model
2 |
3 | Revision ID: 3081fd03a6fb
4 | Revises: ebc3062bc724
5 | Create Date: 2021-01-11 01:21:54.051169
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '3081fd03a6fb'
14 | down_revision = 'ebc3062bc724'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_index(op.f('ix_answer_upvotes_answer_id'), 'answer_upvotes', ['answer_id'], unique=False)
22 | op.create_index(op.f('ix_answer_upvotes_voter_id'), 'answer_upvotes', ['voter_id'], unique=False)
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_index(op.f('ix_answer_upvotes_voter_id'), table_name='answer_upvotes')
29 | op.drop_index(op.f('ix_answer_upvotes_answer_id'), table_name='answer_upvotes')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/3142c63f83f5_add_feedback_status_field.py:
--------------------------------------------------------------------------------
1 | """Add feedback status field
2 |
3 | Revision ID: 3142c63f83f5
4 | Revises: f3925b9ee72e
5 | Create Date: 2021-08-27 21:53:15.458100
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '3142c63f83f5'
14 | down_revision = 'f3925b9ee72e'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('feedback', sa.Column('status', sa.String(), server_default='sent', nullable=False))
22 | op.drop_column('invitation', 'personal_relation')
23 | op.drop_column('invitation', 'invited_email')
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.add_column('invitation', sa.Column('invited_email', sa.VARCHAR(), autoincrement=False, nullable=True))
30 | op.add_column('invitation', sa.Column('personal_relation', sa.VARCHAR(), autoincrement=False, nullable=True))
31 | op.drop_column('feedback', 'status')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/37da2a0a005c_payment_model.py:
--------------------------------------------------------------------------------
1 | """Payment model
2 |
3 | Revision ID: 37da2a0a005c
4 | Revises: 3b7fbbbf91d1
5 | Create Date: 2020-12-26 23:19:12.042214
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '37da2a0a005c'
14 | down_revision = '3b7fbbbf91d1'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('coinpayment', 'payee_id',
22 | existing_type=sa.INTEGER(),
23 | nullable=False)
24 | op.alter_column('coinpayment', 'payer_id',
25 | existing_type=sa.INTEGER(),
26 | nullable=False)
27 | # ### end Alembic commands ###
28 |
29 |
30 | def downgrade():
31 | # ### commands auto generated by Alembic - please adjust! ###
32 | op.alter_column('coinpayment', 'payer_id',
33 | existing_type=sa.INTEGER(),
34 | nullable=True)
35 | op.alter_column('coinpayment', 'payee_id',
36 | existing_type=sa.INTEGER(),
37 | nullable=True)
38 | # ### end Alembic commands ###
39 |
--------------------------------------------------------------------------------
/alembic/versions/3870f5780e72_add_description_text.py:
--------------------------------------------------------------------------------
1 | """Add description_text
2 |
3 | Revision ID: 3870f5780e72
4 | Revises: 05c3a952ea62
5 | Create Date: 2021-03-23 18:20:48.595673
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '3870f5780e72'
14 | down_revision = '05c3a952ea62'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('question', sa.Column('description_text', sa.String(), nullable=True))
22 | op.add_column('submission', sa.Column('description_text', sa.String(), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('submission', 'description_text')
29 | op.drop_column('question', 'description_text')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/3ad68389b887_add_draft_editor.py:
--------------------------------------------------------------------------------
1 | """Add draft_editor
2 |
3 | Revision ID: 3ad68389b887
4 | Revises: 0683eb57352d
5 | Create Date: 2021-04-24 16:03:53.212122
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '3ad68389b887'
14 | down_revision = '0683eb57352d'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('article', sa.Column('draft_editor', sa.String(), server_default='wysiwyg', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('article', 'draft_editor')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/3b7fbbbf91d1_payment_model.py:
--------------------------------------------------------------------------------
1 | """Payment model
2 |
3 | Revision ID: 3b7fbbbf91d1
4 | Revises: 6c3071d8d022
5 | Create Date: 2020-12-26 23:15:59.146322
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '3b7fbbbf91d1'
14 | down_revision = '6c3071d8d022'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | pass
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | pass
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/3b8106237cf3_update_flag.py:
--------------------------------------------------------------------------------
1 | """Update flag
2 |
3 | Revision ID: 3b8106237cf3
4 | Revises: 14e2a7adb2c6
5 | Create Date: 2021-01-11 20:36:56.451555
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '3b8106237cf3'
14 | down_revision = '14e2a7adb2c6'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_index(op.f('ix_question_is_placed_at_home'), 'question', ['is_placed_at_home'], unique=False)
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_index(op.f('ix_question_is_placed_at_home'), table_name='question')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/3e61d9fd232c_remove_deprecated_fields.py:
--------------------------------------------------------------------------------
1 | """Remove deprecated fields
2 |
3 | Revision ID: 3e61d9fd232c
4 | Revises: d9e2d499d67a
5 | Create Date: 2021-03-15 18:26:07.796528
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | from sqlalchemy.dialects import postgresql
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '3e61d9fd232c'
14 | down_revision = 'd9e2d499d67a'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.drop_column('user', 'invitation_token')
22 | op.drop_column('user', 'invitation_token_issued_at')
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.add_column('user', sa.Column('invitation_token_issued_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True))
29 | op.add_column('user', sa.Column('invitation_token', sa.VARCHAR(), autoincrement=False, nullable=True))
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/3f9f62883bbc_update_articles_model.py:
--------------------------------------------------------------------------------
1 | """Update articles model
2 |
3 | Revision ID: 3f9f62883bbc
4 | Revises: 5d2c2140d1ad
5 | Create Date: 2021-01-26 23:01:02.154146
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '3f9f62883bbc'
14 | down_revision = '5d2c2140d1ad'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('article', sa.Column('is_published', sa.Boolean(), server_default='false', nullable=False))
22 | op.add_column('article', sa.Column('title_draft', sa.String(), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('article', 'title_draft')
29 | op.drop_column('article', 'is_published')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/4300e970447a_remove_view_counters.py:
--------------------------------------------------------------------------------
1 | """Remove view counters
2 |
3 | Revision ID: 4300e970447a
4 | Revises: 9e6cfa47d06b
5 | Create Date: 2021-01-24 19:19:09.903397
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '4300e970447a'
14 | down_revision = '9e6cfa47d06b'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.drop_column('answer', 'view_times')
22 | op.drop_column('question', 'view_times')
23 | op.drop_column('user', 'profile_view_times')
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.add_column('user', sa.Column('profile_view_times', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=False))
30 | op.add_column('question', sa.Column('view_times', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=False))
31 | op.add_column('answer', sa.Column('view_times', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=False))
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/43be589a3aca_add_subject_user_uuid.py:
--------------------------------------------------------------------------------
1 | """Add subject_user_uuid
2 |
3 | Revision ID: 43be589a3aca
4 | Revises: fcdd9de59683
5 | Create Date: 2021-04-17 11:08:44.490933
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '43be589a3aca'
14 | down_revision = 'fcdd9de59683'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('feed', sa.Column('subject_user_uuid', sa.CHAR(length=20), nullable=True))
22 | op.create_foreign_key(None, 'feed', 'user', ['subject_user_uuid'], ['uuid'])
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_constraint(None, 'feed', type_='foreignkey')
29 | op.drop_column('feed', 'subject_user_uuid')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/49c39ab108d8_add_keywords_extracted_at_field.py:
--------------------------------------------------------------------------------
1 | """Add keywords_extracted_at field
2 |
3 | Revision ID: 49c39ab108d8
4 | Revises: bc9b289d6c53
5 | Create Date: 2021-03-24 17:17:38.496139
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '49c39ab108d8'
14 | down_revision = 'bc9b289d6c53'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('answer', sa.Column('keywords_extracted_at', sa.DateTime(timezone=True), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('answer', 'keywords_extracted_at')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/4f3bbe1eeaa6_add_editor_field_to_archives.py:
--------------------------------------------------------------------------------
1 | """Add editor field to archives
2 |
3 | Revision ID: 4f3bbe1eeaa6
4 | Revises: c803c015f56b
5 | Create Date: 2021-03-16 15:30:37.501477
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '4f3bbe1eeaa6'
14 | down_revision = 'c803c015f56b'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('archive', sa.Column('editor', sa.String(), server_default='wysiwyg', nullable=False))
22 | op.add_column('articlearchive', sa.Column('editor', sa.String(), server_default='wysiwyg', nullable=False))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('articlearchive', 'editor')
29 | op.drop_column('archive', 'editor')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/584772b747a3_update_nullable.py:
--------------------------------------------------------------------------------
1 | """Update nullable
2 |
3 | Revision ID: 584772b747a3
4 | Revises: e8574b6cdddb
5 | Create Date: 2022-01-28 22:36:52.089904
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '584772b747a3'
14 | down_revision = 'e8574b6cdddb'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('articlearchive', 'body',
22 | existing_type=sa.VARCHAR(),
23 | nullable=False)
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.alter_column('articlearchive', 'body',
30 | existing_type=sa.VARCHAR(),
31 | nullable=True)
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/5a29cea6c435_add_article_body_text_field.py:
--------------------------------------------------------------------------------
1 | """Add article body_text field
2 |
3 | Revision ID: 5a29cea6c435
4 | Revises: a55cb56957e2
5 | Create Date: 2021-05-31 23:26:04.270642
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '5a29cea6c435'
14 | down_revision = 'a55cb56957e2'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('article', sa.Column('body_text', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('article', 'body_text')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/5e5dc2de75fe_payment_model.py:
--------------------------------------------------------------------------------
1 | """Payment model
2 |
3 | Revision ID: 5e5dc2de75fe
4 | Revises: 6aefea467152
5 | Create Date: 2020-12-26 23:50:55.571444
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '5e5dc2de75fe'
14 | down_revision = '6aefea467152'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('coindeposit', sa.Column('ref_id', sa.String(), nullable=False))
22 | op.create_unique_constraint(None, 'coindeposit', ['ref_id'])
23 | op.add_column('coinpayment', sa.Column('ref_id', sa.String(), nullable=False))
24 | op.create_unique_constraint(None, 'coinpayment', ['ref_id'])
25 | # ### end Alembic commands ###
26 |
27 |
28 | def downgrade():
29 | # ### commands auto generated by Alembic - please adjust! ###
30 | op.drop_constraint(None, 'coinpayment', type_='unique')
31 | op.drop_column('coinpayment', 'ref_id')
32 | op.drop_constraint(None, 'coindeposit', type_='unique')
33 | op.drop_column('coindeposit', 'ref_id')
34 | # ### end Alembic commands ###
35 |
--------------------------------------------------------------------------------
/alembic/versions/5ff8d34a8126_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: 5ff8d34a8126
4 | Revises: 09f028729ac8
5 | Create Date: 2021-01-22 10:59:14.030130
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '5ff8d34a8126'
14 | down_revision = '09f028729ac8'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('invitation', sa.Column('personal_relation', sa.String(), nullable=True))
22 | op.add_column('user', sa.Column('invitation_token', sa.String(), nullable=True))
23 | op.add_column('user', sa.Column('invitation_token_issued_at', sa.DateTime(timezone=True), nullable=True))
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_column('user', 'invitation_token_issued_at')
30 | op.drop_column('user', 'invitation_token')
31 | op.drop_column('invitation', 'personal_relation')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/6162e247214e_add_derived_fields.py:
--------------------------------------------------------------------------------
1 | """Add derived fields
2 |
3 | Revision ID: 6162e247214e
4 | Revises: 584772b747a3
5 | Create Date: 2022-07-28 22:01:05.762211
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '6162e247214e'
14 | down_revision = '584772b747a3'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('interesting_question_ids', sa.JSON(), nullable=True))
22 | op.add_column('user', sa.Column('interesting_question_ids_updated_at', sa.DateTime(timezone=True), nullable=True))
23 | op.add_column('user', sa.Column('interesting_user_ids', sa.JSON(), nullable=True))
24 | op.add_column('user', sa.Column('interesting_user_ids_updated_at', sa.DateTime(timezone=True), nullable=True))
25 | # ### end Alembic commands ###
26 |
27 |
28 | def downgrade():
29 | # ### commands auto generated by Alembic - please adjust! ###
30 | op.drop_column('user', 'interesting_user_ids_updated_at')
31 | op.drop_column('user', 'interesting_user_ids')
32 | op.drop_column('user', 'interesting_question_ids_updated_at')
33 | op.drop_column('user', 'interesting_question_ids')
34 | # ### end Alembic commands ###
35 |
--------------------------------------------------------------------------------
/alembic/versions/61871dbc7ba7_add_featured_at.py:
--------------------------------------------------------------------------------
1 | """Add featured_at
2 |
3 | Revision ID: 61871dbc7ba7
4 | Revises: b9fc0a230cde
5 | Create Date: 2022-01-08 11:30:06.919139
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '61871dbc7ba7'
14 | down_revision = 'b9fc0a230cde'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('answer', sa.Column('featured_at', sa.DateTime(timezone=True), nullable=True))
22 | op.add_column('article', sa.Column('featured_at', sa.DateTime(timezone=True), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('article', 'featured_at')
29 | op.drop_column('answer', 'featured_at')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/621513063323_add_keywords_for_question.py:
--------------------------------------------------------------------------------
1 | """Add keywords for question
2 |
3 | Revision ID: 621513063323
4 | Revises: 49c39ab108d8
5 | Create Date: 2021-03-24 17:52:02.123180
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '621513063323'
14 | down_revision = '49c39ab108d8'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('question', sa.Column('keywords', sa.JSON(), nullable=True))
22 | op.add_column('question', sa.Column('keywords_extracted_at', sa.DateTime(timezone=True), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('question', 'keywords_extracted_at')
29 | op.drop_column('question', 'keywords')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/69f40011f79e_add_sub_topic.py:
--------------------------------------------------------------------------------
1 | """Add sub-topic
2 |
3 | Revision ID: 69f40011f79e
4 | Revises: b1c1a85fb152
5 | Create Date: 2021-01-29 21:39:19.213544
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '69f40011f79e'
14 | down_revision = 'b1c1a85fb152'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('topic', sa.Column('parent_topic_id', sa.Integer(), nullable=True))
22 | op.create_index(op.f('ix_topic_parent_topic_id'), 'topic', ['parent_topic_id'], unique=False)
23 | op.create_foreign_key(None, 'topic', 'topic', ['parent_topic_id'], ['id'])
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_constraint(None, 'topic', type_='foreignkey')
30 | op.drop_index(op.f('ix_topic_parent_topic_id'), table_name='topic')
31 | op.drop_column('topic', 'parent_topic_id')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/6a66054d75b6_add_feedbacks.py:
--------------------------------------------------------------------------------
1 | """Add feedbacks
2 |
3 | Revision ID: 6a66054d75b6
4 | Revises: 621513063323
5 | Create Date: 2021-03-25 12:40:20.159438
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '6a66054d75b6'
14 | down_revision = '621513063323'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('feedback',
22 | sa.Column('id', sa.Integer(), nullable=False),
23 | sa.Column('user_id', sa.Integer(), nullable=True),
24 | sa.Column('user_email', sa.String(), nullable=True),
25 | sa.Column('description', sa.String(), nullable=False),
26 | sa.Column('screenshot_blob', sa.LargeBinary(), nullable=True),
27 | sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
28 | sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
29 | sa.PrimaryKeyConstraint('id')
30 | )
31 | op.create_index(op.f('ix_feedback_id'), 'feedback', ['id'], unique=False)
32 | # ### end Alembic commands ###
33 |
34 |
35 | def downgrade():
36 | # ### commands auto generated by Alembic - please adjust! ###
37 | op.drop_index(op.f('ix_feedback_id'), table_name='feedback')
38 | op.drop_table('feedback')
39 | # ### end Alembic commands ###
40 |
--------------------------------------------------------------------------------
/alembic/versions/6aefea467152_payment_model.py:
--------------------------------------------------------------------------------
1 | """Payment model
2 |
3 | Revision ID: 6aefea467152
4 | Revises: 7699728838be
5 | Create Date: 2020-12-26 23:22:29.834682
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '6aefea467152'
14 | down_revision = '7699728838be'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | pass
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | pass
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/6b93fa0e2613_update_bookmark.py:
--------------------------------------------------------------------------------
1 | """Update bookmark
2 |
3 | Revision ID: 6b93fa0e2613
4 | Revises: eda6a2fac15c
5 | Create Date: 2020-12-28 03:00:04.643470
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '6b93fa0e2613'
14 | down_revision = 'eda6a2fac15c'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('personal_introduction', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'personal_introduction')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/6d8ddfacf048_add_user_feed_setting_field.py:
--------------------------------------------------------------------------------
1 | """Add user feed setting field
2 |
3 | Revision ID: 6d8ddfacf048
4 | Revises: e3ca93163b20
5 | Create Date: 2021-06-13 20:56:05.285570
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '6d8ddfacf048'
14 | down_revision = 'e3ca93163b20'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('feed_settings', sa.JSON(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'feed_settings')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/7699728838be_payment_model.py:
--------------------------------------------------------------------------------
1 | """Payment model
2 |
3 | Revision ID: 7699728838be
4 | Revises: 37da2a0a005c
5 | Create Date: 2020-12-26 23:22:07.874221
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '7699728838be'
14 | down_revision = '37da2a0a005c'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | pass
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | pass
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/77bf3df9f84d_update_invitation_model.py:
--------------------------------------------------------------------------------
1 | """Update invitation model
2 |
3 | Revision ID: 77bf3df9f84d
4 | Revises: 3081fd03a6fb
5 | Create Date: 2021-01-11 16:51:00.692594
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '77bf3df9f84d'
14 | down_revision = '3081fd03a6fb'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('invitation', sa.Column('invitation_link', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('invitation', 'invitation_link')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/78bb836eff0a_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: 78bb836eff0a
4 | Revises: 25b463cbeb95
5 | Create Date: 2021-01-09 18:19:57.416894
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '78bb836eff0a'
14 | down_revision = '25b463cbeb95'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('created_at', sa.DateTime(timezone=True), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'created_at')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/7b009fd9bbda_order_outgoing_incoming_rewards_in_model.py:
--------------------------------------------------------------------------------
1 | """Order outgoing/incoming rewards in model
2 |
3 | Revision ID: 7b009fd9bbda
4 | Revises: d727019ca7f0
5 | Create Date: 2021-06-10 23:38:05.857156
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '7b009fd9bbda'
14 | down_revision = 'd727019ca7f0'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_index(op.f('ix_reward_created_at'), 'reward', ['created_at'], unique=False)
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_index(op.f('ix_reward_created_at'), table_name='reward')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/816363389257_update_channel_model.py:
--------------------------------------------------------------------------------
1 | """Update channel model
2 |
3 | Revision ID: 816363389257
4 | Revises: b30886ac002d
5 | Create Date: 2020-12-31 17:30:16.633071
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '816363389257'
14 | down_revision = 'b30886ac002d'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | pass
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | pass
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/820e2f9cfb02_add_karma.py:
--------------------------------------------------------------------------------
1 | """Add karma
2 |
3 | Revision ID: 820e2f9cfb02
4 | Revises: 1df1637c47e7
5 | Create Date: 2021-01-31 00:15:01.361543
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '820e2f9cfb02'
14 | down_revision = '1df1637c47e7'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('karma', sa.Integer(), server_default='0', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'karma')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/8211a967ea0a_payment_model.py:
--------------------------------------------------------------------------------
1 | """Payment model
2 |
3 | Revision ID: 8211a967ea0a
4 | Revises: 5e5dc2de75fe
5 | Create Date: 2020-12-27 00:20:00.249339
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '8211a967ea0a'
14 | down_revision = '5e5dc2de75fe'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('site', sa.Column('create_question_coin_deduction', sa.Integer(), server_default='5', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('site', 'create_question_coin_deduction')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/831dfdf03a18_update_model.py:
--------------------------------------------------------------------------------
1 | """Update model
2 |
3 | Revision ID: 831dfdf03a18
4 | Revises: fd40a9df4c39
5 | Create Date: 2021-01-09 10:33:05.055498
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '831dfdf03a18'
14 | down_revision = 'fd40a9df4c39'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('coinpayment', sa.Column('event_json', sa.String(), nullable=True))
22 | op.drop_constraint('coinpayment_ref_id_key', 'coinpayment', type_='unique')
23 | op.create_unique_constraint(None, 'coinpayment', ['event_json', 'payee_id'])
24 | op.drop_column('coinpayment', 'ref_id')
25 | # ### end Alembic commands ###
26 |
27 |
28 | def downgrade():
29 | # ### commands auto generated by Alembic - please adjust! ###
30 | op.add_column('coinpayment', sa.Column('ref_id', sa.VARCHAR(), autoincrement=False, nullable=False))
31 | op.drop_constraint(None, 'coinpayment', type_='unique')
32 | op.create_unique_constraint('coinpayment_ref_id_key', 'coinpayment', ['ref_id'])
33 | op.drop_column('coinpayment', 'event_json')
34 | # ### end Alembic commands ###
35 |
--------------------------------------------------------------------------------
/alembic/versions/842e9e21589b_add_gif_avatar_url.py:
--------------------------------------------------------------------------------
1 | """Add gif_avatar_url
2 |
3 | Revision ID: 842e9e21589b
4 | Revises: c07f2ab88c8b
5 | Create Date: 2021-02-04 00:18:00.030211
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '842e9e21589b'
14 | down_revision = 'c07f2ab88c8b'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('gif_avatar_url', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'gif_avatar_url')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/8b1e740e7911_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: 8b1e740e7911
4 | Revises: 5ff8d34a8126
5 | Create Date: 2021-01-22 12:50:44.033143
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '8b1e740e7911'
14 | down_revision = '5ff8d34a8126'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('github_username', sa.String(), nullable=True))
22 | op.add_column('user', sa.Column('homepage_url', sa.String(), nullable=True))
23 | op.add_column('user', sa.Column('linkedin_url', sa.String(), nullable=True))
24 | op.add_column('user', sa.Column('twitter_username', sa.String(), nullable=True))
25 | # ### end Alembic commands ###
26 |
27 |
28 | def downgrade():
29 | # ### commands auto generated by Alembic - please adjust! ###
30 | op.drop_column('user', 'twitter_username')
31 | op.drop_column('user', 'linkedin_url')
32 | op.drop_column('user', 'homepage_url')
33 | op.drop_column('user', 'github_username')
34 | # ### end Alembic commands ###
35 |
--------------------------------------------------------------------------------
/alembic/versions/8c4461051c79_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: 8c4461051c79
4 | Revises: d81294903342
5 | Create Date: 2021-01-01 13:22:33.406488
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '8c4461051c79'
14 | down_revision = 'd81294903342'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('site', sa.Column('upvote_answer_coin_deduction', sa.Integer(), server_default='2', nullable=False))
22 | op.add_column('user', sa.Column('sent_new_user_invitataions', sa.Integer(), server_default='0', nullable=False))
23 | op.drop_column('user', 'sent_invitataions')
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.add_column('user', sa.Column('sent_invitataions', sa.INTEGER(), server_default=sa.text('0'), autoincrement=False, nullable=False))
30 | op.drop_column('user', 'sent_new_user_invitataions')
31 | op.drop_column('site', 'upvote_answer_coin_deduction')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/8d11e4de766f_fix_model.py:
--------------------------------------------------------------------------------
1 | """Fix model
2 |
3 | Revision ID: 8d11e4de766f
4 | Revises: b9652e802bb7
5 | Create Date: 2021-01-22 14:30:07.653180
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '8d11e4de766f'
14 | down_revision = 'b9652e802bb7'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('questionarchive', sa.Column('title', sa.String(), nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('questionarchive', 'title')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/8eb6a05d5d19_update_channel_model.py:
--------------------------------------------------------------------------------
1 | """Update channel model
2 |
3 | Revision ID: 8eb6a05d5d19
4 | Revises: a5cefdf035a6
5 | Create Date: 2020-12-30 13:37:28.531365
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '8eb6a05d5d19'
14 | down_revision = 'a5cefdf035a6'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('channel', 'is_private',
22 | existing_type=sa.BOOLEAN(),
23 | nullable=False)
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.alter_column('channel', 'is_private',
30 | existing_type=sa.BOOLEAN(),
31 | nullable=True)
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/91aa83eae955_add_user_keywords.py:
--------------------------------------------------------------------------------
1 | """Add user keywords
2 |
3 | Revision ID: 91aa83eae955
4 | Revises: 0f8b2edf3460
5 | Create Date: 2021-09-07 23:44:46.665921
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '91aa83eae955'
14 | down_revision = '0f8b2edf3460'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('site', 'name',
22 | existing_type=sa.VARCHAR(),
23 | nullable=False)
24 | op.add_column('user', sa.Column('keywords', sa.JSON(), nullable=True))
25 | # ### end Alembic commands ###
26 |
27 |
28 | def downgrade():
29 | # ### commands auto generated by Alembic - please adjust! ###
30 | op.drop_column('user', 'keywords')
31 | op.alter_column('site', 'name',
32 | existing_type=sa.VARCHAR(),
33 | nullable=True)
34 | # ### end Alembic commands ###
35 |
--------------------------------------------------------------------------------
/alembic/versions/92e8bf7639ca_add_profession_topics.py:
--------------------------------------------------------------------------------
1 | """Add profession_topics
2 |
3 | Revision ID: 92e8bf7639ca
4 | Revises: 1da7e93fd803
5 | Create Date: 2021-11-25 11:32:47.880919
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | from sqlalchemy.dialects import postgresql
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '92e8bf7639ca'
14 | down_revision = '1da7e93fd803'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('profession_topics',
22 | sa.Column('user_id', sa.Integer(), nullable=True),
23 | sa.Column('topic_id', sa.Integer(), nullable=True),
24 | sa.ForeignKeyConstraint(['topic_id'], ['topic.id'], ),
25 | sa.ForeignKeyConstraint(['user_id'], ['user.id'], )
26 | )
27 | op.drop_column('user', 'profession_topics_json')
28 | # ### end Alembic commands ###
29 |
30 |
31 | def downgrade():
32 | # ### commands auto generated by Alembic - please adjust! ###
33 | op.add_column('user', sa.Column('profession_topics_json', postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True))
34 | op.drop_table('profession_topics')
35 | # ### end Alembic commands ###
36 |
--------------------------------------------------------------------------------
/alembic/versions/92fae726b416_add_location_url.py:
--------------------------------------------------------------------------------
1 | """Add location_url
2 |
3 | Revision ID: 92fae726b416
4 | Revises: ce37e212bb64
5 | Create Date: 2021-12-24 00:37:59.806346
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '92fae726b416'
14 | down_revision = 'ce37e212bb64'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('feedback', sa.Column('location_url', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('feedback', 'location_url')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/975224f8aced_add_description_format_fields_for_.py:
--------------------------------------------------------------------------------
1 | """Add description format fields for question archive
2 |
3 | Revision ID: 975224f8aced
4 | Revises: 6d8ddfacf048
5 | Create Date: 2021-06-21 22:04:12.958530
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '975224f8aced'
14 | down_revision = '6d8ddfacf048'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('questionarchive', sa.Column('description_text', sa.String(), nullable=True))
22 | op.add_column('questionarchive', sa.Column('description_editor', sa.String(), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('questionarchive', 'description_editor')
29 | op.drop_column('questionarchive', 'description_text')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/98555907d799_add_created_at_field_for_site.py:
--------------------------------------------------------------------------------
1 | """Add created_at field for Site
2 |
3 | Revision ID: 98555907d799
4 | Revises: 13401d048f7d
5 | Create Date: 2021-03-08 00:22:36.495193
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '98555907d799'
14 | down_revision = '13401d048f7d'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('site', sa.Column('created_at', sa.DateTime(timezone=True), nullable=True))
22 | op.drop_column('user', 'work_experiences_json')
23 | op.drop_column('user', 'education_experiences_json')
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.add_column('user', sa.Column('education_experiences_json', sa.VARCHAR(), autoincrement=False, nullable=True))
30 | op.add_column('user', sa.Column('work_experiences_json', sa.VARCHAR(), autoincrement=False, nullable=True))
31 | op.drop_column('site', 'created_at')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/98fd84b5c1ef_add_zhihu_url.py:
--------------------------------------------------------------------------------
1 | """Add zhihu_url
2 |
3 | Revision ID: 98fd84b5c1ef
4 | Revises: 055c3cdb6b04
5 | Create Date: 2021-12-11 23:42:13.048758
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '98fd84b5c1ef'
14 | down_revision = '055c3cdb6b04'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('zhihu_url', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'zhihu_url')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/9990700dc85b_add_answer_bookmarks.py:
--------------------------------------------------------------------------------
1 | """Add answer bookmarks
2 |
3 | Revision ID: 9990700dc85b
4 | Revises: a8c68d11ccf0
5 | Create Date: 2020-12-27 12:57:38.575389
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '9990700dc85b'
14 | down_revision = 'a8c68d11ccf0'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('bookmarked_answers',
22 | sa.Column('user_id', sa.Integer(), nullable=True),
23 | sa.Column('answer_id', sa.Integer(), nullable=True),
24 | sa.ForeignKeyConstraint(['answer_id'], ['answer.id'], ),
25 | sa.ForeignKeyConstraint(['user_id'], ['user.id'], )
26 | )
27 | # ### end Alembic commands ###
28 |
29 |
30 | def downgrade():
31 | # ### commands auto generated by Alembic - please adjust! ###
32 | op.drop_table('bookmarked_answers')
33 | # ### end Alembic commands ###
34 |
--------------------------------------------------------------------------------
/alembic/versions/9a8a72e3fa0c_add_submission_ardchive_topic_uuids.py:
--------------------------------------------------------------------------------
1 | """Add submission ardchive topic uuids
2 |
3 | Revision ID: 9a8a72e3fa0c
4 | Revises: f9cc4868db52
5 | Create Date: 2021-06-10 00:02:50.614138
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '9a8a72e3fa0c'
14 | down_revision = 'f9cc4868db52'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('submissionarchive', sa.Column('topic_uuids', sa.JSON(), nullable=True))
22 | op.add_column('submissionsuggestion', sa.Column('accepted_diff', sa.JSON(), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('submissionsuggestion', 'accepted_diff')
29 | op.drop_column('submissionarchive', 'topic_uuids')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/9cc77f9fff00_add_category_topic_for_site.py:
--------------------------------------------------------------------------------
1 | """Add category_topic for site
2 |
3 | Revision ID: 9cc77f9fff00
4 | Revises: 4f3bbe1eeaa6
5 | Create Date: 2021-03-20 23:35:43.957858
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '9cc77f9fff00'
14 | down_revision = '4f3bbe1eeaa6'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('site', sa.Column('category_topic_id', sa.Integer(), nullable=True))
22 | op.create_index(op.f('ix_site_category_topic_id'), 'site', ['category_topic_id'], unique=False)
23 | op.create_foreign_key(None, 'site', 'topic', ['category_topic_id'], ['id'])
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_constraint(None, 'site', type_='foreignkey')
30 | op.drop_index(op.f('ix_site_category_topic_id'), table_name='site')
31 | op.drop_column('site', 'category_topic_id')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/9fee55d72d95_update_answer_upvote_model.py:
--------------------------------------------------------------------------------
1 | """Update answer upvote model
2 |
3 | Revision ID: 9fee55d72d95
4 | Revises: 6a3707257d81
5 | Create Date: 2021-01-11 00:36:54.766141
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '9fee55d72d95'
14 | down_revision = '6a3707257d81'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('answer_upvotes', sa.Column('cancelled', sa.Boolean(), server_default='false', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('answer_upvotes', 'cancelled')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/a2c4da7f3afc_add_editor_to_comment.py:
--------------------------------------------------------------------------------
1 | """Add editor to comment
2 |
3 | Revision ID: a2c4da7f3afc
4 | Revises: 6a66054d75b6
5 | Create Date: 2021-03-26 17:33:16.627088
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'a2c4da7f3afc'
14 | down_revision = '6a66054d75b6'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('comment', sa.Column('body_html', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('comment', 'body_html')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/a5cefdf035a6_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: a5cefdf035a6
4 | Revises: e0bb8baabaa5
5 | Create Date: 2020-12-30 11:31:59.524140
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'a5cefdf035a6'
14 | down_revision = 'e0bb8baabaa5'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('handle', sa.String(), nullable=True))
22 | op.create_index(op.f('ix_user_handle'), 'user', ['handle'], unique=True)
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_index(op.f('ix_user_handle'), table_name='user')
29 | op.drop_column('user', 'handle')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/a8802e4a70f4_update.py:
--------------------------------------------------------------------------------
1 | """Update
2 |
3 | Revision ID: a8802e4a70f4
4 | Revises: d7239240b702
5 | Create Date: 2021-12-04 01:06:20.026030
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | from sqlalchemy.dialects import postgresql
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'a8802e4a70f4'
14 | down_revision = 'd7239240b702'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('submissionsuggestion', 'accepted_diff_base',
22 | existing_type=postgresql.JSON(astext_type=sa.Text()),
23 | nullable=False)
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.alter_column('submissionsuggestion', 'accepted_diff_base',
30 | existing_type=postgresql.JSON(astext_type=sa.Text()),
31 | nullable=True)
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/ae37fb780460_update_site_model.py:
--------------------------------------------------------------------------------
1 | """Update site model
2 |
3 | Revision ID: ae37fb780460
4 | Revises: ebc55177cad6
5 | Create Date: 2021-01-03 16:59:06.113963
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'ae37fb780460'
14 | down_revision = 'ebc55177cad6'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_index(op.f('ix_site_subdomain'), 'site', ['subdomain'], unique=True)
22 | op.drop_constraint('site_subdomain_key', 'site', type_='unique')
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.create_unique_constraint('site_subdomain_key', 'site', ['subdomain'])
29 | op.drop_index(op.f('ix_site_subdomain'), table_name='site')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/b087a350779c_add_submission_subscription.py:
--------------------------------------------------------------------------------
1 | """Add submission subscription
2 |
3 | Revision ID: b087a350779c
4 | Revises: ecce4b4966ab
5 | Create Date: 2021-03-11 15:59:55.481843
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'b087a350779c'
14 | down_revision = 'ecce4b4966ab'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('subscribed_submission',
22 | sa.Column('user_id', sa.Integer(), nullable=True),
23 | sa.Column('submission_id', sa.Integer(), nullable=True),
24 | sa.ForeignKeyConstraint(['submission_id'], ['submission.id'], ),
25 | sa.ForeignKeyConstraint(['user_id'], ['user.id'], )
26 | )
27 | # ### end Alembic commands ###
28 |
29 |
30 | def downgrade():
31 | # ### commands auto generated by Alembic - please adjust! ###
32 | op.drop_table('subscribed_submission')
33 | # ### end Alembic commands ###
34 |
--------------------------------------------------------------------------------
/alembic/versions/b15af42b3583_add_body_prerendered_text_for_article.py:
--------------------------------------------------------------------------------
1 | """Add body_prerendered_text for article
2 |
3 | Revision ID: b15af42b3583
4 | Revises: 3870f5780e72
5 | Create Date: 2021-03-24 16:26:54.280392
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'b15af42b3583'
14 | down_revision = '3870f5780e72'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('article', sa.Column('body_prerendered_text', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('article', 'body_prerendered_text')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/b1c1a85fb152_add_phone_number.py:
--------------------------------------------------------------------------------
1 | """Add phone_number
2 |
3 | Revision ID: b1c1a85fb152
4 | Revises: 3f9f62883bbc
5 | Create Date: 2021-01-28 17:10:27.796252
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'b1c1a85fb152'
14 | down_revision = '3f9f62883bbc'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('phone_number', sa.String(), nullable=True))
22 | op.create_index(op.f('ix_user_phone_number'), 'user', ['phone_number'], unique=True)
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_index(op.f('ix_user_phone_number'), table_name='user')
29 | op.drop_column('user', 'phone_number')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/b30886ac002d_update_channel_model.py:
--------------------------------------------------------------------------------
1 | """Update channel model
2 |
3 | Revision ID: b30886ac002d
4 | Revises: 2149dde81b01
5 | Create Date: 2020-12-31 17:27:39.835773
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'b30886ac002d'
14 | down_revision = '2149dde81b01'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('channel', sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('channel', 'updated_at')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/b86c5b5477fd_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: b86c5b5477fd
4 | Revises: db11135cb499
5 | Create Date: 2021-02-12 11:11:39.328645
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'b86c5b5477fd'
14 | down_revision = 'db11135cb499'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('default_editor_mode', sa.String(), server_default='wysiwyg', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'default_editor_mode')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/b8d3e3432d39_add_comment_body_text_field.py:
--------------------------------------------------------------------------------
1 | """Add comment body_text field
2 |
3 | Revision ID: b8d3e3432d39
4 | Revises: a2c4da7f3afc
5 | Create Date: 2021-03-30 14:13:50.104724
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'b8d3e3432d39'
14 | down_revision = 'a2c4da7f3afc'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('comment', sa.Column('body_text', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('comment', 'body_text')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/bc9b289d6c53_add_keywords_for_answer.py:
--------------------------------------------------------------------------------
1 | """Add keywords for answer
2 |
3 | Revision ID: bc9b289d6c53
4 | Revises: b15af42b3583
5 | Create Date: 2021-03-24 17:11:51.062276
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'bc9b289d6c53'
14 | down_revision = 'b15af42b3583'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('answer', sa.Column('keywords', sa.JSON(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('answer', 'keywords')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/bf88da60488f_add_subject_to_channel.py:
--------------------------------------------------------------------------------
1 | """Add subject to channel
2 |
3 | Revision ID: bf88da60488f
4 | Revises: 98fd84b5c1ef
5 | Create Date: 2021-12-18 22:47:41.268911
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'bf88da60488f'
14 | down_revision = '98fd84b5c1ef'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('channel', sa.Column('feedback_subject_id', sa.Integer(), nullable=True))
22 | op.create_index(op.f('ix_channel_feedback_subject_id'), 'channel', ['feedback_subject_id'], unique=False)
23 | op.create_foreign_key(None, 'channel', 'feedback', ['feedback_subject_id'], ['id'])
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_constraint(None, 'channel', type_='foreignkey')
30 | op.drop_index(op.f('ix_channel_feedback_subject_id'), table_name='channel')
31 | op.drop_column('channel', 'feedback_subject_id')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/c07f2ab88c8b_add_claimed_welcome_test_rewards_with_.py:
--------------------------------------------------------------------------------
1 | """Add claimed_welcome_test_rewards_with_form_response_id
2 |
3 | Revision ID: c07f2ab88c8b
4 | Revises: d19bd4e72e97
5 | Create Date: 2021-02-03 22:56:18.827604
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'c07f2ab88c8b'
14 | down_revision = 'd19bd4e72e97'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('claimed_welcome_test_rewards_with_form_response_id', sa.Integer(), nullable=True))
22 | op.create_foreign_key(None, 'user', 'formresponse', ['claimed_welcome_test_rewards_with_form_response_id'], ['id'])
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_constraint(None, 'user', type_='foreignkey')
29 | op.drop_column('user', 'claimed_welcome_test_rewards_with_form_response_id')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/c26f33a78370_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: c26f33a78370
4 | Revises: ae37fb780460
5 | Create Date: 2021-01-05 22:44:14.553160
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'c26f33a78370'
14 | down_revision = 'ae37fb780460'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('avatar_url', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'avatar_url')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/c590109a43f8_regenerated_data_model.py:
--------------------------------------------------------------------------------
1 | """Regenerated data model
2 |
3 | Revision ID: c590109a43f8
4 | Revises: c96147f8c295
5 | Create Date: 2020-12-26 00:33:34.010022
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'c590109a43f8'
14 | down_revision = 'c96147f8c295'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_unique_constraint(None, 'profile', ['owner_id', 'site_id'])
22 | op.add_column('user', sa.Column('education_experiences_json', sa.String(), nullable=True))
23 | op.add_column('user', sa.Column('work_experiences_json', sa.String(), nullable=True))
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_column('user', 'work_experiences_json')
30 | op.drop_column('user', 'education_experiences_json')
31 | op.drop_constraint(None, 'profile', type_='unique')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/c803c015f56b_add_article_prerendered.py:
--------------------------------------------------------------------------------
1 | """Add article prerendered
2 |
3 | Revision ID: c803c015f56b
4 | Revises: f6a5ec609bf7
5 | Create Date: 2021-03-16 11:29:38.280971
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'c803c015f56b'
14 | down_revision = 'f6a5ec609bf7'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('article', sa.Column('body_prerendered', sa.String(), nullable=True))
22 | op.add_column('article', sa.Column('prerendered_at', sa.DateTime(timezone=True), nullable=True))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('article', 'prerendered_at')
29 | op.drop_column('article', 'body_prerendered')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/c8fd907cf166_remove_user_about_editor.py:
--------------------------------------------------------------------------------
1 | """Remove user.about_editor
2 |
3 | Revision ID: c8fd907cf166
4 | Revises: 91aa83eae955
5 | Create Date: 2021-11-12 18:30:37.359288
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'c8fd907cf166'
14 | down_revision = '91aa83eae955'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.drop_column('user', 'about_editor')
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.add_column('user', sa.Column('about_editor', sa.VARCHAR(), server_default=sa.text("'wysiwyg'::character varying"), autoincrement=False, nullable=False))
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/ccddb38c98c2_add_question_upvotes.py:
--------------------------------------------------------------------------------
1 | """Add question upvotes
2 |
3 | Revision ID: ccddb38c98c2
4 | Revises: c376381a1d91
5 | Create Date: 2021-01-14 15:32:48.159670
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'ccddb38c98c2'
14 | down_revision = 'c376381a1d91'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_unique_constraint(None, 'questionupvotes', ['question_id', 'voter_id'])
22 | op.add_column('site', sa.Column('upvote_question_coin_deduction', sa.Integer(), server_default='1', nullable=False))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('site', 'upvote_question_coin_deduction')
29 | op.drop_constraint(None, 'questionupvotes', type_='unique')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/cd1d0246bf82_update_site_model.py:
--------------------------------------------------------------------------------
1 | """Update site model
2 |
3 | Revision ID: cd1d0246bf82
4 | Revises: f43df48a00e2
5 | Create Date: 2021-02-27 13:27:29.263287
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'cd1d0246bf82'
14 | down_revision = 'f43df48a00e2'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('site', sa.Column('auto_approval', sa.Boolean(), server_default='false', nullable=False))
22 | op.add_column('site', sa.Column('email_domain_suffix_for_application', sa.String(), nullable=True))
23 | op.add_column('site', sa.Column('min_karma_for_application', sa.Integer(), nullable=True))
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_column('site', 'min_karma_for_application')
30 | op.drop_column('site', 'email_domain_suffix_for_application')
31 | op.drop_column('site', 'auto_approval')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/ce37e212bb64_update.py:
--------------------------------------------------------------------------------
1 | """Update
2 |
3 | Revision ID: ce37e212bb64
4 | Revises: bf88da60488f
5 | Create Date: 2021-12-19 14:44:36.257531
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'ce37e212bb64'
14 | down_revision = 'bf88da60488f'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('channel', sa.Column('site_creation_subject_subdomain', sa.String(), nullable=True))
22 | op.add_column('channel', sa.Column('site_creation_subject', sa.JSON(), nullable=True))
23 | op.create_index(op.f('ix_channel_site_creation_subject_subdomain'), 'channel', ['site_creation_subject_subdomain'], unique=False)
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_index(op.f('ix_channel_site_creation_subject_subdomain'), table_name='channel')
30 | op.drop_column('channel', 'site_creation_subject')
31 | op.drop_column('channel', 'site_creation_subject_subdomain')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/d1396c067972_remove_content_json.py:
--------------------------------------------------------------------------------
1 | """Remove content_json
2 |
3 | Revision ID: d1396c067972
4 | Revises: 1541eb7cdb55
5 | Create Date: 2021-01-31 19:45:39.489585
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'd1396c067972'
14 | down_revision = '1541eb7cdb55'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.drop_column('activity', 'content_json')
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.add_column('activity', sa.Column('content_json', sa.VARCHAR(), autoincrement=False, nullable=True))
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/d19bd4e72e97_update_forms.py:
--------------------------------------------------------------------------------
1 | """Update forms
2 |
3 | Revision ID: d19bd4e72e97
4 | Revises: be76b1afbc63
5 | Create Date: 2021-02-03 21:03:05.804599
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'd19bd4e72e97'
14 | down_revision = 'be76b1afbc63'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('form', sa.Column('form_fields', sa.JSON(), nullable=False))
22 | op.add_column('formresponse', sa.Column('response_fields', sa.JSON(), nullable=False))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('formresponse', 'response_fields')
29 | op.drop_column('form', 'form_fields')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/d7239240b702_update.py:
--------------------------------------------------------------------------------
1 | """Update
2 |
3 | Revision ID: d7239240b702
4 | Revises: 92e8bf7639ca
5 | Create Date: 2021-11-25 13:05:50.512696
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'd7239240b702'
14 | down_revision = '92e8bf7639ca'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | pass
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | pass
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/d727019ca7f0_add_submission_suggestion_accepted_diff_.py:
--------------------------------------------------------------------------------
1 | """Add submission suggestion accepted diff base
2 |
3 | Revision ID: d727019ca7f0
4 | Revises: 031d89b0d6ea
5 | Create Date: 2021-06-10 00:13:03.037413
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | from sqlalchemy.dialects import postgresql
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'd727019ca7f0'
14 | down_revision = '031d89b0d6ea'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('submissionsuggestion', sa.Column('accepted_diff_base', sa.JSON(), nullable=True))
22 | op.drop_column('submissionsuggestion', 'accepted_diff')
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.add_column('submissionsuggestion', sa.Column('accepted_diff', postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True))
29 | op.drop_column('submissionsuggestion', 'accepted_diff_base')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/d81294903342_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: d81294903342
4 | Revises: 816363389257
5 | Create Date: 2021-01-01 00:57:25.237846
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'd81294903342'
14 | down_revision = '816363389257'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.drop_column('user', 'is_system_user')
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.add_column('user', sa.Column('is_system_user', sa.BOOLEAN(), autoincrement=False, nullable=True))
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/d9e2d499d67a_add_content_visibility_flag_for_article.py:
--------------------------------------------------------------------------------
1 | """Add content visibility flag for article
2 |
3 | Revision ID: d9e2d499d67a
4 | Revises: f3822eef0de4
5 | Create Date: 2021-03-13 00:48:36.293723
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'd9e2d499d67a'
14 | down_revision = 'f3822eef0de4'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('article', sa.Column('visibility', sa.Enum('ANYONE', 'REGISTERED', name='contentvisibility'), server_default='ANYONE', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('article', 'visibility')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/da17aa8c10be_add_nullable_false.py:
--------------------------------------------------------------------------------
1 | """Add nullable=False
2 |
3 | Revision ID: da17aa8c10be
4 | Revises: 98555907d799
5 | Create Date: 2021-03-08 00:36:51.977520
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | from sqlalchemy.dialects import postgresql
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'da17aa8c10be'
14 | down_revision = '98555907d799'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('site', 'created_at',
22 | existing_type=postgresql.TIMESTAMP(timezone=True),
23 | nullable=False)
24 | op.alter_column('user', 'created_at',
25 | existing_type=postgresql.TIMESTAMP(timezone=True),
26 | nullable=False)
27 | # ### end Alembic commands ###
28 |
29 |
30 | def downgrade():
31 | # ### commands auto generated by Alembic - please adjust! ###
32 | op.alter_column('user', 'created_at',
33 | existing_type=postgresql.TIMESTAMP(timezone=True),
34 | nullable=True)
35 | op.alter_column('site', 'created_at',
36 | existing_type=postgresql.TIMESTAMP(timezone=True),
37 | nullable=True)
38 | # ### end Alembic commands ###
39 |
--------------------------------------------------------------------------------
/alembic/versions/db11135cb499_update_comment_model.py:
--------------------------------------------------------------------------------
1 | """Update comment model
2 |
3 | Revision ID: db11135cb499
4 | Revises: fe7fda109125
5 | Create Date: 2021-02-12 10:41:28.923663
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'db11135cb499'
14 | down_revision = 'fe7fda109125'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('comment', sa.Column('is_deleted', sa.Boolean(), server_default='false', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('comment', 'is_deleted')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/e0bb8baabaa5_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: e0bb8baabaa5
4 | Revises: 6b93fa0e2613
5 | Create Date: 2020-12-28 23:09:58.418021
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'e0bb8baabaa5'
14 | down_revision = '6b93fa0e2613'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('locale_preference', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'locale_preference')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/e3ca93163b20_add_suggestion_comment.py:
--------------------------------------------------------------------------------
1 | """Add suggestion comment
2 |
3 | Revision ID: e3ca93163b20
4 | Revises: 096f816e6921
5 | Create Date: 2021-06-13 11:02:16.953333
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'e3ca93163b20'
14 | down_revision = '096f816e6921'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('submissionsuggestion', sa.Column('comment', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('submissionsuggestion', 'comment')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/e476333cd0b1_update_model.py:
--------------------------------------------------------------------------------
1 | """Update model
2 |
3 | Revision ID: e476333cd0b1
4 | Revises: 1cfd11f59759
5 | Create Date: 2021-01-16 14:13:52.443301
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'e476333cd0b1'
14 | down_revision = '1cfd11f59759'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.drop_column('answer', 'is_autosaved')
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.add_column('answer', sa.Column('is_autosaved', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/e8574b6cdddb_update_nullable.py:
--------------------------------------------------------------------------------
1 | """Update nullable
2 |
3 | Revision ID: e8574b6cdddb
4 | Revises: daa73c481ef0
5 | Create Date: 2022-01-28 22:32:54.583375
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'e8574b6cdddb'
14 | down_revision = 'daa73c481ef0'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('answer', 'is_hidden_by_moderator',
22 | existing_type=sa.BOOLEAN(),
23 | nullable=False,
24 | existing_server_default=sa.text('false'))
25 | # ### end Alembic commands ###
26 |
27 |
28 | def downgrade():
29 | # ### commands auto generated by Alembic - please adjust! ###
30 | op.alter_column('answer', 'is_hidden_by_moderator',
31 | existing_type=sa.BOOLEAN(),
32 | nullable=True,
33 | existing_server_default=sa.text('false'))
34 | # ### end Alembic commands ###
35 |
--------------------------------------------------------------------------------
/alembic/versions/ebc3062bc724_update_answer_upvote_model.py:
--------------------------------------------------------------------------------
1 | """Update answer upvote model
2 |
3 | Revision ID: ebc3062bc724
4 | Revises: 9fee55d72d95
5 | Create Date: 2021-01-11 01:09:31.224977
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'ebc3062bc724'
14 | down_revision = '9fee55d72d95'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_unique_constraint(None, 'answer_upvotes', ['answer_id', 'voter_id'])
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_constraint(None, 'answer_upvotes', type_='unique')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/ebc55177cad6_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: ebc55177cad6
4 | Revises: f66a6a2cbb25
5 | Create Date: 2021-01-02 13:09:36.492212
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'ebc55177cad6'
14 | down_revision = 'f66a6a2cbb25'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('user', 'handle',
22 | existing_type=sa.VARCHAR(),
23 | nullable=False)
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.alter_column('user', 'handle',
30 | existing_type=sa.VARCHAR(),
31 | nullable=True)
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/ecce4b4966ab_remove_is_placed_at_question_top.py:
--------------------------------------------------------------------------------
1 | """Remove is_placed_at_question_top
2 |
3 | Revision ID: ecce4b4966ab
4 | Revises: da17aa8c10be
5 | Create Date: 2021-03-09 11:28:11.698308
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'ecce4b4966ab'
14 | down_revision = 'da17aa8c10be'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.drop_column('answer', 'is_placed_at_question_top')
22 | op.drop_column('question', 'is_placed_at_site_top')
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.add_column('question', sa.Column('is_placed_at_site_top', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
29 | op.add_column('answer', sa.Column('is_placed_at_question_top', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/eda6a2fac15c_update_bookmark.py:
--------------------------------------------------------------------------------
1 | """Update bookmark
2 |
3 | Revision ID: eda6a2fac15c
4 | Revises: 9990700dc85b
5 | Create Date: 2020-12-27 13:04:46.640413
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'eda6a2fac15c'
14 | down_revision = '9990700dc85b'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | pass
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | pass
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/efd4a8bf854e_add_per_site_karma.py:
--------------------------------------------------------------------------------
1 | """Add per site karma
2 |
3 | Revision ID: efd4a8bf854e
4 | Revises: 2668718cdb60
5 | Create Date: 2021-02-13 22:01:03.086875
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'efd4a8bf854e'
14 | down_revision = '2668718cdb60'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_unique_constraint(None, 'commentupvotes', ['comment_id', 'voter_id'])
22 | op.add_column('profile', sa.Column('karma', sa.Integer(), server_default='0', nullable=False))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('profile', 'karma')
29 | op.drop_constraint(None, 'commentupvotes', type_='unique')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/f3822eef0de4_add_content_visibility_flag_for_answer.py:
--------------------------------------------------------------------------------
1 | """Add content visibility flag for answer
2 |
3 | Revision ID: f3822eef0de4
4 | Revises: b087a350779c
5 | Create Date: 2021-03-13 00:01:21.781870
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'f3822eef0de4'
14 | down_revision = 'b087a350779c'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | contentvisibility = sa.Enum('ANYONE', 'REGISTERED', name='contentvisibility')
22 | contentvisibility.create(op.get_bind())
23 | op.add_column('answer', sa.Column('visibility', sa.Enum('ANYONE', 'REGISTERED', name='contentvisibility'), server_default='ANYONE', nullable=False))
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_column('answer', 'visibility')
30 | # ### end Alembic commands ###
31 | contentvisibility = sa.Enum('ANYONE', 'REGISTERED', name='contentvisibility')
32 | contentvisibility.drop(op.get_bind())
33 |
--------------------------------------------------------------------------------
/alembic/versions/f3925b9ee72e_update_index.py:
--------------------------------------------------------------------------------
1 | """Update index
2 |
3 | Revision ID: f3925b9ee72e
4 | Revises: 975224f8aced
5 | Create Date: 2021-08-24 00:20:33.207363
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'f3925b9ee72e'
14 | down_revision = '975224f8aced'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_index(op.f('ix_followers_followed_id'), 'followers', ['followed_id'], unique=False)
22 | op.create_index(op.f('ix_followers_follower_id'), 'followers', ['follower_id'], unique=False)
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_index(op.f('ix_followers_follower_id'), table_name='followers')
29 | op.drop_index(op.f('ix_followers_followed_id'), table_name='followers')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/f43df48a00e2_update_user_model.py:
--------------------------------------------------------------------------------
1 | """Update user model
2 |
3 | Revision ID: f43df48a00e2
4 | Revises: 690f409863df
5 | Create Date: 2021-02-27 12:48:54.318762
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'f43df48a00e2'
14 | down_revision = '690f409863df'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('secondary_emails', sa.JSON(), server_default='[]', nullable=False))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'secondary_emails')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/f6a5ec609bf7_add_body_prerendered_text_field.py:
--------------------------------------------------------------------------------
1 | """Add body prerendered text field
2 |
3 | Revision ID: f6a5ec609bf7
4 | Revises: 4fab480583ba
5 | Create Date: 2021-03-16 11:08:21.959884
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'f6a5ec609bf7'
14 | down_revision = '4fab480583ba'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('answer', sa.Column('body_prerendered_text', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('answer', 'body_prerendered_text')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/f8e5c9d0d48a_update_model.py:
--------------------------------------------------------------------------------
1 | """Update model
2 |
3 | Revision ID: f8e5c9d0d48a
4 | Revises: 0144c1da0fe6
5 | Create Date: 2021-02-01 18:11:24.287115
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'f8e5c9d0d48a'
14 | down_revision = '0144c1da0fe6'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('reward', sa.Column('refunded_at', sa.DateTime(timezone=True), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('reward', 'refunded_at')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/f9cc4868db52_add_submission_suggestion_topic_uuids.py:
--------------------------------------------------------------------------------
1 | """Add submission suggestion topic uuids
2 |
3 | Revision ID: f9cc4868db52
4 | Revises: dffd3046e10c
5 | Create Date: 2021-06-09 22:05:05.557806
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'f9cc4868db52'
14 | down_revision = 'dffd3046e10c'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('submissionsuggestion', sa.Column('topic_uuids', sa.JSON(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('submissionsuggestion', 'topic_uuids')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/alembic/versions/fa7a9c06c679_add_about_in_user_profile.py:
--------------------------------------------------------------------------------
1 | """Add about in user profile
2 |
3 | Revision ID: fa7a9c06c679
4 | Revises: 06369b058ac1
5 | Create Date: 2021-05-16 00:00:51.751172
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'fa7a9c06c679'
14 | down_revision = '06369b058ac1'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('about', sa.String(), nullable=True))
22 | op.add_column('user', sa.Column('about_editor', sa.String(), server_default='wysiwyg', nullable=False))
23 | # ### end Alembic commands ###
24 |
25 |
26 | def downgrade():
27 | # ### commands auto generated by Alembic - please adjust! ###
28 | op.drop_column('user', 'about_editor')
29 | op.drop_column('user', 'about')
30 | # ### end Alembic commands ###
31 |
--------------------------------------------------------------------------------
/alembic/versions/fe7fda109125_update_question_model.py:
--------------------------------------------------------------------------------
1 | """Update question model
2 |
3 | Revision ID: fe7fda109125
4 | Revises: 4c0ee489cfa1
5 | Create Date: 2021-02-08 12:18:16.070161
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'fe7fda109125'
14 | down_revision = '4c0ee489cfa1'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.alter_column('question', 'is_hidden',
22 | existing_type=sa.BOOLEAN(),
23 | nullable=False,
24 | existing_server_default=sa.text('false'))
25 | # ### end Alembic commands ###
26 |
27 |
28 | def downgrade():
29 | # ### commands auto generated by Alembic - please adjust! ###
30 | op.alter_column('question', 'is_hidden',
31 | existing_type=sa.BOOLEAN(),
32 | nullable=True,
33 | existing_server_default=sa.text('false'))
34 | # ### end Alembic commands ###
35 |
--------------------------------------------------------------------------------
/alembic/versions/fed07815fb47_add_view_times.py:
--------------------------------------------------------------------------------
1 | """Add view times
2 |
3 | Revision ID: fed07815fb47
4 | Revises: 8211a967ea0a
5 | Create Date: 2020-12-27 00:55:36.161559
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'fed07815fb47'
14 | down_revision = '8211a967ea0a'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('answer', sa.Column('view_times', sa.Integer(), server_default='0', nullable=False))
22 | op.add_column('question', sa.Column('view_times', sa.Integer(), server_default='0', nullable=False))
23 | op.add_column('user', sa.Column('profile_view_times', sa.Integer(), server_default='0', nullable=False))
24 | # ### end Alembic commands ###
25 |
26 |
27 | def downgrade():
28 | # ### commands auto generated by Alembic - please adjust! ###
29 | op.drop_column('user', 'profile_view_times')
30 | op.drop_column('question', 'view_times')
31 | op.drop_column('answer', 'view_times')
32 | # ### end Alembic commands ###
33 |
--------------------------------------------------------------------------------
/alembic/versions/ffcc3b882cf6_add_verified_telegram_user_id.py:
--------------------------------------------------------------------------------
1 | """Add verified_telegram_user_id
2 |
3 | Revision ID: ffcc3b882cf6
4 | Revises: 1e95c68901bb
5 | Create Date: 2021-06-05 00:00:56.871712
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'ffcc3b882cf6'
14 | down_revision = '1e95c68901bb'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('user', sa.Column('verified_telegram_user_id', sa.String(), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('user', 'verified_telegram_user_id')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "cron": [
3 | {
4 | "command": "python scripts/schedule-runner.py cache_new_activity_to_feeds",
5 | "schedule": "*/30 * * * *"
6 | },
7 | {
8 | "command": "python scripts/schedule-runner.py daily",
9 | "schedule": "@daily"
10 | },
11 | {
12 | "command": "python scripts/schedule-runner.py refresh_search_index",
13 | "schedule": "@weekly"
14 | },
15 | {
16 | "command": "python scripts/schedule-runner.py run_deliver_notification_task",
17 | "schedule": "@weekly"
18 | }
19 | ],
20 | "formation": {
21 | "web": {
22 | "quantity": 1
23 | },
24 | "worker": {
25 | "quantity": 1
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/chafan_core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/__init__.py
--------------------------------------------------------------------------------
/chafan_core/app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/app/__init__.py
--------------------------------------------------------------------------------
/chafan_core/app/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/app/api/__init__.py
--------------------------------------------------------------------------------
/chafan_core/app/api/api_v1/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/app/api/api_v1/__init__.py
--------------------------------------------------------------------------------
/chafan_core/app/api/api_v1/endpoints/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/app/api/api_v1/endpoints/__init__.py
--------------------------------------------------------------------------------
/chafan_core/app/api/api_v1/endpoints/audit_logs.py:
--------------------------------------------------------------------------------
1 | from typing import Any, List
2 |
3 | from fastapi import APIRouter, Depends
4 |
5 | from chafan_core.app import crud, schemas
6 | from chafan_core.app.api import deps
7 | from chafan_core.app.cached_layer import CachedLayer
8 |
9 | router = APIRouter()
10 |
11 |
12 | @router.get("/", response_model=List[schemas.AuditLog])
13 | def get_audit_logs(
14 | *,
15 | cached_layer: CachedLayer = Depends(deps.get_cached_layer_logged_in),
16 | ) -> Any:
17 | return [
18 | cached_layer.materializer.audit_log_schema_from_orm(audit_log)
19 | for audit_log in crud.audit_log.get_audit_logs(
20 | cached_layer.get_db(),
21 | user_id=cached_layer.unwrapped_principal_id(),
22 | )
23 | ]
24 |
--------------------------------------------------------------------------------
/chafan_core/app/api/api_v1/endpoints/coin_deposits.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | from fastapi import APIRouter, Depends
4 | from sqlalchemy.orm import Session
5 |
6 | from chafan_core.app import crud, schemas
7 | from chafan_core.app.api import deps
8 | from chafan_core.utils.base import HTTPException_
9 |
10 | router = APIRouter()
11 |
12 |
13 | @router.get("/{id}", response_model=schemas.CoinDeposit, include_in_schema=False)
14 | def get_deposit(
15 | *,
16 | db: Session = Depends(deps.get_db),
17 | id: int,
18 | current_user_id: int = Depends(deps.get_current_user_id),
19 | ) -> Any:
20 | deposit = crud.coin_deposit.get(db, id=id)
21 | if deposit is None:
22 | raise HTTPException_(
23 | status_code=400,
24 | detail="The deposit doesn't exist in the system.",
25 | )
26 | if current_user_id not in (deposit.authorizer_id, deposit.payee_id):
27 | raise HTTPException_(
28 | status_code=400,
29 | detail="Unauthorized.",
30 | )
31 | return deposit
32 |
--------------------------------------------------------------------------------
/chafan_core/app/api/api_v1/endpoints/drafts.py:
--------------------------------------------------------------------------------
1 | from typing import Any, List
2 |
3 | from fastapi import APIRouter, Depends
4 |
5 | from chafan_core.app import schemas
6 | from chafan_core.app.api import deps
7 | from chafan_core.app.cached_layer import CachedLayer
8 | from chafan_core.utils.base import filter_not_none
9 |
10 | router = APIRouter()
11 |
12 |
13 | @router.get("/answers/", response_model=List[schemas.AnswerPreview])
14 | def get_draft_answers(
15 | cached_layer: CachedLayer = Depends(deps.get_cached_layer_logged_in),
16 | ) -> Any:
17 | current_user = cached_layer.get_current_active_user()
18 | return filter_not_none(
19 | [
20 | cached_layer.materializer.preview_of_answer(answer)
21 | for answer in current_user.answers
22 | if not answer.is_published and answer.body_draft
23 | ]
24 | )
25 |
26 |
27 | @router.get("/articles/", response_model=List[schemas.ArticlePreview])
28 | def get_draft_articles(
29 | cached_layer: CachedLayer = Depends(deps.get_cached_layer_logged_in),
30 | ) -> Any:
31 | current_user = cached_layer.get_current_active_user()
32 | return filter_not_none(
33 | [
34 | cached_layer.materializer.preview_of_article(article)
35 | for article in current_user.articles
36 | if not article.is_published or article.body_draft
37 | ]
38 | )
39 |
--------------------------------------------------------------------------------
/chafan_core/app/api/api_v1/endpoints/sitemaps.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | from fastapi import APIRouter, Depends
4 |
5 | from chafan_core.app.api import deps
6 | from chafan_core.app.cached_layer import CachedLayer
7 | from chafan_core.app.schemas.site import SiteMaps
8 |
9 | router = APIRouter()
10 |
11 |
12 | @router.get("/", response_model=SiteMaps)
13 | def read_sitemaps(
14 | cached_layer: CachedLayer = Depends(deps.get_cached_layer),
15 | ) -> Any:
16 | """
17 | Retrieve site map.
18 | """
19 | return cached_layer.get_site_maps()
20 |
--------------------------------------------------------------------------------
/chafan_core/app/api/health.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | from fastapi import APIRouter
4 |
5 | from chafan_core.app import schemas
6 |
7 | router = APIRouter()
8 |
9 |
10 | @router.get("/", response_model=schemas.HealthResponse)
11 | def get_health() -> Any:
12 | return schemas.HealthResponse()
13 |
--------------------------------------------------------------------------------
/chafan_core/app/aws.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 |
3 | import boto3
4 |
5 | from chafan_core.app.config import settings
6 |
7 |
8 | def get_boto3_client() -> boto3.Session:
9 | assert settings.AWS_ACCESS_KEY_ID is not None
10 | assert settings.AWS_SECRET_ACCESS_KEY is not None
11 | assert settings.AWS_REGION is not None
12 | return boto3.Session(
13 | aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
14 | aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
15 | region_name=settings.AWS_REGION,
16 | )
17 |
18 |
19 | def get_s3_client() -> Any:
20 | return get_boto3_client().client("s3")
21 |
22 |
23 | def get_ses_client() -> Any:
24 | return get_boto3_client().client("ses")
25 |
26 |
27 | def get_sns_client() -> Any:
28 | return get_boto3_client().client("sns")
29 |
--------------------------------------------------------------------------------
/chafan_core/app/aws_ses.py:
--------------------------------------------------------------------------------
1 | import html2text
2 | import sentry_sdk
3 | from botocore.exceptions import ClientError
4 |
5 | from chafan_core.app.aws import get_ses_client
6 | from chafan_core.app.common import is_dev
7 | from chafan_core.app.config import settings
8 |
9 |
10 | def send_email_ses(email_to: str, body_html: str, subject: str) -> None:
11 | if is_dev():
12 | return
13 | try:
14 | ses = get_ses_client()
15 | ses.send_email(
16 | Destination={
17 | "ToAddresses": [
18 | email_to,
19 | ],
20 | },
21 | Message={
22 | "Body": {
23 | "Html": {
24 | "Charset": "utf-8",
25 | "Data": body_html,
26 | },
27 | "Text": {
28 | "Charset": "utf-8",
29 | "Data": html2text.html2text(body_html),
30 | },
31 | },
32 | "Subject": {
33 | "Charset": "utf-8",
34 | "Data": subject,
35 | },
36 | },
37 | Source=settings.EMAILS_FROM_EMAIL,
38 | )
39 | except ClientError as e:
40 | sentry_sdk.capture_exception(e)
41 |
--------------------------------------------------------------------------------
/chafan_core/app/aws_sns.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 |
4 | import sentry_sdk
5 | from botocore.exceptions import ClientError
6 |
7 | from chafan_core.app.aws import get_sns_client
8 | from chafan_core.app.common import is_dev
9 |
10 |
11 | def send_sms(phone_number: str, msg: str) -> None:
12 | if not is_dev():
13 | try:
14 | ses = get_sns_client()
15 | ses.publish(
16 | PhoneNumber=phone_number,
17 | Message=msg,
18 | )
19 | except ClientError as e:
20 | sentry_sdk.capture_exception(e)
21 | else:
22 | inbox_dir = f"/tmp/chafan/sms-inbox/{phone_number}"
23 | os.makedirs(inbox_dir, exist_ok=True)
24 | with open(inbox_dir + f"/{int(time.time())}.txt", "w") as f:
25 | f.write(msg)
26 |
--------------------------------------------------------------------------------
/chafan_core/app/crud/__init__.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 |
3 | from .crud_activity import activity
4 | from .crud_answer import answer
5 | from .crud_answer_suggest_edit import answer_suggest_edit
6 | from .crud_application import application
7 | from .crud_article import article
8 | from .crud_article_column import article_column
9 | from .crud_audit_log import audit_log
10 | from .crud_channel import channel
11 | from .crud_coin_deposit import coin_deposit
12 | from .crud_coin_payment import coin_payment
13 | from .crud_comment import comment
14 | from .crud_feedback import feedback
15 | from .crud_form import form
16 | from .crud_form_response import form_response
17 | from .crud_invitation import invitation
18 | from .crud_invitation_link import invitation_link
19 | from .crud_message import message
20 | from .crud_notification import notification
21 | from .crud_profile import profile
22 | from .crud_question import question
23 | from .crud_report import report
24 | from .crud_reward import reward
25 | from .crud_site import site
26 | from .crud_submission import submission
27 | from .crud_submission_suggestion import submission_suggestion
28 | from .crud_topic import topic
29 | from .crud_user import user
30 | from .crud_webhook import webhook
31 |
--------------------------------------------------------------------------------
/chafan_core/app/crud/crud_article_column.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from fastapi.encoders import jsonable_encoder
4 | from sqlalchemy.orm import Session
5 |
6 | from chafan_core.app.crud.base import CRUDBase
7 | from chafan_core.app.models.article_column import ArticleColumn
8 | from chafan_core.app.schemas.article_column import (
9 | ArticleColumnCreate,
10 | ArticleColumnUpdate,
11 | )
12 |
13 |
14 | class CRUDArticleColumn(
15 | CRUDBase[ArticleColumn, ArticleColumnCreate, ArticleColumnUpdate]
16 | ):
17 | def create_with_owner(
18 | self, db: Session, *, obj_in: ArticleColumnCreate, owner_id: int
19 | ) -> ArticleColumn:
20 | obj_in_data = jsonable_encoder(obj_in)
21 | utc_now = datetime.datetime.now(tz=datetime.timezone.utc)
22 | db_obj = self.model(
23 | **obj_in_data,
24 | owner_id=owner_id,
25 | created_at=utc_now,
26 | uuid=self.get_unique_uuid(db)
27 | )
28 | db.add(db_obj)
29 | db.commit()
30 | return db_obj
31 |
32 |
33 | article_column = CRUDArticleColumn(ArticleColumn)
34 |
--------------------------------------------------------------------------------
/chafan_core/app/crud/crud_coin_deposit.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Optional
3 |
4 | from fastapi.encoders import jsonable_encoder
5 | from sqlalchemy.orm import Session
6 |
7 | from chafan_core.app import models
8 | from chafan_core.app.crud.base import CRUDBase
9 | from chafan_core.app.models.coin_deposit import CoinDeposit
10 | from chafan_core.app.schemas.coin_deposit import CoinDepositCreate, CoinDepositUpdate
11 |
12 |
13 | class CRUDCoinDeposit(CRUDBase[CoinDeposit, CoinDepositCreate, CoinDepositUpdate]):
14 | def make_deposit(
15 | self,
16 | db: Session,
17 | *,
18 | obj_in: CoinDepositCreate,
19 | authorizer_id: int,
20 | payee: models.User
21 | ) -> CoinDeposit:
22 | payee.remaining_coins += obj_in.amount
23 | deposit = CoinDeposit(
24 | **jsonable_encoder(obj_in),
25 | created_at=datetime.datetime.now(tz=datetime.timezone.utc),
26 | authorizer_id=authorizer_id,
27 | )
28 | db.add(deposit)
29 | db.commit()
30 | db.refresh(deposit)
31 | return deposit
32 |
33 | def get_with_ref_id(self, db: Session, *, ref_id: str) -> Optional[CoinDeposit]:
34 | return db.query(CoinDeposit).filter_by(ref_id=ref_id).first()
35 |
36 |
37 | coin_deposit = CRUDCoinDeposit(CoinDeposit)
38 |
--------------------------------------------------------------------------------
/chafan_core/app/crud/crud_feedback.py:
--------------------------------------------------------------------------------
1 | from chafan_core.app.crud.base import CRUDBase
2 | from chafan_core.app.models.feedback import Feedback
3 | from chafan_core.app.schemas.feedback import FeedbackCreate, FeedbackUpdate
4 |
5 |
6 | class CRUDFeedback(CRUDBase[Feedback, FeedbackCreate, FeedbackUpdate]):
7 | pass
8 |
9 |
10 | feedback = CRUDFeedback(Feedback)
11 |
--------------------------------------------------------------------------------
/chafan_core/app/crud/crud_form.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from fastapi.encoders import jsonable_encoder
4 | from sqlalchemy.orm import Session
5 |
6 | from chafan_core.app import models
7 | from chafan_core.app.crud.base import CRUDBase
8 | from chafan_core.app.models.form import Form
9 | from chafan_core.app.schemas.form import FormCreate, FormUpdate
10 |
11 |
12 | class CRUDForm(CRUDBase[Form, FormCreate, FormUpdate]):
13 | def create_with_author(
14 | self,
15 | db: Session,
16 | *,
17 | obj_in: FormCreate,
18 | author: models.User,
19 | ) -> Form:
20 | obj_in_data = jsonable_encoder(obj_in)
21 | utc_now = datetime.datetime.now(tz=datetime.timezone.utc)
22 | db_obj = self.model(
23 | **obj_in_data,
24 | uuid=self.get_unique_uuid(db),
25 | author_id=author.id,
26 | updated_at=utc_now,
27 | created_at=utc_now,
28 | )
29 | db.add(db_obj)
30 | db.commit()
31 | return db_obj
32 |
33 |
34 | form = CRUDForm(Form)
35 |
--------------------------------------------------------------------------------
/chafan_core/app/crud/crud_form_response.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from fastapi.encoders import jsonable_encoder
4 | from sqlalchemy.orm import Session
5 |
6 | from chafan_core.app import models
7 | from chafan_core.app.crud.base import CRUDBase
8 | from chafan_core.app.models.form_response import FormResponse
9 | from chafan_core.app.schemas.form_response import FormResponseCreate, FormResponseUpdate
10 |
11 |
12 | class CRUDFormResponse(CRUDBase[FormResponse, FormResponseCreate, FormResponseUpdate]):
13 | def create_with_author(
14 | self,
15 | db: Session,
16 | *,
17 | obj_in: FormResponseCreate,
18 | response_author_id: int,
19 | form: models.Form,
20 | ) -> FormResponse:
21 | obj_in_data = jsonable_encoder(obj_in)
22 | del obj_in_data["form_uuid"]
23 | utc_now = datetime.datetime.now(tz=datetime.timezone.utc)
24 | db_obj = self.model(
25 | **obj_in_data,
26 | response_author_id=response_author_id,
27 | created_at=utc_now,
28 | form_id=form.id,
29 | )
30 | db.add(db_obj)
31 | db.commit()
32 | return db_obj
33 |
34 |
35 | form_response = CRUDFormResponse(FormResponse)
36 |
--------------------------------------------------------------------------------
/chafan_core/app/crud/crud_invitation_link.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Optional
3 |
4 | from sqlalchemy.orm import Session
5 |
6 | from chafan_core.app import models
7 | from chafan_core.app.crud.base import CRUDBase
8 | from chafan_core.app.models import InvitationLink
9 | from chafan_core.app.schemas.invitation_link import (
10 | InvitationLinkCreate,
11 | InvitationLinkUpdate,
12 | )
13 |
14 |
15 | class CRUDInvitationLink(
16 | CRUDBase[InvitationLink, InvitationLinkCreate, InvitationLinkUpdate]
17 | ):
18 | def create_invitation(
19 | self,
20 | db: Session,
21 | *,
22 | invited_to_site_id: Optional[int],
23 | inviter: models.User,
24 | ) -> InvitationLink:
25 | utc_now = datetime.datetime.now(tz=datetime.timezone.utc)
26 | db_obj = InvitationLink(
27 | uuid=self.get_unique_uuid(db),
28 | created_at=utc_now,
29 | expired_at=utc_now + datetime.timedelta(days=7),
30 | inviter_id=inviter.id,
31 | invited_to_site_id=invited_to_site_id,
32 | remaining_quota=100,
33 | )
34 | db.add(db_obj)
35 | db.commit()
36 | db.refresh(db_obj)
37 | return db_obj
38 |
39 |
40 | invitation_link = CRUDInvitationLink(InvitationLink)
41 |
--------------------------------------------------------------------------------
/chafan_core/app/crud/crud_topic.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from fastapi.encoders import jsonable_encoder
4 | from sqlalchemy.orm import Session
5 |
6 | from chafan_core.app.crud.base import CRUDBase
7 | from chafan_core.app.models.topic import Topic
8 | from chafan_core.app.schemas.topic import TopicCreate, TopicUpdate
9 | from chafan_core.utils.validators import StrippedNonEmptyStr
10 |
11 |
12 | class CRUDTopic(CRUDBase[Topic, TopicCreate, TopicUpdate]):
13 | def get_by_name(self, db: Session, *, name: str) -> Optional[Topic]:
14 | return db.query(Topic).filter(Topic.name == name).first()
15 |
16 | def create(self, db: Session, *, obj_in: TopicCreate) -> Topic:
17 | obj_in_data = jsonable_encoder(obj_in)
18 | db_obj = self.model(**obj_in_data, uuid=self.get_unique_uuid(db))
19 | db.add(db_obj)
20 | db.commit()
21 | db.refresh(db_obj)
22 | return db_obj
23 |
24 | def get_category_topics(self, db: Session) -> List[Topic]:
25 | return db.query(Topic).filter_by(is_category=True).all()
26 |
27 | def get_or_create(self, db: Session, *, name: StrippedNonEmptyStr) -> Topic:
28 | topic = self.get_by_name(db, name=name)
29 | if topic is not None:
30 | return topic
31 | return self.create(db, obj_in=TopicCreate(name=name))
32 |
33 |
34 | topic = CRUDTopic(Topic)
35 |
--------------------------------------------------------------------------------
/chafan_core/app/crud/crud_webhook.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from fastapi.encoders import jsonable_encoder
4 | from sqlalchemy.orm.session import Session
5 |
6 | from chafan_core.app.crud.base import CRUDBase
7 | from chafan_core.app.models.webhook import Webhook
8 | from chafan_core.app.schemas.webhook import WebhookCreate, WebhookUpdate
9 |
10 |
11 | class CRUDWebhook(CRUDBase[Webhook, WebhookCreate, WebhookUpdate]):
12 | def create_with_site(
13 | self, db: Session, *, obj_in: WebhookCreate, site_id: int
14 | ) -> Webhook:
15 | obj_in_data = jsonable_encoder(obj_in)
16 | del obj_in_data["site_uuid"]
17 | obj_in_data["site_id"] = site_id
18 | obj_in_data["updated_at"] = datetime.datetime.now(tz=datetime.timezone.utc)
19 | db_obj = self.model(**obj_in_data)
20 | db.add(db_obj)
21 | db.commit()
22 | db.refresh(db_obj)
23 | return db_obj
24 |
25 |
26 | webhook = CRUDWebhook(Webhook)
27 |
--------------------------------------------------------------------------------
/chafan_core/app/data_broker.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | import redis
4 | from sqlalchemy.orm import Session
5 |
6 | from chafan_core.app.common import get_redis_cli
7 | from chafan_core.db.session import ReadSessionLocal, SessionLocal
8 |
9 |
10 | # Data broker operates after authentication logic and business logic
11 | # Its main job is to manage cache and data backend sessions.
12 | class DataBroker(object):
13 | def __init__(self, use_read_replica: bool = False) -> None:
14 | self.redis: Optional[redis.Redis] = None
15 | self.db: Optional[Session] = None
16 | self.use_read_replica = use_read_replica
17 |
18 | def get_redis(self) -> redis.Redis:
19 | if self.redis is None:
20 | self.redis = get_redis_cli()
21 | return self.redis
22 |
23 | def get_db(self) -> Session:
24 | if self.db is None:
25 | if self.use_read_replica:
26 | self.db = ReadSessionLocal()
27 | else:
28 | self.db = SessionLocal()
29 | return self.db
30 |
31 | def close(self) -> None:
32 | if self.db:
33 | if not self.use_read_replica:
34 | self.db.commit()
35 | self.db.close()
36 |
--------------------------------------------------------------------------------
/chafan_core/app/email-templates/src/feedback_status_update.mjml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ project_name }} - 您的反馈状态有更新
7 | 反馈的最新状态为「{{ new_status }}」,原文:
8 |
9 | {{ desc }}
10 |
11 | 联系 contact@cha.fan 以获取更多支持
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chafan_core/app/email-templates/src/notifications.mjml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ project_name }} 未读通知
6 |
7 | {% for notif in notifications %}
8 |
9 | {{ notif.body | safe }}
10 | ({{ notif.created_at }})
11 |
12 | {% endfor %}
13 | 点击退订未读通知邮件
14 | {{ unsubscribe_link }}
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/chafan_core/app/email-templates/src/reset_password.mjml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ project_name }}密码找回
7 | 如果是你本人申请找回用户 {{ username }} 的密码,请点击以下按钮重置密码:
8 | 重置密码
9 | 如果按钮不工作,也可以直接打开以下链接:
10 | {{ link }}
11 |
12 | 重置按钮在 {{ valid_hours }} 小时内有效。
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/chafan_core/app/email-templates/src/verification_code.mjml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ project_name }}验证码
7 | {{ code }}
8 | 验证码将在 {{ valid_hours }} 小时后失效
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/chafan_core/app/endpoint_utils.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from sqlalchemy.orm import Session
4 |
5 | from chafan_core.app import crud, models
6 | from chafan_core.app.common import get_redis_cli
7 | from chafan_core.utils.base import HTTPException_
8 |
9 |
10 | def get_site(db: Session, site_uuid: str) -> models.Site:
11 | site = crud.site.get_by_uuid(db, uuid=site_uuid)
12 | if site is None:
13 | raise HTTPException_(
14 | status_code=400,
15 | detail="The site doesn't exists in the system.",
16 | )
17 | return site
18 |
19 |
20 | def check_writing_session(uuid: str) -> None:
21 | redis = get_redis_cli()
22 | key = f"chafan:writing-session:{uuid}"
23 | if redis.get(key) is not None:
24 | raise HTTPException_(
25 | status_code=400,
26 | detail="Frondend bug: repeated posting in one writing session.",
27 | )
28 | redis.set(
29 | key,
30 | "on",
31 | ex=datetime.timedelta(minutes=30),
32 | )
33 |
--------------------------------------------------------------------------------
/chafan_core/app/limiter.py:
--------------------------------------------------------------------------------
1 | from slowapi import Limiter
2 |
3 | from chafan_core.app.common import client_ip, enable_rate_limit, get_redis_cli
4 | from chafan_core.app.config import settings
5 |
6 | limiter = Limiter(
7 | key_func=client_ip,
8 | headers_enabled=True,
9 | default_limits=["150/minute"],
10 | storage_uri=settings.REDIS_URL,
11 | key_prefix="chafan:slowapi:",
12 | enabled=enable_rate_limit(),
13 | )
14 |
15 |
16 | def clear_limits_for_ip(ip: str) -> None:
17 | redis = get_redis_cli()
18 | for key in redis.scan_iter(f"LIMITER/chafan:slowapi:/{ip}/*"):
19 | limiter._storage.clear(key)
20 |
--------------------------------------------------------------------------------
/chafan_core/app/metrics/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/app/metrics/__init__.py
--------------------------------------------------------------------------------
/chafan_core/app/model_utils.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from chafan_core.app import models
4 |
5 |
6 | def is_live_answer(answer: models.Answer) -> bool:
7 | return (
8 | (not answer.is_deleted)
9 | and (not answer.is_hidden_by_moderator)
10 | and answer.is_published
11 | )
12 |
13 |
14 | def get_live_answers_of_question(question: models.Question) -> List[models.Answer]:
15 | return [a for a in question.answers if is_live_answer(a)]
16 |
17 |
18 | def is_live_article(article: models.Article) -> bool:
19 | return (not article.is_deleted) and article.is_published
20 |
--------------------------------------------------------------------------------
/chafan_core/app/models/activity.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Optional
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 |
6 | from chafan_core.db.base_class import Base
7 |
8 | if TYPE_CHECKING:
9 | from . import * # noqa: F401, F403
10 |
11 |
12 | class Activity(Base):
13 | id = Column(Integer, primary_key=True, index=True)
14 | site_id = Column(Integer, ForeignKey("site.id"), index=True)
15 | site: Optional["Site"] = relationship("Site", foreign_keys=[site_id]) # type: ignore
16 | created_at = Column(DateTime(timezone=True), nullable=False, index=True)
17 | event_json = Column(String, nullable=False)
18 |
--------------------------------------------------------------------------------
/chafan_core/app/models/application.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer
4 | from sqlalchemy.orm import relationship
5 |
6 | from chafan_core.db.base_class import Base
7 |
8 | if TYPE_CHECKING:
9 | from . import * # noqa: F401, F403
10 |
11 |
12 | class Application(Base):
13 | id = Column(Integer, primary_key=True, index=True)
14 | created_at = Column(DateTime(timezone=True), nullable=False)
15 | applicant_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
16 | applicant: "User" = relationship("User", back_populates="applications") # type: ignore
17 |
18 | applied_site_id = Column(Integer, ForeignKey("site.id"), nullable=False, index=True)
19 | applied_site: "Site" = relationship("Site", back_populates="applications") # type: ignore
20 |
21 | pending = Column(Boolean, default=True, nullable=False)
22 |
--------------------------------------------------------------------------------
/chafan_core/app/models/archive.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 |
6 | from chafan_core.db.base_class import Base
7 | from chafan_core.utils.constants import editor_T
8 |
9 | if TYPE_CHECKING:
10 | from . import * # noqa: F401, F403
11 |
12 |
13 | class Archive(Base):
14 | id = Column(Integer, primary_key=True, index=True)
15 | body = Column(String, nullable=False)
16 | editor: editor_T = Column(String, nullable=False, server_default="wysiwyg") # type: ignore
17 |
18 | created_at = Column(DateTime(timezone=True), nullable=False)
19 | answer_id = Column(Integer, ForeignKey("answer.id"), nullable=False, index=True)
20 | answer = relationship("Answer", back_populates="archives")
21 |
--------------------------------------------------------------------------------
/chafan_core/app/models/article_archive.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 |
6 | from chafan_core.db.base_class import Base
7 | from chafan_core.utils.constants import editor_T
8 |
9 | if TYPE_CHECKING:
10 | from . import * # noqa: F401, F403
11 |
12 |
13 | class ArticleArchive(Base):
14 | id = Column(Integer, primary_key=True, index=True)
15 | title = Column(String, nullable=False)
16 | body = Column(String, nullable=False)
17 | editor: editor_T = Column(String, nullable=False, server_default="wysiwyg") # type: ignore
18 |
19 | created_at = Column(DateTime(timezone=True), nullable=False)
20 | article_id = Column(Integer, ForeignKey("article.id"), nullable=False, index=True)
21 | article = relationship("Article", back_populates="archives")
22 |
--------------------------------------------------------------------------------
/chafan_core/app/models/article_column.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, List
2 |
3 | from sqlalchemy import CHAR, Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import JSON
6 |
7 | from chafan_core.db.base_class import Base
8 | from chafan_core.utils.base import UUID_LENGTH
9 |
10 | if TYPE_CHECKING:
11 | from . import * # noqa: F401, F403
12 |
13 |
14 | class ArticleColumn(Base):
15 | id = Column(Integer, primary_key=True, index=True)
16 | uuid = Column(CHAR(length=UUID_LENGTH), index=True, unique=True, nullable=False)
17 | owner_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
18 | owner = relationship(
19 | "User", back_populates="article_columns", foreign_keys=[owner_id]
20 | )
21 | name = Column(String, nullable=False)
22 | description = Column(String)
23 | created_at = Column(DateTime(timezone=True), nullable=False)
24 |
25 | articles: List["Article"] = relationship( # type: ignore
26 | "Article", back_populates="article_column", order_by="Article.created_at.desc()"
27 | )
28 |
29 | keywords = Column(JSON)
30 |
--------------------------------------------------------------------------------
/chafan_core/app/models/audit_log.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import CHAR, Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import JSON
6 |
7 | from chafan_core.db.base_class import Base
8 | from chafan_core.utils.base import UUID_LENGTH
9 |
10 | if TYPE_CHECKING:
11 | from . import * # noqa: F401, F403
12 |
13 |
14 | class AuditLog(Base):
15 | id = Column(Integer, primary_key=True, index=True)
16 | uuid = Column(CHAR(length=UUID_LENGTH), index=True, unique=True, nullable=False)
17 | user_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
18 | user = relationship("User", back_populates="audit_logs")
19 | ipaddr = Column(String, index=True, nullable=False)
20 |
21 | api = Column(String, nullable=False)
22 | created_at = Column(DateTime(timezone=True), nullable=False)
23 | request_info = Column(JSON)
24 |
--------------------------------------------------------------------------------
/chafan_core/app/models/coin_deposit.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 |
6 | from chafan_core.db.base_class import Base
7 |
8 | if TYPE_CHECKING:
9 | from . import * # noqa: F401, F403
10 |
11 |
12 | class CoinDeposit(Base):
13 | id = Column(Integer, primary_key=True, index=True)
14 | created_at = Column(DateTime(timezone=True), nullable=False)
15 | amount = Column(Integer, nullable=False)
16 | ref_id = Column(String, nullable=False, unique=True)
17 | authorizer_id = Column("authorizer_id", Integer, ForeignKey("user.id"))
18 | authorizer = relationship(
19 | "User", back_populates="authorized_deposits", foreign_keys=[authorizer_id]
20 | )
21 | payee_id = Column("payee_id", Integer, ForeignKey("user.id"))
22 | payee = relationship(
23 | "User", back_populates="in_coin_deposits", foreign_keys=[payee_id]
24 | )
25 | comment = Column(String)
26 |
--------------------------------------------------------------------------------
/chafan_core/app/models/coin_payment.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.schema import UniqueConstraint
6 |
7 | from chafan_core.db.base_class import Base
8 |
9 | if TYPE_CHECKING:
10 | from . import * # noqa: F401, F403
11 |
12 |
13 | class CoinPayment(Base):
14 | id = Column(Integer, primary_key=True, index=True)
15 | created_at = Column(DateTime(timezone=True), nullable=False)
16 | amount = Column(Integer, nullable=False)
17 | event_json = Column(String) # Change to nullable=False after deprecation of ref_id
18 | payer_id = Column("payer_id", Integer, ForeignKey("user.id"), nullable=False)
19 | payer = relationship(
20 | "User", back_populates="out_coin_payments", foreign_keys=[payer_id]
21 | )
22 | payee_id = Column("payee_id", Integer, ForeignKey("user.id"), nullable=False)
23 | payee = relationship(
24 | "User", back_populates="in_coin_payments", foreign_keys=[payee_id]
25 | )
26 | comment = Column(String)
27 |
28 | __table_args__ = (UniqueConstraint("event_json", "payee_id", "payer_id"),)
29 |
--------------------------------------------------------------------------------
/chafan_core/app/models/feed.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Optional
2 |
3 | from sqlalchemy import CHAR, Column, ForeignKey, Integer
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.schema import UniqueConstraint
6 |
7 | from chafan_core.db.base_class import Base
8 | from chafan_core.utils.base import UUID_LENGTH
9 |
10 | if TYPE_CHECKING:
11 | from . import * # noqa: F401, F403
12 |
13 |
14 | class Feed(Base):
15 | id = Column(Integer, primary_key=True, index=True)
16 | receiver_id = Column(Integer, ForeignKey("user.id"), index=True, nullable=False)
17 | receiver = relationship("User", foreign_keys=[receiver_id])
18 | activity_id = Column(Integer, ForeignKey("activity.id"), index=True, nullable=False)
19 | activity: "Activity" = relationship("Activity", foreign_keys=[activity_id]) # type: ignore
20 |
21 | subject_user_uuid = Column(
22 | CHAR(length=UUID_LENGTH), ForeignKey("user.uuid"), nullable=True
23 | )
24 | subject_user: Optional["User"] = relationship("User", foreign_keys=[subject_user_uuid]) # type: ignore
25 |
26 | __table_args__ = (UniqueConstraint("activity_id", "receiver_id"),)
27 |
--------------------------------------------------------------------------------
/chafan_core/app/models/feedback.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Optional
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import LargeBinary
6 |
7 | from chafan_core.db.base_class import Base
8 | from chafan_core.utils.constants import feedback_status_T
9 |
10 | if TYPE_CHECKING:
11 | from . import * # noqa: F401, F403
12 |
13 |
14 | class Feedback(Base):
15 | id = Column(Integer, primary_key=True, index=True)
16 | user_id = Column(Integer, ForeignKey("user.id"))
17 | user: Optional["User"] = relationship("User", back_populates="feedbacks", foreign_keys=[user_id]) # type: ignore
18 | user_email = Column(String) # User-provided email if not logged in
19 | created_at = Column(DateTime(timezone=True), nullable=False)
20 | status: feedback_status_T = Column(String, server_default="sent", nullable=False) # type: ignore
21 |
22 | location_url = Column(String)
23 |
24 | description = Column(String, nullable=False)
25 | screenshot_blob = Column(LargeBinary)
26 |
--------------------------------------------------------------------------------
/chafan_core/app/models/form.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, List
2 |
3 | from sqlalchemy import CHAR, Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import JSON
6 |
7 | from chafan_core.db.base_class import Base
8 | from chafan_core.utils.base import UUID_LENGTH
9 |
10 | if TYPE_CHECKING:
11 | from . import * # noqa: F401, F403
12 |
13 |
14 | class Form(Base):
15 | id = Column(Integer, primary_key=True, index=True)
16 | uuid = Column(CHAR(length=UUID_LENGTH), index=True, unique=True, nullable=False)
17 | author_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
18 | author = relationship("User", back_populates="forms")
19 |
20 | title = Column(String, nullable=False)
21 | created_at = Column(DateTime(timezone=True), nullable=False)
22 | updated_at = Column(DateTime(timezone=True), nullable=False)
23 |
24 | form_fields = Column(JSON, nullable=False)
25 |
26 | responses: List["FormResponse"] = relationship( # type: ignore
27 | "FormResponse", back_populates="form", order_by="FormResponse.created_at.desc()"
28 | )
29 |
--------------------------------------------------------------------------------
/chafan_core/app/models/form_response.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import JSON
6 |
7 | from chafan_core.db.base_class import Base
8 |
9 | if TYPE_CHECKING:
10 | from . import * # noqa: F401, F403
11 |
12 |
13 | class FormResponse(Base):
14 | id = Column(Integer, primary_key=True, index=True)
15 | response_author_id = Column(
16 | Integer, ForeignKey("user.id"), nullable=False, index=True
17 | )
18 | response_author = relationship(
19 | "User", back_populates="form_responses", foreign_keys=[response_author_id]
20 | )
21 | form_id = Column(Integer, ForeignKey("form.id"), nullable=False, index=True)
22 | form: "Form" = relationship("Form", back_populates="responses") # type: ignore
23 |
24 | created_at = Column(DateTime(timezone=True), nullable=False)
25 |
26 | response_fields = Column(JSON, nullable=False)
27 |
--------------------------------------------------------------------------------
/chafan_core/app/models/invitation.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 |
6 | from chafan_core.db.base_class import Base
7 |
8 | if TYPE_CHECKING:
9 | from . import * # noqa: F401, F403
10 |
11 |
12 | class Invitation(Base):
13 | id = Column(Integer, primary_key=True)
14 | created_at = Column(DateTime(timezone=True), nullable=False)
15 | inviter_id = Column(Integer, ForeignKey("user.id"), nullable=False)
16 | inviter = relationship("User", foreign_keys=[inviter_id])
17 |
18 | invited_user_id = Column(Integer, ForeignKey("user.id"), nullable=True)
19 | invited_user = relationship(
20 | "User",
21 | foreign_keys=[invited_user_id],
22 | )
23 |
24 | invited_to_site_id = Column(Integer, ForeignKey("site.id"), nullable=True)
25 | invited_to_site = relationship("Site", foreign_keys=[invited_to_site_id])
26 | is_sent = Column(Boolean, default=False, server_default="false", nullable=False)
27 |
28 | invitation_link = Column(String)
29 |
--------------------------------------------------------------------------------
/chafan_core/app/models/invitation_link.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import CHAR, Column, DateTime, ForeignKey, Integer
4 | from sqlalchemy.orm import relationship
5 |
6 | from chafan_core.db.base_class import Base
7 | from chafan_core.utils.base import UUID_LENGTH
8 |
9 | if TYPE_CHECKING:
10 | from . import * # noqa: F401, F403
11 |
12 |
13 | class InvitationLink(Base):
14 | id = Column(Integer, primary_key=True)
15 | uuid = Column(CHAR(length=UUID_LENGTH), index=True, unique=True, nullable=False)
16 | created_at = Column(DateTime(timezone=True), nullable=False)
17 | expired_at = Column(DateTime(timezone=True), nullable=False)
18 |
19 | inviter_id = Column(Integer, ForeignKey("user.id"), nullable=False)
20 | inviter = relationship("User", foreign_keys=[inviter_id])
21 |
22 | invited_to_site_id = Column(Integer, ForeignKey("site.id"), nullable=True)
23 | invited_to_site = relationship("Site", foreign_keys=[invited_to_site_id])
24 |
25 | remaining_quota = Column(Integer, nullable=False)
26 |
--------------------------------------------------------------------------------
/chafan_core/app/models/message.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 |
6 | from chafan_core.db.base_class import Base
7 |
8 | if TYPE_CHECKING:
9 | from . import * # noqa: F401, F403
10 |
11 |
12 | class Message(Base):
13 | id = Column(Integer, primary_key=True, index=True)
14 | author_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
15 | author = relationship("User", back_populates="messages")
16 | channel_id = Column(Integer, ForeignKey("channel.id"), nullable=False, index=True)
17 | channel = relationship("Channel", back_populates="messages")
18 |
19 | body = Column(String, nullable=False)
20 | created_at = Column(DateTime(timezone=True), nullable=False)
21 | updated_at = Column(DateTime(timezone=True), nullable=False)
22 |
--------------------------------------------------------------------------------
/chafan_core/app/models/notification.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 |
6 | from chafan_core.db.base_class import Base
7 |
8 | if TYPE_CHECKING:
9 | from . import * # noqa: F401, F403
10 |
11 |
12 | class Notification(Base):
13 | id = Column(Integer, primary_key=True, index=True)
14 | receiver_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
15 | receiver = relationship("User", back_populates="notifications")
16 |
17 | # Change to non-null after deprecation
18 | event_json = Column(String)
19 | created_at = Column(DateTime(timezone=True), nullable=False)
20 |
21 | is_read = Column(Boolean, default=False, server_default="false", nullable=False)
22 | is_delivered = Column(
23 | Boolean, default=False, server_default="false", nullable=False
24 | )
25 |
--------------------------------------------------------------------------------
/chafan_core/app/models/profile.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import (
4 | Column,
5 | ForeignKey,
6 | Integer,
7 | PrimaryKeyConstraint,
8 | UniqueConstraint,
9 | )
10 | from sqlalchemy.orm import relationship
11 |
12 | from chafan_core.db.base_class import Base
13 |
14 | if TYPE_CHECKING:
15 | from . import * # noqa: F401, F403
16 |
17 |
18 | class Profile(Base):
19 | __table_args__ = (
20 | UniqueConstraint("owner_id", "site_id"),
21 | PrimaryKeyConstraint("owner_id", "site_id"),
22 | )
23 |
24 | karma = Column(Integer, nullable=False, server_default="0")
25 | owner_id = Column(Integer, ForeignKey("user.id"), primary_key=True)
26 | owner = relationship("User", back_populates="profiles")
27 | site_id = Column(Integer, ForeignKey("site.id"), primary_key=True, nullable=False)
28 | site: "Site" = relationship("Site", back_populates="profiles") # type: ignore
29 |
--------------------------------------------------------------------------------
/chafan_core/app/models/reward.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import JSON
6 |
7 | from chafan_core.db.base_class import Base
8 |
9 | if TYPE_CHECKING:
10 | from . import * # noqa: F401, F403
11 |
12 |
13 | class Reward(Base):
14 | id = Column(Integer, primary_key=True, index=True)
15 | created_at = Column(DateTime(timezone=True), nullable=False, index=True)
16 | expired_at = Column(DateTime(timezone=True), nullable=False)
17 | claimed_at = Column(DateTime(timezone=True))
18 | refunded_at = Column(DateTime(timezone=True))
19 |
20 | giver_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
21 | giver = relationship(
22 | "User", back_populates="outgoing_rewards", foreign_keys=[giver_id]
23 | )
24 |
25 | receiver_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
26 | receiver = relationship(
27 | "User", back_populates="incoming_rewards", foreign_keys=[receiver_id]
28 | )
29 |
30 | coin_amount = Column(Integer, nullable=False)
31 | note_to_receiver = Column(String)
32 |
33 | condition = Column(JSON)
34 |
--------------------------------------------------------------------------------
/chafan_core/app/models/submission_archive.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import JSON
6 |
7 | from chafan_core.db.base_class import Base
8 | from chafan_core.utils.constants import editor_T
9 |
10 | if TYPE_CHECKING:
11 | from . import * # noqa: F401, F403
12 |
13 |
14 | class SubmissionArchive(Base):
15 | id = Column(Integer, primary_key=True, index=True)
16 | title = Column(String, nullable=False)
17 | description = Column(String)
18 | description_text = Column(String)
19 | description_editor: editor_T = Column(String) # type: ignore
20 | created_at = Column(DateTime(timezone=True), nullable=False)
21 | submission_id = Column(
22 | Integer, ForeignKey("submission.id"), nullable=False, index=True
23 | )
24 | submission = relationship("Submission", back_populates="archives")
25 | topic_uuids = Column(JSON)
26 |
--------------------------------------------------------------------------------
/chafan_core/app/models/task.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Column, DateTime, ForeignKey, Integer
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import JSON, Enum
6 |
7 | from chafan_core.db.base_class import Base
8 | from chafan_core.utils.base import TaskStatus
9 |
10 | if TYPE_CHECKING:
11 | from . import * # noqa: F401, F403
12 |
13 |
14 | class Task(Base):
15 | id = Column(Integer, primary_key=True, index=True)
16 | created_at = Column(DateTime(timezone=True), nullable=False)
17 | initiator_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
18 | initiator = relationship("User", back_populates="initiated_tasks")
19 | task_definition = Column(JSON, nullable=False)
20 |
21 | status = Column(Enum(TaskStatus), nullable=False)
22 |
--------------------------------------------------------------------------------
/chafan_core/app/models/topic.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, List
2 |
3 | from sqlalchemy import CHAR, Column, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import Boolean
6 |
7 | from chafan_core.db.base_class import Base
8 | from chafan_core.utils.base import UUID_LENGTH
9 |
10 | if TYPE_CHECKING:
11 | from . import * # noqa: F401, F403
12 |
13 |
14 | class Topic(Base):
15 | id = Column(Integer, primary_key=True, index=True)
16 | uuid = Column(CHAR(length=UUID_LENGTH), index=True, unique=True, nullable=False)
17 | name = Column(String, nullable=False)
18 | description = Column(String)
19 | is_category = Column(Boolean, server_default="false")
20 |
21 | parent_topic_id = Column(Integer, ForeignKey("topic.id"), index=True)
22 | parent_topic = relationship(
23 | "Topic", back_populates="child_topics", remote_side=[id]
24 | )
25 |
26 | child_topics: List["Topic"] = relationship("Topic", back_populates="parent_topic") # type: ignore
27 |
--------------------------------------------------------------------------------
/chafan_core/app/models/webhook.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING
2 |
3 | from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
4 | from sqlalchemy.orm import relationship
5 | from sqlalchemy.sql.sqltypes import JSON
6 |
7 | from chafan_core.db.base_class import Base
8 |
9 | if TYPE_CHECKING:
10 | from . import * # noqa: F401, F403
11 |
12 |
13 | class Webhook(Base):
14 | id = Column(Integer, primary_key=True, index=True)
15 | updated_at = Column(DateTime(timezone=True), nullable=False)
16 |
17 | site_id = Column(Integer, ForeignKey("site.id"), nullable=False, index=True)
18 | site: "Site" = relationship("Site", back_populates="webhooks") # type: ignore
19 |
20 | enabled = Column(Boolean, default=True, nullable=False)
21 | event_spec = Column(JSON, nullable=False)
22 | secret = Column(String, nullable=False)
23 | callback_url = Column(String, nullable=False)
24 |
--------------------------------------------------------------------------------
/chafan_core/app/recs/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/app/recs/__init__.py
--------------------------------------------------------------------------------
/chafan_core/app/schemas/activity.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import List, Literal, Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.event import Event
7 | from chafan_core.app.schemas.site import Site
8 |
9 |
10 | class OriginSite(BaseModel):
11 | origin_type: Literal["site"] = "site"
12 | subdomain: str
13 |
14 |
15 | Origin = OriginSite
16 |
17 |
18 | class UpdateOrigins(BaseModel):
19 | action: Literal["add", "remove"]
20 | origin: Origin
21 |
22 |
23 | class UserFeedSettings(BaseModel):
24 | blocked_origins: List[Origin] = []
25 |
26 |
27 | class Activity(BaseModel):
28 | id: int
29 | site: Optional[Site]
30 | created_at: datetime.datetime
31 | verb: str
32 | event: Event
33 | origins: Optional[List[Origin]]
34 |
35 |
36 | class FeedSequence(BaseModel):
37 | activities: List[Activity]
38 | random: bool
39 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/answer_archive.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from pydantic import BaseModel
4 |
5 | from chafan_core.app.schemas.richtext import RichText
6 |
7 |
8 | class AnswerArchiveInDB(BaseModel):
9 | id: int
10 | created_at: datetime.datetime
11 |
12 | class Config:
13 | from_attributes = True
14 |
15 |
16 | class AnswerArchive(AnswerArchiveInDB):
17 | content: RichText
18 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/answer_suggest_edit.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Literal, Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.answer import Answer
7 | from chafan_core.app.schemas.preview import UserPreview
8 | from chafan_core.app.schemas.richtext import RichText
9 |
10 |
11 | class AnswerSuggestEditInDB(BaseModel):
12 | uuid: str
13 | created_at: datetime.datetime
14 | comment: Optional[str]
15 | status: Literal["pending", "accepted", "rejected", "retracted"]
16 | accepted_at: Optional[datetime.datetime]
17 | rejected_at: Optional[datetime.datetime]
18 | retracted_at: Optional[datetime.datetime]
19 | accepted_diff_base: Optional[RichText]
20 |
21 | class Config:
22 | from_attributes = True
23 |
24 |
25 | class AnswerSuggestEdit(AnswerSuggestEditInDB):
26 | body_rich_text: RichText
27 | author: UserPreview
28 | answer: Answer
29 |
30 |
31 | class AnswerSuggestEditCreate(BaseModel):
32 | answer_uuid: str
33 | body_rich_text: RichText
34 | comment: Optional[str] = None
35 |
36 |
37 | class AnswerSuggestEditUpdate(BaseModel):
38 | status: Literal["pending", "accepted", "rejected", "retracted"]
39 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/application.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from pydantic import BaseModel
4 |
5 | from chafan_core.app.schemas.preview import UserPreview
6 | from chafan_core.app.schemas.site import Site
7 |
8 |
9 | # Shared properties
10 | class ApplicationBase(BaseModel):
11 | pass
12 |
13 |
14 | class ApplicationCreate(ApplicationBase):
15 | applied_site_id: int
16 |
17 |
18 | class ApplicationUpdate(BaseModel):
19 | pending: bool
20 |
21 |
22 | class ApplicationInDBBase(ApplicationBase):
23 | id: int
24 | pending: bool
25 | created_at: datetime.datetime
26 |
27 | class Config:
28 | from_attributes = True
29 |
30 |
31 | # Additional properties to return via API
32 | class Application(ApplicationInDBBase):
33 | applicant: UserPreview
34 | applied_site: Site
35 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/article_archive.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import List
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.richtext import RichText
7 | from chafan_core.app.schemas.topic import Topic
8 | from chafan_core.utils.constants import editor_T
9 |
10 |
11 | class ArticleArchiveInDB(BaseModel):
12 | id: int
13 | title: str
14 | topics: List[Topic] = []
15 | created_at: datetime.datetime
16 |
17 | # TODO: deprecate
18 | body: str
19 | editor: editor_T
20 |
21 | class Config:
22 | from_attributes = True
23 |
24 |
25 | class ArticleArchive(ArticleArchiveInDB):
26 | content: RichText
27 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/article_column.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.preview import UserPreview
7 | from chafan_core.utils.validators import UUID, StrippedNonEmptyStr
8 |
9 |
10 | # Shared properties
11 | class ArticleColumnBase(BaseModel):
12 | pass
13 |
14 |
15 | # Properties to receive via API on creation
16 | class ArticleColumnCreate(ArticleColumnBase):
17 | name: StrippedNonEmptyStr
18 | description: Optional[str] = None
19 |
20 |
21 | # Properties to receive via API on update
22 | class ArticleColumnUpdate(ArticleColumnBase):
23 | name: Optional[StrippedNonEmptyStr] = None
24 | description: Optional[str] = None
25 |
26 |
27 | class ArticleColumnInDBBase(ArticleColumnBase):
28 | uuid: UUID
29 | name: StrippedNonEmptyStr
30 | description: Optional[str]
31 | created_at: datetime.datetime
32 |
33 | class Config:
34 | from_attributes = True
35 |
36 |
37 | class UserArticleColumnSubscription(BaseModel):
38 | article_column_uuid: str
39 | subscription_count: int
40 | subscribed_by_me: bool
41 |
42 |
43 | # Additional properties to return via API
44 | class ArticleColumn(ArticleColumnInDBBase):
45 | owner: UserPreview
46 | subscription: Optional[UserArticleColumnSubscription] = None
47 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/audit_log.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Literal
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.preview import UserPreview
7 |
8 | AUDIT_LOG_API_TYPE = Literal[
9 | "/login/access-token",
10 | "create access token",
11 | "post answer",
12 | "post question",
13 | "put question",
14 | "post article",
15 | "post submission",
16 | "scheduled/run_deliver_notification_task",
17 | "scheduled/cache_new_activity_to_feeds",
18 | "scheduled/daily",
19 | "scheduled/refresh_search_index",
20 | "scheduled/force_refresh_all_index",
21 | ]
22 |
23 |
24 | # Properties to receive via API on creation
25 | class AuditLogCreate(BaseModel):
26 | pass
27 |
28 |
29 | class AuditLogUpdate(BaseModel):
30 | pass
31 |
32 |
33 | class AuditLogInDBBase(BaseModel):
34 | uuid: str
35 | created_at: datetime.datetime
36 | api: AUDIT_LOG_API_TYPE
37 | ipaddr: str
38 |
39 | class Config:
40 | from_attributes = True
41 |
42 |
43 | class AuditLog(AuditLogInDBBase):
44 | user: UserPreview
45 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/coin_deposit.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Optional
3 |
4 | from pydantic import BaseModel
5 |
6 |
7 | # Shared properties
8 | class CoinDepositBase(BaseModel):
9 | pass
10 |
11 |
12 | class CoinDepositReference(BaseModel):
13 | action: str
14 | object_id: str
15 |
16 |
17 | # Properties to receive via API on creation
18 | class CoinDepositCreate(CoinDepositBase):
19 | payee_id: int
20 | amount: int
21 | ref_id: str
22 | comment: Optional[str]
23 |
24 |
25 | # Properties to receive via API on update
26 | class CoinDepositUpdate(CoinDepositBase):
27 | pass
28 |
29 |
30 | class CoinDepositInDBBase(CoinDepositBase):
31 | id: int
32 | created_at: datetime.datetime
33 | amount: int
34 | ref_id: str
35 | authorizer_id: int
36 | payee_id: int
37 | comment: Optional[str]
38 |
39 | class Config:
40 | from_attributes = True
41 |
42 |
43 | # Additional properties to return via API
44 | class CoinDeposit(CoinDepositInDBBase):
45 | pass
46 |
47 |
48 | # Additional properties stored in DB
49 | class CoinDepositInDB(CoinDepositInDBBase):
50 | pass
51 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/coin_payment.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.event import Event
7 | from chafan_core.app.schemas.preview import UserPreview
8 |
9 |
10 | # Shared properties
11 | class CoinPaymentBase(BaseModel):
12 | pass
13 |
14 |
15 | # Properties to receive via API on creation
16 | class CoinPaymentCreate(CoinPaymentBase):
17 | payee_id: int
18 | amount: int
19 | event_json: str
20 | comment: Optional[str] = None
21 |
22 |
23 | # Properties to receive via API on update
24 | class CoinPaymentUpdate(CoinPaymentBase):
25 | pass
26 |
27 |
28 | class CoinPaymentInDBBase(CoinPaymentBase):
29 | id: int
30 | created_at: datetime.datetime
31 | amount: int
32 | comment: Optional[str]
33 |
34 | class Config:
35 | from_attributes = True
36 |
37 |
38 | # Additional properties to return via API
39 | class CoinPayment(CoinPaymentInDBBase):
40 | payer: UserPreview
41 | payee: UserPreview
42 | event: Optional[Event]
43 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/feedback.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.preview import UserPreview
7 | from chafan_core.utils.constants import feedback_status_T
8 |
9 |
10 | class FeedbackInDBBase(BaseModel):
11 | id: int
12 | created_at: datetime.datetime
13 | description: str
14 | status: feedback_status_T
15 | location_url: Optional[str] = None
16 |
17 | class Config:
18 | from_attributes = True
19 |
20 |
21 | class Feedback(FeedbackInDBBase):
22 | has_screenshot: bool
23 | user: Optional[UserPreview] = None
24 | user_email: Optional[str] = None
25 |
26 |
27 | class FeedbackCreate(BaseModel):
28 | pass
29 |
30 |
31 | class FeedbackUpdate(BaseModel):
32 | status: feedback_status_T
33 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/invitation_link.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.preview import UserPreview
7 | from chafan_core.app.schemas.site import Site
8 |
9 |
10 | class InvitationLinkCreate(BaseModel):
11 | invited_to_site_uuid: Optional[str] = None
12 |
13 |
14 | class InvitationLinkInDB(BaseModel):
15 | uuid: str
16 | created_at: datetime.datetime
17 | expired_at: datetime.datetime
18 | remaining_quota: int
19 |
20 | class Config:
21 | from_attributes = True
22 |
23 |
24 | class InvitationLink(InvitationLinkInDB):
25 | invited_to_site: Optional[Site]
26 | inviter: UserPreview
27 | valid: bool
28 |
29 |
30 | class InvitationLinkUpdate(BaseModel):
31 | pass
32 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/message.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from pydantic import BaseModel, validator
4 |
5 | from chafan_core.app.schemas.preview import UserPreview
6 | from chafan_core.utils.validators import validate_message_body
7 |
8 |
9 | # Shared properties
10 | class MessageBase(BaseModel):
11 | channel_id: int
12 | body: str
13 |
14 | @validator("body")
15 | def _valid_body(cls, v: str) -> str:
16 | validate_message_body(v)
17 | return v
18 |
19 |
20 | # Properties to receive via API on creation
21 | class MessageCreate(MessageBase):
22 | pass
23 |
24 |
25 | class MessageUpdate(BaseModel):
26 | pass
27 |
28 |
29 | class MessageInDBBase(MessageBase):
30 | id: int
31 | created_at: datetime.datetime
32 | updated_at: datetime.datetime
33 |
34 | class Config:
35 | from_attributes = True
36 |
37 |
38 | # Additional properties to return via API
39 | class Message(MessageInDBBase):
40 | author: UserPreview
41 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/mq.py:
--------------------------------------------------------------------------------
1 | from typing import Literal
2 |
3 | from pydantic import BaseModel
4 |
5 | from chafan_core.app.schemas.notification import Notification
6 |
7 |
8 | class WsUserMsg(BaseModel):
9 | type: Literal["notification"]
10 | data: Notification
11 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/msg.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 | from typing import List, Mapping, Optional
3 |
4 | from pydantic import BaseModel
5 | from pydantic.networks import AnyHttpUrl
6 |
7 | from chafan_core.utils.validators import StrippedNonEmptyBasicStr
8 |
9 |
10 | class Scores(BaseModel):
11 | full_score: int
12 | score: int
13 |
14 |
15 | class ClaimWelcomeTestScoreMsg(BaseModel):
16 | success: bool
17 | scores: Scores
18 |
19 |
20 | class UploadResultData(BaseModel):
21 | errFiles: List[str] = []
22 | succMap: Mapping[str, AnyHttpUrl]
23 |
24 |
25 | class UploadResults(BaseModel):
26 | msg: str = ""
27 | code: int = 0
28 | data: UploadResultData
29 |
30 |
31 | class HealthResponse(BaseModel):
32 | success: bool = True
33 | version: str = "v0.1.0"
34 |
35 |
36 | class WsAuthResponse(BaseModel):
37 | token: str
38 |
39 |
40 | class ErrorCode(str, Enum):
41 | pass
42 |
43 |
44 | class GenericResponse(BaseModel):
45 | success: bool = True
46 | error_code: Optional[ErrorCode] = None
47 | msg: Optional[str] = None
48 |
49 |
50 | class UploadedImage(BaseModel):
51 | url: str
52 |
53 |
54 | class SiteApplicationResponse(BaseModel):
55 | auto_approved: bool = False
56 | applied_before: bool = False
57 |
58 |
59 | class VerifyTelegramResponse(BaseModel):
60 | handle: StrippedNonEmptyBasicStr
61 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/notification.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.event import Event
7 |
8 |
9 | # Shared properties
10 | class NotificationBase(BaseModel):
11 | pass
12 |
13 |
14 | class NotificationCreate(NotificationBase):
15 | receiver_id: int
16 | created_at: datetime.datetime
17 | event_json: str
18 |
19 |
20 | class NotificationUpdate(BaseModel):
21 | is_read: bool
22 |
23 |
24 | class NotificationInDBBase(NotificationBase):
25 | id: int
26 | created_at: datetime.datetime
27 | is_read: bool
28 |
29 | class Config:
30 | from_attributes = True
31 |
32 |
33 | # Additional properties to return via API
34 | class Notification(NotificationInDBBase):
35 | event: Optional[Event]
36 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/preview.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import BaseModel
4 |
5 | from chafan_core.utils.validators import StrippedNonEmptyBasicStr
6 |
7 |
8 | class SocialAnnotations(BaseModel):
9 | follow_follows: Optional[int] = None
10 |
11 |
12 | class UserFollows(BaseModel):
13 | user_uuid: str
14 | followers_count: int
15 | followed_count: int
16 | followed_by_me: bool
17 |
18 |
19 | class UserPreview(BaseModel):
20 | uuid: str
21 | handle: StrippedNonEmptyBasicStr
22 | full_name: Optional[str]
23 | avatar_url: Optional[str] = None
24 | personal_introduction: Optional[str] = None
25 | karma: int = 0
26 | social_annotations: SocialAnnotations = SocialAnnotations()
27 | follows: Optional[UserFollows] = None
28 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/profile.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import BaseModel
4 |
5 | from chafan_core.app.schemas.preview import UserPreview
6 | from chafan_core.app.schemas.site import Site
7 |
8 |
9 | # Shared properties
10 | class ProfileBase(BaseModel):
11 | pass
12 |
13 |
14 | # Properties to receive via API on creation
15 | class ProfileCreate(ProfileBase):
16 | site_uuid: str
17 | owner_uuid: str
18 |
19 |
20 | class ProfileUpdate(ProfileBase):
21 | pass
22 |
23 |
24 | class ProfileInDBBase(ProfileBase):
25 | karma: int
26 |
27 | class Config:
28 | from_attributes = True
29 |
30 |
31 | # Additional properties to return via API
32 | class Profile(ProfileInDBBase):
33 | owner: UserPreview
34 | site: Site
35 | introduction: Optional[str] = None
36 |
37 |
38 | # Additional properties stored in DB
39 | class ProfileInDB(ProfileInDBBase):
40 | pass
41 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/question_archive.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import List, Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.preview import UserPreview
7 | from chafan_core.app.schemas.richtext import RichText
8 | from chafan_core.app.schemas.topic import Topic
9 | from chafan_core.utils.validators import StrippedNonEmptyStr
10 |
11 |
12 | class QuestionArchiveInDB(BaseModel):
13 | id: int
14 | title: StrippedNonEmptyStr
15 | topics: List[Topic] = []
16 | created_at: datetime.datetime
17 |
18 | class Config:
19 | from_attributes = True
20 |
21 |
22 | class QuestionArchive(QuestionArchiveInDB):
23 | editor: Optional[UserPreview]
24 | desc: Optional[RichText]
25 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/question_page.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional, Union
2 |
3 | from pydantic import BaseModel
4 |
5 | from chafan_core.app.schemas.answer import (
6 | Answer,
7 | AnswerForVisitor,
8 | AnswerPreview,
9 | AnswerPreviewForVisitor,
10 | )
11 | from chafan_core.app.schemas.question import Question, QuestionForVisitor
12 | from chafan_core.app.schemas.user import UserQuestionSubscription
13 |
14 |
15 | class QuestionPageFlags(BaseModel):
16 | editable: bool = False
17 | answer_writable: bool = False
18 | comment_writable: bool = False
19 | hideable: bool = False
20 | is_mod: bool = False
21 |
22 |
23 | class QuestionPage(BaseModel):
24 | question: Union[Question, QuestionForVisitor]
25 | full_answers: List[Union[Answer, AnswerForVisitor]]
26 | answer_previews: List[Union[AnswerPreview, AnswerPreviewForVisitor]]
27 | question_subscription: Optional[UserQuestionSubscription]
28 | flags: QuestionPageFlags
29 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/reaction.py:
--------------------------------------------------------------------------------
1 | from typing import Literal, Mapping, Set
2 |
3 | from pydantic import BaseModel
4 |
5 | ReactionObjectType = Literal["question", "answer", "comment", "article", "submission"]
6 |
7 |
8 | class Reaction(BaseModel):
9 | object_uuid: str
10 | object_type: ReactionObjectType
11 | reaction: Literal["👍", "👎", "👀", "❤️", "😂", "🙏"]
12 | action: Literal["add", "cancel"]
13 |
14 |
15 | class Reactions(BaseModel):
16 | counters: Mapping[str, int]
17 | my_reactions: Set[str]
18 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/report.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.preview import UserPreview
7 | from chafan_core.utils.base import ReportReason
8 |
9 |
10 | # Properties to receive via API on creation
11 | class ReportCreate(BaseModel):
12 | question_uuid: Optional[str]
13 | submission_uuid: Optional[str]
14 | answer_uuid: Optional[str]
15 | article_uuid: Optional[str]
16 | comment_uuid: Optional[str]
17 | reason: ReportReason
18 | reason_comment: Optional[str]
19 |
20 |
21 | # Properties to receive via API on update
22 | class ReportUpdate(BaseModel):
23 | pass
24 |
25 |
26 | class ReportInDBBase(BaseModel):
27 | id: int
28 | created_at: datetime.datetime
29 | reason: ReportReason
30 | reason_comment: Optional[str]
31 |
32 | class Config:
33 | from_attributes = True
34 |
35 |
36 | # Additional properties to return via API
37 | class Report(ReportInDBBase):
38 | author: UserPreview
39 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/richtext.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import BaseModel
4 |
5 | from chafan_core.utils.constants import editor_T
6 |
7 |
8 | class RichText(BaseModel):
9 | source: str
10 | rendered_text: Optional[str] = None
11 | editor: editor_T
12 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/submission_archive.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import List, Optional
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.richtext import RichText
7 |
8 |
9 | class SubmissionEditableSnapshot(BaseModel):
10 | title: str
11 | desc: Optional[RichText]
12 | topic_uuids: Optional[List[str]]
13 |
14 |
15 | class SubmissionArchive(SubmissionEditableSnapshot):
16 | id: int
17 | url: Optional[str]
18 | created_at: datetime.datetime
19 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/task.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Literal, Union
3 |
4 | from pydantic import BaseModel
5 |
6 | from chafan_core.app.schemas.preview import UserPreview
7 | from chafan_core.utils.base import TaskStatus
8 |
9 |
10 | class SuperUserBroadcastTaskDefinition(BaseModel):
11 | task_type: Literal["super_broadcast"] = "super_broadcast"
12 | message: str
13 |
14 |
15 | class SiteModeratorBroadcastTaskDefinition(BaseModel):
16 | task_type: Literal["site_broadcast"] = "site_broadcast"
17 | to_members_of_site_uuid: str
18 | submission_uuid: str
19 |
20 |
21 | TaskDefinition = Union[
22 | SuperUserBroadcastTaskDefinition, SiteModeratorBroadcastTaskDefinition
23 | ]
24 |
25 |
26 | class TaskInDB(BaseModel):
27 | id: int
28 | created_at: datetime.datetime
29 | task_definition: TaskDefinition
30 | status: TaskStatus
31 |
32 | class Config:
33 | from_attributes = True
34 |
35 |
36 | class Task(TaskInDB):
37 | initiator: UserPreview
38 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/token.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import BaseModel
4 |
5 |
6 | class Token(BaseModel):
7 | access_token: str
8 | token_type: str
9 |
10 |
11 | class TokenPayload(BaseModel):
12 | sub: Optional[int] = None
13 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/topic.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import BaseModel
4 |
5 | from chafan_core.utils.validators import StrippedNonEmptyStr
6 |
7 |
8 | # Shared properties
9 | class TopicBase(BaseModel):
10 | name: StrippedNonEmptyStr
11 |
12 |
13 | # Properties to receive via API on creation
14 | class TopicCreate(TopicBase):
15 | pass
16 |
17 |
18 | # Properties to receive via API on update
19 | class TopicUpdate(BaseModel):
20 | description: Optional[str] = None
21 | parent_topic_uuid: Optional[str] = None
22 |
23 |
24 | class TopicInDBBase(TopicBase):
25 | uuid: str
26 |
27 | class Config:
28 | from_attributes = True
29 |
30 |
31 | # Additional properties to return via API
32 | class Topic(TopicInDBBase):
33 | # NOTE: this is a deliberate "problem" since parent topic is only useful when returning to the Topic page
34 | parent_topic_uuid: Optional[str] = None
35 |
36 |
37 | # Additional properties stored in DB
38 | class TopicInDB(TopicInDBBase):
39 | pass
40 |
--------------------------------------------------------------------------------
/chafan_core/app/schemas/webhook.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from typing import Literal, Optional
3 |
4 | from pydantic import BaseModel
5 | from pydantic.networks import HttpUrl
6 |
7 | from chafan_core.app.schemas.site import Site
8 |
9 |
10 | class WebhookSiteEvent(BaseModel):
11 | event_type: Literal["site_event"] = "site_event"
12 | new_question: Optional[bool] = False
13 | new_answer: Optional[bool] = False
14 | new_submission: Optional[bool] = False
15 |
16 |
17 | class WebhookEventSpec(BaseModel):
18 | content: WebhookSiteEvent
19 |
20 |
21 | # Properties to receive via API on creation
22 | class WebhookCreate(BaseModel):
23 | site_uuid: str
24 | event_spec: WebhookEventSpec
25 | secret: str
26 | callback_url: HttpUrl
27 |
28 |
29 | # Properties to receive via API on update
30 | class WebhookUpdate(BaseModel):
31 | enabled: Optional[bool] = None
32 | event_spec: Optional[WebhookEventSpec] = None
33 | secret: Optional[str] = None
34 | callback_url: Optional[HttpUrl] = None
35 |
36 |
37 | class WebhookInDB(BaseModel):
38 | id: int
39 | updated_at: datetime.datetime
40 | enabled: bool
41 | event_spec: WebhookEventSpec
42 | secret: str
43 | callback_url: HttpUrl
44 |
45 | class Config:
46 | from_attributes = True
47 |
48 |
49 | class Webhook(WebhookInDB):
50 | site: Site
51 |
--------------------------------------------------------------------------------
/chafan_core/app/security.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime, timedelta
2 | from typing import Any, Optional, Union
3 |
4 | from jose import jwt
5 | from passlib.context import CryptContext # type: ignore
6 | from pydantic.types import SecretStr
7 |
8 | from chafan_core.app.config import settings
9 | from chafan_core.utils.base import unwrap
10 |
11 | pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
12 |
13 |
14 | ALGORITHM = "HS256"
15 |
16 |
17 | def create_access_token(
18 | subject: Union[str, Any], expires_delta: Optional[timedelta] = None
19 | ) -> str:
20 | if expires_delta:
21 | expire = datetime.utcnow() + expires_delta
22 | else:
23 | expire = datetime.utcnow() + timedelta(
24 | minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
25 | )
26 | to_encode = {"exp": expire, "sub": str(subject)}
27 | encoded_jwt = jwt.encode(
28 | to_encode, unwrap(settings.SECRET_KEY), algorithm=ALGORITHM
29 | )
30 | return encoded_jwt
31 |
32 |
33 | def verify_password(plain_password: SecretStr, hashed_password: str) -> bool:
34 | return pwd_context.verify(plain_password.get_secret_value(), hashed_password)
35 |
36 |
37 | def get_password_hash(password: SecretStr) -> str:
38 | return pwd_context.hash(password.get_secret_value())
39 |
--------------------------------------------------------------------------------
/chafan_core/app/task_utils.py:
--------------------------------------------------------------------------------
1 | from typing import Callable, Optional, TypeVar
2 |
3 | from sqlalchemy.orm.session import Session
4 |
5 | from chafan_core.app.common import handle_exception
6 | from chafan_core.app.data_broker import DataBroker
7 |
8 | T = TypeVar("T")
9 |
10 |
11 | def execute_with_db(
12 | db: Session, runnable: Callable[[Session], T], auto_commit: bool = True
13 | ) -> Optional[T]:
14 | try:
15 | ret = runnable(db)
16 | if auto_commit:
17 | db.commit()
18 | return ret
19 | except Exception as e:
20 | handle_exception(e)
21 | finally:
22 | db.close()
23 | return None
24 |
25 |
26 | def execute_with_broker(
27 | runnable: Callable[[DataBroker], T],
28 | use_read_replica: bool = False,
29 | auto_commit: bool = True,
30 | ) -> Optional[T]:
31 | try:
32 | broker = DataBroker(use_read_replica=use_read_replica)
33 | ret = runnable(broker)
34 | if auto_commit and broker.db:
35 | broker.db.commit()
36 | return ret
37 | except Exception as e:
38 | handle_exception(e)
39 | finally:
40 | broker.close()
41 | return None
42 |
--------------------------------------------------------------------------------
/chafan_core/app/ws_connections.py:
--------------------------------------------------------------------------------
1 | from typing import MutableMapping
2 |
3 | from fastapi.websockets import WebSocket
4 |
5 |
6 | class ConnectionManager:
7 | def __init__(self) -> None:
8 | # User ID -> WebSocket
9 | self.active_connections: MutableMapping[int, WebSocket] = {}
10 |
11 | async def connect(self, user_id: int, websocket: WebSocket) -> None:
12 | await websocket.accept()
13 | self.active_connections[user_id] = websocket
14 |
15 | def remove(self, user_id: int) -> None:
16 | del self.active_connections[user_id]
17 |
18 | async def send_message(self, message: str, user_id: int) -> None:
19 | ws = self.active_connections.get(user_id)
20 | if ws:
21 | await ws.send_text(message)
22 |
23 |
24 | manager = ConnectionManager()
25 |
--------------------------------------------------------------------------------
/chafan_core/db/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/db/__init__.py
--------------------------------------------------------------------------------
/chafan_core/db/base.py:
--------------------------------------------------------------------------------
1 | # Import all the models, so that Base has them before being
2 | # imported by Alembic
3 | from chafan_core.app.models.user import User # noqa
4 | from chafan_core.db.base_class import Base # noqa
5 |
--------------------------------------------------------------------------------
/chafan_core/db/base_class.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 |
3 | from sqlalchemy.ext.declarative import declared_attr
4 | from sqlalchemy.orm import DeclarativeBase # type: ignore
5 |
6 |
7 | class Base(DeclarativeBase):
8 | __allow_unmapped__ = True
9 |
10 | # Generate __tablename__ automatically
11 | @declared_attr
12 | def __tablename__(cls) -> str:
13 | return cls.__name__.lower()
14 |
--------------------------------------------------------------------------------
/chafan_core/db/session.py:
--------------------------------------------------------------------------------
1 | from sqlalchemy import create_engine
2 | from sqlalchemy.orm import sessionmaker
3 |
4 | from chafan_core.app.config import settings
5 |
6 | engine = create_engine(
7 | settings.DATABASE_URL,
8 | pool_pre_ping=True,
9 | pool_size=settings.DB_SESSION_POOL_SIZE,
10 | max_overflow=settings.DB_SESSION_POOL_MAX_OVERFLOW_SIZE,
11 | )
12 |
13 | read_engine = engine
14 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
15 | ReadSessionLocal = SessionLocal
16 |
--------------------------------------------------------------------------------
/chafan_core/db/simple_session.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from dotenv import load_dotenv
4 | from sqlalchemy import create_engine
5 | from sqlalchemy.orm import sessionmaker
6 |
7 | load_dotenv()
8 |
9 | # Format: postgres://:@:/
10 | # Production database_name is: chafan_prod
11 | DATABASE_URL = os.getenv("DATABASE_URL")
12 |
13 | engine = create_engine(
14 | DATABASE_URL,
15 | pool_pre_ping=True,
16 | max_overflow=5,
17 | )
18 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
19 |
--------------------------------------------------------------------------------
/chafan_core/scheduled/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/scheduled/__init__.py
--------------------------------------------------------------------------------
/chafan_core/scheduled/deliver_notifications.py:
--------------------------------------------------------------------------------
1 | from chafan_core.app.task_utils import execute_with_broker
2 | from chafan_core.scheduled.lib import deliver_notifications
3 |
4 |
5 | def run_deliver_notification_task() -> None:
6 | print("run_deliver_notification_task", flush=True)
7 | execute_with_broker(deliver_notifications)
8 |
--------------------------------------------------------------------------------
/chafan_core/tests/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 |
--------------------------------------------------------------------------------
/chafan_core/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/tests/__init__.py
--------------------------------------------------------------------------------
/chafan_core/tests/app/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/tests/app/api/__init__.py
--------------------------------------------------------------------------------
/chafan_core/tests/app/api/api_v1/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/tests/app/api/api_v1/__init__.py
--------------------------------------------------------------------------------
/chafan_core/tests/app/api/api_v1/test_answers.py:
--------------------------------------------------------------------------------
1 | from fastapi.testclient import TestClient
2 |
3 | from chafan_core.app.config import settings
4 | from chafan_core.utils.base import get_uuid
5 |
6 |
7 | def test_answers(
8 | client: TestClient,
9 | normal_user_token_headers: dict,
10 | normal_user_authored_question_uuid: str,
11 | ) -> None:
12 | data = {
13 | "question_uuid": normal_user_authored_question_uuid,
14 | "content": {
15 | "source": "test answer",
16 | "rendered_text": "test answer",
17 | "editor": "markdown",
18 | },
19 | "is_published": True,
20 | "is_autosaved": False,
21 | "visibility": "anyone",
22 | "writing_session_uuid": get_uuid(),
23 | }
24 |
25 | r = client.post(
26 | f"{settings.API_V1_STR}/answers/",
27 | json=data,
28 | )
29 | assert r.status_code == 401
30 |
31 | r = client.post(
32 | f"{settings.API_V1_STR}/answers/",
33 | headers=normal_user_token_headers,
34 | json=data,
35 | )
36 | assert 200 <= r.status_code < 300, r.text
37 | assert "author" in r.json(), r.json()
38 | normal_user_uuid = client.get(
39 | f"{settings.API_V1_STR}/me", headers=normal_user_token_headers
40 | ).json()["uuid"]
41 | assert r.json()["author"]["uuid"] == normal_user_uuid
42 |
--------------------------------------------------------------------------------
/chafan_core/tests/app/api/api_v1/test_login.py:
--------------------------------------------------------------------------------
1 | from fastapi.testclient import TestClient
2 |
3 | from chafan_core.app.common import (
4 | check_token_validity_impl,
5 | generate_password_reset_token,
6 | )
7 | from chafan_core.app.config import settings
8 | from chafan_core.utils.base import unwrap
9 |
10 |
11 | def test_get_access_token(client: TestClient) -> None:
12 | login_data = {
13 | "username": unwrap(settings.FIRST_SUPERUSER),
14 | "password": unwrap(settings.FIRST_SUPERUSER_PASSWORD).get_secret_value(),
15 | }
16 | r = client.post(f"{settings.API_V1_STR}/login/access-token", data=login_data)
17 | tokens = r.json()
18 | assert r.status_code == 200
19 | assert "access_token" in tokens
20 | assert tokens["access_token"]
21 |
22 |
23 | def test_reset_password_verify(client: TestClient) -> None:
24 | reset_token = generate_password_reset_token(email=unwrap(settings.FIRST_SUPERUSER))
25 | assert check_token_validity_impl(reset_token), reset_token
26 |
27 | r = client.post(
28 | f"{settings.API_V1_STR}/check-token-validity/", data={"token": reset_token}
29 | )
30 | response = r.json()
31 | assert r.status_code == 200
32 | assert response["success"], response["msg"]
33 |
--------------------------------------------------------------------------------
/chafan_core/tests/app/api/api_v1/test_profiles.py:
--------------------------------------------------------------------------------
1 | from fastapi.testclient import TestClient
2 | from sqlalchemy.orm.session import Session
3 |
4 | from chafan_core.app import crud
5 | from chafan_core.app.config import settings
6 |
7 |
8 | def test_profiles(
9 | client: TestClient,
10 | db: Session,
11 | superuser_token_headers: dict,
12 | normal_user_token_headers: dict,
13 | example_site_uuid: str,
14 | normal_user_id: int,
15 | ) -> None:
16 | site = crud.site.get_by_uuid(db, uuid=example_site_uuid)
17 | assert site is not None
18 | crud.profile.remove_by_user_and_site(db, owner_id=normal_user_id, site_id=site.id)
19 | normal_user_uuid = client.get(
20 | f"{settings.API_V1_STR}/me", headers=normal_user_token_headers
21 | ).json()["uuid"]
22 | data = {"site_uuid": example_site_uuid, "user_uuid": normal_user_uuid}
23 |
24 | r = client.post(
25 | f"{settings.API_V1_STR}/users/invite",
26 | headers=superuser_token_headers,
27 | json=data,
28 | )
29 | assert 200 <= r.status_code < 300, r.text
30 |
31 | r = client.get(
32 | f"{settings.API_V1_STR}/profiles/members/{example_site_uuid}/{normal_user_uuid}",
33 | headers=normal_user_token_headers,
34 | )
35 | assert r.status_code == 200, (r.status_code, r.json())
36 |
--------------------------------------------------------------------------------
/chafan_core/tests/app/crud/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/tests/app/crud/__init__.py
--------------------------------------------------------------------------------
/chafan_core/tests/app/test_email_utils.py:
--------------------------------------------------------------------------------
1 | from pytest_mock.plugin import MockerFixture
2 |
3 | from chafan_core.app.email_utils import send_verification_code_email
4 |
5 |
6 | def test_send_verification_code_email(mocker: MockerFixture) -> None:
7 | mocker.patch("chafan_core.app.config.settings.EMAILS_ENABLED", True)
8 | send_verification_code_email("test@example.com", "123456")
9 |
--------------------------------------------------------------------------------
/chafan_core/tests/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/tests/utils/__init__.py
--------------------------------------------------------------------------------
/chafan_core/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chafan-dev/chafan-core/431d914a3fee8c14fcbe1c8a790585d127cc100f/chafan_core/utils/__init__.py
--------------------------------------------------------------------------------
/mypy.ini:
--------------------------------------------------------------------------------
1 | [mypy]
2 | plugins = pydantic.mypy, sqlmypy
3 |
4 | strict_optional = True
5 |
6 | follow_imports = silent
7 | warn_redundant_casts = True
8 | warn_unused_ignores = True
9 | ; disallow_any_generics = True
10 | check_untyped_defs = True
11 | ; no_implicit_reexport = True
12 |
13 | # for strict mypy: (this is the tricky one :-))
14 | disallow_untyped_defs = True
15 |
16 | [pydantic-mypy]
17 | init_forbid_extra = True
18 | init_typed = True
19 | warn_required_dynamic_aliases = True
20 | warn_untyped_fields = True
21 |
--------------------------------------------------------------------------------
/scripts/check.py:
--------------------------------------------------------------------------------
1 | from dotenv import load_dotenv # isort:skip
2 |
3 | load_dotenv() # isort:skip
4 |
5 | from chafan_core.app.common import EVENT_TEMPLATES
6 | from chafan_core.app.schemas.event import Event
7 |
8 | _event_verbs = []
9 |
10 | for k, v in Event.model_json_schema()["$defs"].items():
11 | if k == "ContentVisibility":
12 | continue
13 | if "properties" not in v:
14 | raise Exception(f"{k}: {v}")
15 | if "verb" in v["properties"]:
16 | _event_verbs.append(v["properties"]["verb"]["default"])
17 |
18 | assert set(EVENT_TEMPLATES.keys()) == set(_event_verbs), set(
19 | EVENT_TEMPLATES.keys()
20 | ).symmetric_difference(set(_event_verbs))
21 |
22 | print(f"Checked _event_verbs: {_event_verbs}")
23 |
--------------------------------------------------------------------------------
/scripts/dramatiq_worker.sh:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 | set -e
3 |
4 | export dramatiq_prom_host=0.0.0.0
5 | export dramatiq_prom_port=9191
6 | export prometheus_multiproc_dir=/tmp/dramatiq-prometheus
7 | export dramatiq_prom_db=$prometheus_multiproc_dir
8 | export DB_SESSION_POOL_SIZE=2
9 | export DB_SESSION_POOL_MAX_OVERFLOW_SIZE=1
10 |
11 | mkdir -p $prometheus_multiproc_dir
12 |
13 | dramatiq --threads 2 chafan_core.app.task
14 |
--------------------------------------------------------------------------------
/scripts/format.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | set -x
3 |
4 | isort --force-single-line-imports chafan_core
5 | autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place chafan_core --exclude=__init__.py
6 | black chafan_core
7 | isort chafan_core
8 |
--------------------------------------------------------------------------------
/scripts/initial_data.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from dotenv import load_dotenv # isort:skip
4 | load_dotenv() # isort:skip
5 |
6 | from chafan_core.db.init_db import init_db
7 | from chafan_core.db.session import SessionLocal
8 |
9 | logging.basicConfig(level=logging.INFO)
10 | logger = logging.getLogger(__name__)
11 |
12 |
13 | def init() -> None:
14 | db = SessionLocal()
15 | init_db(db)
16 |
17 |
18 | def main() -> None:
19 | logger.info("Creating initial data")
20 | init()
21 | logger.info("Initial data created")
22 |
23 |
24 | if __name__ == "__main__":
25 | main()
26 |
--------------------------------------------------------------------------------
/scripts/lint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -x
4 | set -e
5 |
6 | mypy chafan_core
7 | black chafan_core --check
8 | isort --check-only --skip chafan_core/e2e_tests/main.py chafan_core
9 | flake8 chafan_core --max-line-length 99 --select=E9,E63,F7,F82
10 |
--------------------------------------------------------------------------------
/scripts/reset_app_state.sh:
--------------------------------------------------------------------------------
1 | set -e
2 | set -x
3 |
4 | redis-cli flushall
5 | psql -h localhost -p 5432 -U postgres -c 'drop database chafan_dev WITH (FORCE);'
6 | psql -h localhost -p 5432 -U postgres -c 'create database chafan_dev;'
7 | alembic upgrade head
8 | python scripts/initial_data.py
9 |
--------------------------------------------------------------------------------
/scripts/run-unit-tests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 | set -x
5 |
6 | pytest -vv chafan_core/tests "${@}"
7 |
--------------------------------------------------------------------------------
/scripts/upgrade-db.py:
--------------------------------------------------------------------------------
1 | import os
2 | import subprocess
3 |
4 | from chafan_core.app.config import settings
5 |
6 | env = os.environ.copy()
7 | env["DATABASE_URL"] = settings.DATABASE_URL
8 |
9 | subprocess.check_call(["alembic", "upgrade", "head"], env=env)
10 |
--------------------------------------------------------------------------------