|
4 | 4 |
|
5 | 5 | import sqlalchemy as sa |
6 | 6 | from aiohttp import web |
| 7 | +from common_library.exclude import UnSet, is_unset |
7 | 8 | from common_library.users_enums import AccountRequestStatus, UserRole |
8 | 9 | from models_library.groups import GroupID |
9 | 10 | from models_library.products import ProductName |
@@ -534,30 +535,6 @@ async def get_user_products( |
534 | 535 | return [row async for row in result] |
535 | 536 |
|
536 | 537 |
|
537 | | -async def create_user_pre_registration( |
538 | | - engine: AsyncEngine, |
539 | | - connection: AsyncConnection | None = None, |
540 | | - *, |
541 | | - email: str, |
542 | | - created_by: UserID, |
543 | | - product_name: ProductName, |
544 | | - **other_values, |
545 | | -) -> int: |
546 | | - async with transaction_context(engine, connection) as conn: |
547 | | - result = await conn.execute( |
548 | | - sa.insert(users_pre_registration_details) |
549 | | - .values( |
550 | | - created_by=created_by, |
551 | | - pre_email=email, |
552 | | - product_name=product_name, |
553 | | - **other_values, |
554 | | - ) |
555 | | - .returning(users_pre_registration_details.c.id) |
556 | | - ) |
557 | | - pre_registration_id: int = result.scalar_one() |
558 | | - return pre_registration_id |
559 | | - |
560 | | - |
561 | 538 | async def get_user_billing_details( |
562 | 539 | engine: AsyncEngine, connection: AsyncConnection | None = None, *, user_id: UserID |
563 | 540 | ) -> UserBillingDetails: |
@@ -694,3 +671,176 @@ async def update_user_profile( |
694 | 671 | ) from err |
695 | 672 |
|
696 | 673 | raise # not due to name duplication |
| 674 | + |
| 675 | + |
| 676 | +async def create_user_pre_registration( |
| 677 | + engine: AsyncEngine, |
| 678 | + connection: AsyncConnection | None = None, |
| 679 | + *, |
| 680 | + email: str, |
| 681 | + created_by: UserID, |
| 682 | + product_name: ProductName, |
| 683 | + **other_values, |
| 684 | +) -> int: |
| 685 | + async with transaction_context(engine, connection) as conn: |
| 686 | + result = await conn.execute( |
| 687 | + sa.insert(users_pre_registration_details) |
| 688 | + .values( |
| 689 | + created_by=created_by, |
| 690 | + pre_email=email, |
| 691 | + product_name=product_name, |
| 692 | + **other_values, |
| 693 | + ) |
| 694 | + .returning(users_pre_registration_details.c.id) |
| 695 | + ) |
| 696 | + pre_registration_id: int = result.scalar_one() |
| 697 | + return pre_registration_id |
| 698 | + |
| 699 | + |
| 700 | +async def list_user_pre_registrations( |
| 701 | + engine: AsyncEngine, |
| 702 | + connection: AsyncConnection | None = None, |
| 703 | + *, |
| 704 | + filter_by_pre_email: str | None = None, |
| 705 | + filter_by_product_name: ProductName | UnSet = UnSet.VALUE, |
| 706 | + filter_by_account_request_status: AccountRequestStatus | None = None, |
| 707 | + pagination_limit: int = 50, |
| 708 | + pagination_offset: int = 0, |
| 709 | +) -> tuple[list[dict[str, Any]], int]: |
| 710 | + """Lists user pre-registrations with optional filters. |
| 711 | +
|
| 712 | + Args: |
| 713 | + engine: Database engine |
| 714 | + connection: Optional existing connection |
| 715 | + filter_by_pre_email: Filter by email pattern (SQL LIKE pattern) |
| 716 | + filter_by_product_name: Filter by product name |
| 717 | + filter_by_account_request_status: Filter by account request status |
| 718 | + pagination_limit: Maximum number of results to return |
| 719 | + pagination_offset: Number of results to skip (for pagination) |
| 720 | +
|
| 721 | + Returns: |
| 722 | + Tuple of (list of pre-registration records, total count) |
| 723 | + """ |
| 724 | + # Base query conditions |
| 725 | + where_conditions = [] |
| 726 | + |
| 727 | + # Apply filters if provided |
| 728 | + if filter_by_pre_email is not None: |
| 729 | + where_conditions.append( |
| 730 | + users_pre_registration_details.c.pre_email.ilike(f"%{filter_by_pre_email}%") |
| 731 | + ) |
| 732 | + |
| 733 | + if not is_unset(filter_by_product_name): |
| 734 | + where_conditions.append( |
| 735 | + users_pre_registration_details.c.product_name == filter_by_product_name |
| 736 | + ) |
| 737 | + |
| 738 | + if filter_by_account_request_status is not None: |
| 739 | + where_conditions.append( |
| 740 | + users_pre_registration_details.c.account_request_status |
| 741 | + == filter_by_account_request_status |
| 742 | + ) |
| 743 | + |
| 744 | + # Combine conditions |
| 745 | + where_clause = sa.and_(*where_conditions) if where_conditions else sa.true() |
| 746 | + |
| 747 | + # Create an alias for the users table for the created_by join |
| 748 | + creator_users_alias = sa.alias(users, name="creator") |
| 749 | + reviewer_users_alias = sa.alias(users, name="reviewer") |
| 750 | + |
| 751 | + # Count query for pagination |
| 752 | + count_query = ( |
| 753 | + sa.select(sa.func.count().label("total")) |
| 754 | + .select_from(users_pre_registration_details) |
| 755 | + .where(where_clause) |
| 756 | + ) |
| 757 | + |
| 758 | + # Main query to get pre-registration data |
| 759 | + main_query = ( |
| 760 | + sa.select( |
| 761 | + users_pre_registration_details.c.id, |
| 762 | + users_pre_registration_details.c.user_id, |
| 763 | + users_pre_registration_details.c.pre_email, |
| 764 | + users_pre_registration_details.c.pre_first_name, |
| 765 | + users_pre_registration_details.c.pre_last_name, |
| 766 | + users_pre_registration_details.c.pre_phone, |
| 767 | + users_pre_registration_details.c.institution, |
| 768 | + users_pre_registration_details.c.address, |
| 769 | + users_pre_registration_details.c.city, |
| 770 | + users_pre_registration_details.c.state, |
| 771 | + users_pre_registration_details.c.postal_code, |
| 772 | + users_pre_registration_details.c.country, |
| 773 | + users_pre_registration_details.c.product_name, |
| 774 | + users_pre_registration_details.c.account_request_status, |
| 775 | + users_pre_registration_details.c.extras, |
| 776 | + users_pre_registration_details.c.created, |
| 777 | + users_pre_registration_details.c.modified, |
| 778 | + users_pre_registration_details.c.created_by, |
| 779 | + creator_users_alias.c.name.label("created_by_name"), |
| 780 | + users_pre_registration_details.c.account_request_reviewed_by, |
| 781 | + reviewer_users_alias.c.name.label("reviewed_by_name"), |
| 782 | + users_pre_registration_details.c.account_request_reviewed_at, |
| 783 | + ) |
| 784 | + .select_from( |
| 785 | + users_pre_registration_details.outerjoin( |
| 786 | + creator_users_alias, |
| 787 | + users_pre_registration_details.c.created_by == creator_users_alias.c.id, |
| 788 | + ).outerjoin( |
| 789 | + reviewer_users_alias, |
| 790 | + users_pre_registration_details.c.account_request_reviewed_by |
| 791 | + == reviewer_users_alias.c.id, |
| 792 | + ) |
| 793 | + ) |
| 794 | + .where(where_clause) |
| 795 | + .order_by( |
| 796 | + users_pre_registration_details.c.created.desc(), |
| 797 | + users_pre_registration_details.c.pre_email, |
| 798 | + ) |
| 799 | + .limit(pagination_limit) |
| 800 | + .offset(pagination_offset) |
| 801 | + ) |
| 802 | + |
| 803 | + async with pass_or_acquire_connection(engine, connection) as conn: |
| 804 | + # Get total count |
| 805 | + count_result = await conn.execute(count_query) |
| 806 | + total_count = count_result.scalar() |
| 807 | + |
| 808 | + # Get pre-registration records |
| 809 | + result = await conn.execute(main_query) |
| 810 | + records = result.mappings().all() |
| 811 | + |
| 812 | + return list(records), total_count |
| 813 | + |
| 814 | + |
| 815 | +async def review_user_pre_registration( |
| 816 | + engine: AsyncEngine, |
| 817 | + connection: AsyncConnection | None = None, |
| 818 | + *, |
| 819 | + pre_registration_id: int, |
| 820 | + reviewed_by: UserID, |
| 821 | + new_status: AccountRequestStatus, |
| 822 | +) -> None: |
| 823 | + """Updates the account request status of a pre-registered user. |
| 824 | +
|
| 825 | + Args: |
| 826 | + engine: The database engine |
| 827 | + connection: Optional existing connection |
| 828 | + pre_registration_id: ID of the pre-registration record |
| 829 | + reviewed_by: ID of the user who reviewed the request |
| 830 | + new_status: New status (APPROVED or REJECTED) |
| 831 | + """ |
| 832 | + if new_status not in (AccountRequestStatus.APPROVED, AccountRequestStatus.REJECTED): |
| 833 | + raise ValueError( |
| 834 | + f"Invalid status for review: {new_status}. Must be APPROVED or REJECTED." |
| 835 | + ) |
| 836 | + |
| 837 | + async with transaction_context(engine, connection) as conn: |
| 838 | + await conn.execute( |
| 839 | + users_pre_registration_details.update() |
| 840 | + .values( |
| 841 | + account_request_status=new_status, |
| 842 | + account_request_reviewed_by=reviewed_by, |
| 843 | + account_request_reviewed_at=sa.func.now(), |
| 844 | + ) |
| 845 | + .where(users_pre_registration_details.c.id == pre_registration_id) |
| 846 | + ) |
0 commit comments