Compare commits

..

No commits in common. "fb4c79c5c649dd238e9451b49fa77b9cb3ffba21" and "a8b32a16f48233685fb7c9e6ee6651b41a884379" have entirely different histories.

37 changed files with 71 additions and 1007 deletions

1
.gitignore vendored
View file

@ -5,4 +5,3 @@ utils/delme.py
*.pyc *.pyc
.env .env
/venv/ /venv/
/.venv/

View file

@ -1,12 +1,8 @@
FROM base:debian13 FROM debian:13
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
WORKDIR /app WORKDIR /app
#RUN date RUN apt update
#RUN apt update RUN apt install gnupg2 netcat-traditional libssl-dev libcrypto++-dev lsb-release python3-pip nano htop iputils-ping -y
#RUN apt install gnupg2 netcat-traditional libssl-dev libcrypto++-dev lsb-release python3-pip -y COPY requirements.txt /app/requirements.txt
#RUN apt install binutils libproj-dev gdal-bin -y RUN pip3 install --break-system-packages -r requirements.txt
#COPY requirements.txt /app/requirements.txt
#RUN pip3 install --break-system-packages -r requirements.txt
#RUN pip3 install --break-system-packages django-admin-rangefilter==0.13.5
#RUN pip3 install --break-system-packages setuptools
#ENTRYPOINT ["./run.sh"] #ENTRYPOINT ["./run.sh"]

View file

@ -1,107 +1,15 @@
from django.contrib import admin from django.contrib import admin
from .models import Transaction, Account, Wallet from .models import Transaction, Account, Wallet
from django.http import HttpResponse
import io
import xlsxwriter
import jdatetime
from django.utils import timezone
from rangefilter.filters import DateTimeRangeFilter
# from utils.accounts_client import get_user_detailed_info
@admin.action(description="export excel")
def export_excel(modeladmin, request, queryset):
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output)
worksheet = workbook.add_worksheet()
for col, header in enumerate(['uuid',
'application',
'amount',
'jalali_datetime',
'payer',
'payer_type',
'payee',
'payee_type',
'state',
'created_at',
'updated_at',
'details',
]):
worksheet.write(0, col, header)
for row, tranaction in enumerate(queryset, 1):
worksheet.write(row, 0, str(tranaction.uuid))
worksheet.write(row, 1, str(tranaction.application))
worksheet.write(row, 2, tranaction.amount)
worksheet.write(row, 3, str(tranaction.jalali_datetime))
worksheet.write(row, 4, str(tranaction.payer))
worksheet.write(row, 5, str(tranaction.payer_type))
worksheet.write(row, 6, str(tranaction.payee))
worksheet.write(row, 7, str(tranaction.payee_type))
worksheet.write(row, 8, str(tranaction.state))
worksheet.write(row, 9, str(tranaction.created_at))
worksheet.write(row, 10, str(tranaction.updated_at))
worksheet.write(row, 11, str(tranaction.details))
workbook.close()
# create a response
response = HttpResponse(content_type='application/vnd.ms-excel')
# tell the browser what the file is named
file_name = jdatetime.date.fromgregorian(date=timezone.now(), locale='fa_IR').strftime("%Y/%m/%d")
response['Content-Disposition'] = f'attachment;filename="{file_name}.xlsx"'
# put the spreadsheet data into the response
response.write(output.getvalue())
# return the response
return response
@admin.action(description="export excel")
def export_accounts_as_excel(modeladmin, request, queryset):
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output)
worksheet = workbook.add_worksheet()
for col, header in enumerate(['uuid',
'owner_uuid',
'owner_type',
'wallet',
'balance',
]):
worksheet.write(0, col, header)
for row, account in enumerate(queryset, 1):
worksheet.write(row, 0, str(account.uuid))
worksheet.write(row, 1, str(account.owner_uuid))
worksheet.write(row, 2, account.owner_type)
worksheet.write(row, 3, str(account.wallet))
worksheet.write(row, 4, str(account.balance))
workbook.close()
# create a response
response = HttpResponse(content_type='application/vnd.ms-excel')
# tell the browser what the file is named
file_name = jdatetime.date.fromgregorian(date=timezone.now(), locale='fa_IR').strftime("%Y/%m/%d")
response['Content-Disposition'] = f'attachment;filename="{file_name}.xlsx"'
# put the spreadsheet data into the response
response.write(output.getvalue())
# return the response
return response
class TransactionAdmin(admin.ModelAdmin): class TransactionAdmin(admin.ModelAdmin):
list_display = ['uuid', 'application', 'splited_amount', 'jalali_datetime', 'payer', 'payer_type', 'payee', 'payee_type', 'state', 'amount', 'created_at', 'updated_at'] list_display = ['uuid', 'amount', 'created_at', 'updated_at', 'payer', 'payer_type', 'payee', 'payee_type', 'state', 'application']
search_fields = ['uuid', 'created_at', 'updated_at', 'payer_account__owner_uuid', 'payee_account__owner_uuid', 'application__uuid'] search_fields = ['uuid', 'created_at', 'updated_at', 'payer_account__owner_uuid', 'payee_account__owner_uuid', 'application__uuid']
list_filter = ['application', 'state', ('created_at', DateTimeRangeFilter), ('updated_at', DateTimeRangeFilter),] list_filter = ['state']
actions = [export_excel]
class AccountAdmin(admin.ModelAdmin): class AccountAdmin(admin.ModelAdmin):
list_display = ['uuid', 'owner_uuid', 'wallet', 'owner_type', 'balance', 'get_name'] list_display = ['uuid', 'owner_uuid', 'wallet', 'owner_type', 'balance', 'get_name']
search_fields = ['uuid', 'owner_uuid'] search_fields = ['uuid', 'owner_uuid']
list_filter = ['wallet__label', 'owner_type'] list_filter = ['wallet__label', 'owner_type']
actions = [export_accounts_as_excel]
admin.site.register(Transaction, TransactionAdmin) admin.site.register(Transaction, TransactionAdmin)
admin.site.register(Account, AccountAdmin) admin.site.register(Account, AccountAdmin)

View file

@ -9,7 +9,8 @@ from django.db.models import F
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import jdatetime from collections import defaultdict
from django.db.models import Count, Sum
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from django.conf import settings from django.conf import settings
@ -181,27 +182,6 @@ class Transaction(BaseModel):
# unique_together = ('application', 'detail_id',) # unique_together = ('application', 'detail_id',)
@property
def jalali_datetime(self):
def to_jalali(value):
if value:
return jdatetime.datetime.fromgregorian(date=value, locale='fa_IR').strftime("%Y/%m/%d-%H:%M:%S")
else:
return value
return to_jalali(self.created_at)
@property
def splited_amount(self):
def amount_split_3digit(value):
text = str(value)[::-1]
return ','.join([l[::-1] for l in [text[i:i + 3] for i in range(0, len(text), 3)][::-1]])
if self.amount:
amount_str = amount_split_3digit(str(self.amount))
return amount_str
return
def get_payer(self): def get_payer(self):
return self.payer_account.get_owner() return self.payer_account.get_owner()

View file

@ -1 +0,0 @@
"""Contains endpoint functions for accessing the API"""

View file

@ -1 +0,0 @@
"""Contains endpoint functions for accessing the API"""

View file

@ -1,151 +0,0 @@
from http import HTTPStatus
from typing import Any, Optional, Union
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.user_inquiry import UserInquiry
from ...types import Response
def _get_kwargs(
*,
body: UserInquiry,
) -> dict[str, Any]:
headers: dict[str, Any] = {}
_kwargs: dict[str, Any] = {
"method": "post",
"url": "/users/api/inquiry/",
}
_body = body.to_dict()
_kwargs["json"] = _body
headers["Content-Type"] = "application/json"
_kwargs["headers"] = headers
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[UserInquiry]:
if response.status_code == 201:
response_201 = UserInquiry.from_dict(response.json())
return response_201
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[UserInquiry]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
*,
client: AuthenticatedClient,
body: UserInquiry,
) -> Response[UserInquiry]:
"""
Args:
body (UserInquiry):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[UserInquiry]
"""
kwargs = _get_kwargs(
body=body,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
*,
client: AuthenticatedClient,
body: UserInquiry,
) -> Optional[UserInquiry]:
"""
Args:
body (UserInquiry):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
UserInquiry
"""
return sync_detailed(
client=client,
body=body,
).parsed
async def asyncio_detailed(
*,
client: AuthenticatedClient,
body: UserInquiry,
) -> Response[UserInquiry]:
"""
Args:
body (UserInquiry):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[UserInquiry]
"""
kwargs = _get_kwargs(
body=body,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
*,
client: AuthenticatedClient,
body: UserInquiry,
) -> Optional[UserInquiry]:
"""
Args:
body (UserInquiry):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
UserInquiry
"""
return (
await asyncio_detailed(
client=client,
body=body,
)
).parsed

View file

@ -1,143 +0,0 @@
from http import HTTPStatus
from typing import Any, Optional, Union
from uuid import UUID
import httpx
from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.account import Account
from ...types import Response
def _get_kwargs(
uuid: UUID,
) -> dict[str, Any]:
_kwargs: dict[str, Any] = {
"method": "get",
"url": f"/users/api/users/{uuid}/details",
}
return _kwargs
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Account]:
if response.status_code == 200:
response_200 = Account.from_dict(response.json())
return response_200
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Account]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)
def sync_detailed(
uuid: UUID,
*,
client: AuthenticatedClient,
) -> Response[Account]:
"""
Args:
uuid (UUID):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Account]
"""
kwargs = _get_kwargs(
uuid=uuid,
)
response = client.get_httpx_client().request(
**kwargs,
)
return _build_response(client=client, response=response)
def sync(
uuid: UUID,
*,
client: AuthenticatedClient,
) -> Optional[Account]:
"""
Args:
uuid (UUID):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Account
"""
return sync_detailed(
uuid=uuid,
client=client,
).parsed
async def asyncio_detailed(
uuid: UUID,
*,
client: AuthenticatedClient,
) -> Response[Account]:
"""
Args:
uuid (UUID):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Response[Account]
"""
kwargs = _get_kwargs(
uuid=uuid,
)
response = await client.get_async_httpx_client().request(**kwargs)
return _build_response(client=client, response=response)
async def asyncio(
uuid: UUID,
*,
client: AuthenticatedClient,
) -> Optional[Account]:
"""
Args:
uuid (UUID):
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.
Returns:
Account
"""
return (
await asyncio_detailed(
uuid=uuid,
client=client,
)
).parsed

View file

@ -2,10 +2,7 @@
from .account import Account from .account import Account
from .application import Application from .application import Application
from .blank_enum import BlankEnum
from .change_password import ChangePassword from .change_password import ChangePassword
from .education_degree_enum import EducationDegreeEnum
from .gender_enum import GenderEnum
from .paginated_application_list import PaginatedApplicationList from .paginated_application_list import PaginatedApplicationList
from .paginated_public_user_list import PaginatedPublicUserList from .paginated_public_user_list import PaginatedPublicUserList
from .patched_account import PatchedAccount from .patched_account import PatchedAccount
@ -13,15 +10,11 @@ from .patched_change_password import PatchedChangePassword
from .public_user import PublicUser from .public_user import PublicUser
from .request_otp import RequestOTP from .request_otp import RequestOTP
from .request_ott import RequestOTT from .request_ott import RequestOTT
from .user_inquiry import UserInquiry
__all__ = ( __all__ = (
"Account", "Account",
"Application", "Application",
"BlankEnum",
"ChangePassword", "ChangePassword",
"EducationDegreeEnum",
"GenderEnum",
"PaginatedApplicationList", "PaginatedApplicationList",
"PaginatedPublicUserList", "PaginatedPublicUserList",
"PatchedAccount", "PatchedAccount",
@ -29,5 +22,4 @@ __all__ = (
"PublicUser", "PublicUser",
"RequestOTP", "RequestOTP",
"RequestOTT", "RequestOTT",
"UserInquiry",
) )

View file

@ -1,15 +1,9 @@
import datetime
from collections.abc import Mapping
from typing import Any, TypeVar, Union, cast from typing import Any, TypeVar, Union, cast
from uuid import UUID from uuid import UUID
from attrs import define as _attrs_define from attrs import define as _attrs_define
from attrs import field as _attrs_field from attrs import field as _attrs_field
from dateutil.parser import isoparse
from ..models.blank_enum import BlankEnum
from ..models.education_degree_enum import EducationDegreeEnum
from ..models.gender_enum import GenderEnum
from ..types import UNSET, Unset from ..types import UNSET, Unset
T = TypeVar("T", bound="Account") T = TypeVar("T", bound="Account")
@ -30,10 +24,6 @@ class Account:
last_name (Union[Unset, str]): last_name (Union[Unset, str]):
name (Union[Unset, str]): name (Union[Unset, str]):
avatar (Union[None, Unset, str]): avatar (Union[None, Unset, str]):
gender (Union[BlankEnum, GenderEnum, None, Unset]):
education_degree (Union[BlankEnum, EducationDegreeEnum, None, Unset]):
education_field (Union[None, Unset, str]):
birthdate (Union[None, Unset, datetime.date]):
iban (Union[None, Unset, str]): iban (Union[None, Unset, str]):
province (Union[None, Unset, int]): province (Union[None, Unset, int]):
city (Union[None, Unset, int]): city (Union[None, Unset, int]):
@ -52,10 +42,6 @@ class Account:
last_name: Union[Unset, str] = UNSET last_name: Union[Unset, str] = UNSET
name: Union[Unset, str] = UNSET name: Union[Unset, str] = UNSET
avatar: Union[None, Unset, str] = UNSET avatar: Union[None, Unset, str] = UNSET
gender: Union[BlankEnum, GenderEnum, None, Unset] = UNSET
education_degree: Union[BlankEnum, EducationDegreeEnum, None, Unset] = UNSET
education_field: Union[None, Unset, str] = UNSET
birthdate: Union[None, Unset, datetime.date] = UNSET
iban: Union[None, Unset, str] = UNSET iban: Union[None, Unset, str] = UNSET
province: Union[None, Unset, int] = UNSET province: Union[None, Unset, int] = UNSET
city: Union[None, Unset, int] = UNSET city: Union[None, Unset, int] = UNSET
@ -95,40 +81,6 @@ class Account:
else: else:
avatar = self.avatar avatar = self.avatar
gender: Union[None, Unset, str]
if isinstance(self.gender, Unset):
gender = UNSET
elif isinstance(self.gender, GenderEnum):
gender = self.gender.value
elif isinstance(self.gender, BlankEnum):
gender = self.gender.value
else:
gender = self.gender
education_degree: Union[None, Unset, str]
if isinstance(self.education_degree, Unset):
education_degree = UNSET
elif isinstance(self.education_degree, EducationDegreeEnum):
education_degree = self.education_degree.value
elif isinstance(self.education_degree, BlankEnum):
education_degree = self.education_degree.value
else:
education_degree = self.education_degree
education_field: Union[None, Unset, str]
if isinstance(self.education_field, Unset):
education_field = UNSET
else:
education_field = self.education_field
birthdate: Union[None, Unset, str]
if isinstance(self.birthdate, Unset):
birthdate = UNSET
elif isinstance(self.birthdate, datetime.date):
birthdate = self.birthdate.isoformat()
else:
birthdate = self.birthdate
iban: Union[None, Unset, str] iban: Union[None, Unset, str]
if isinstance(self.iban, Unset): if isinstance(self.iban, Unset):
iban = UNSET iban = UNSET
@ -181,14 +133,6 @@ class Account:
field_dict["name"] = name field_dict["name"] = name
if avatar is not UNSET: if avatar is not UNSET:
field_dict["avatar"] = avatar field_dict["avatar"] = avatar
if gender is not UNSET:
field_dict["gender"] = gender
if education_degree is not UNSET:
field_dict["education_degree"] = education_degree
if education_field is not UNSET:
field_dict["education_field"] = education_field
if birthdate is not UNSET:
field_dict["birthdate"] = birthdate
if iban is not UNSET: if iban is not UNSET:
field_dict["iban"] = iban field_dict["iban"] = iban
if province is not UNSET: if province is not UNSET:
@ -203,8 +147,8 @@ class Account:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = dict(src_dict) d = src_dict.copy()
uuid = UUID(d.pop("uuid")) uuid = UUID(d.pop("uuid"))
email = d.pop("email") email = d.pop("email")
@ -246,82 +190,6 @@ class Account:
avatar = _parse_avatar(d.pop("avatar", UNSET)) avatar = _parse_avatar(d.pop("avatar", UNSET))
def _parse_gender(data: object) -> Union[BlankEnum, GenderEnum, None, Unset]:
if data is None:
return data
if isinstance(data, Unset):
return data
try:
if not isinstance(data, str):
raise TypeError()
gender_type_0 = GenderEnum(data)
return gender_type_0
except: # noqa: E722
pass
try:
if not isinstance(data, str):
raise TypeError()
gender_type_1 = BlankEnum(data)
return gender_type_1
except: # noqa: E722
pass
return cast(Union[BlankEnum, GenderEnum, None, Unset], data)
gender = _parse_gender(d.pop("gender", UNSET))
def _parse_education_degree(data: object) -> Union[BlankEnum, EducationDegreeEnum, None, Unset]:
if data is None:
return data
if isinstance(data, Unset):
return data
try:
if not isinstance(data, str):
raise TypeError()
education_degree_type_0 = EducationDegreeEnum(data)
return education_degree_type_0
except: # noqa: E722
pass
try:
if not isinstance(data, str):
raise TypeError()
education_degree_type_1 = BlankEnum(data)
return education_degree_type_1
except: # noqa: E722
pass
return cast(Union[BlankEnum, EducationDegreeEnum, None, Unset], data)
education_degree = _parse_education_degree(d.pop("education_degree", UNSET))
def _parse_education_field(data: object) -> Union[None, Unset, str]:
if data is None:
return data
if isinstance(data, Unset):
return data
return cast(Union[None, Unset, str], data)
education_field = _parse_education_field(d.pop("education_field", UNSET))
def _parse_birthdate(data: object) -> Union[None, Unset, datetime.date]:
if data is None:
return data
if isinstance(data, Unset):
return data
try:
if not isinstance(data, str):
raise TypeError()
birthdate_type_0 = isoparse(data).date()
return birthdate_type_0
except: # noqa: E722
pass
return cast(Union[None, Unset, datetime.date], data)
birthdate = _parse_birthdate(d.pop("birthdate", UNSET))
def _parse_iban(data: object) -> Union[None, Unset, str]: def _parse_iban(data: object) -> Union[None, Unset, str]:
if data is None: if data is None:
return data return data
@ -379,10 +247,6 @@ class Account:
last_name=last_name, last_name=last_name,
name=name, name=name,
avatar=avatar, avatar=avatar,
gender=gender,
education_degree=education_degree,
education_field=education_field,
birthdate=birthdate,
iban=iban, iban=iban,
province=province, province=province,
city=city, city=city,

View file

@ -1,5 +1,4 @@
from collections.abc import Mapping from typing import Any, TypeVar
from typing import Any, TypeVar, Union, cast
from uuid import UUID from uuid import UUID
from attrs import define as _attrs_define from attrs import define as _attrs_define
@ -14,7 +13,6 @@ class Application:
Attributes: Attributes:
uuid (UUID): uuid (UUID):
name (str): name (str):
avatar (Union[None, str]):
client_id (str): client_id (str):
user (UUID): user (UUID):
allowed_scope (str): allowed_scope (str):
@ -22,7 +20,6 @@ class Application:
uuid: UUID uuid: UUID
name: str name: str
avatar: Union[None, str]
client_id: str client_id: str
user: UUID user: UUID
allowed_scope: str allowed_scope: str
@ -33,9 +30,6 @@ class Application:
name = self.name name = self.name
avatar: Union[None, str]
avatar = self.avatar
client_id = self.client_id client_id = self.client_id
user = str(self.user) user = str(self.user)
@ -48,7 +42,6 @@ class Application:
{ {
"uuid": uuid, "uuid": uuid,
"name": name, "name": name,
"avatar": avatar,
"client_id": client_id, "client_id": client_id,
"user": user, "user": user,
"allowed_scope": allowed_scope, "allowed_scope": allowed_scope,
@ -58,19 +51,12 @@ class Application:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = dict(src_dict) d = src_dict.copy()
uuid = UUID(d.pop("uuid")) uuid = UUID(d.pop("uuid"))
name = d.pop("name") name = d.pop("name")
def _parse_avatar(data: object) -> Union[None, str]:
if data is None:
return data
return cast(Union[None, str], data)
avatar = _parse_avatar(d.pop("avatar"))
client_id = d.pop("client_id") client_id = d.pop("client_id")
user = UUID(d.pop("user")) user = UUID(d.pop("user"))
@ -80,7 +66,6 @@ class Application:
application = cls( application = cls(
uuid=uuid, uuid=uuid,
name=name, name=name,
avatar=avatar,
client_id=client_id, client_id=client_id,
user=user, user=user,
allowed_scope=allowed_scope, allowed_scope=allowed_scope,

View file

@ -1,8 +0,0 @@
from enum import Enum
class BlankEnum(str, Enum):
VALUE_0 = ""
def __str__(self) -> str:
return str(self.value)

View file

@ -1,4 +1,3 @@
from collections.abc import Mapping
from typing import Any, TypeVar, Union from typing import Any, TypeVar, Union
from attrs import define as _attrs_define from attrs import define as _attrs_define
@ -44,8 +43,8 @@ class ChangePassword:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = dict(src_dict) d = src_dict.copy()
old_password = d.pop("old_password") old_password = d.pop("old_password")
new_password = d.pop("new_password") new_password = d.pop("new_password")

View file

@ -1,14 +0,0 @@
from enum import Enum
class EducationDegreeEnum(str, Enum):
ASSOCIATE = "associate"
BACHELORS = "bachelors"
DOCTORATE = "doctorate"
HIGH = "high"
MASTERS = "masters"
MIDDLE = "middle"
PRIMARY = "primary"
def __str__(self) -> str:
return str(self.value)

View file

@ -1,9 +0,0 @@
from enum import Enum
class GenderEnum(str, Enum):
F = "f"
M = "m"
def __str__(self) -> str:
return str(self.value)

View file

@ -1,4 +1,3 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from typing import TYPE_CHECKING, Any, TypeVar, Union, cast
from attrs import define as _attrs_define from attrs import define as _attrs_define
@ -65,10 +64,10 @@ class PaginatedApplicationList:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
from ..models.application import Application from ..models.application import Application
d = dict(src_dict) d = src_dict.copy()
count = d.pop("count") count = d.pop("count")
results = [] results = []

View file

@ -1,4 +1,3 @@
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from typing import TYPE_CHECKING, Any, TypeVar, Union, cast
from attrs import define as _attrs_define from attrs import define as _attrs_define
@ -65,10 +64,10 @@ class PaginatedPublicUserList:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
from ..models.public_user import PublicUser from ..models.public_user import PublicUser
d = dict(src_dict) d = src_dict.copy()
count = d.pop("count") count = d.pop("count")
results = [] results = []

View file

@ -1,15 +1,9 @@
import datetime
from collections.abc import Mapping
from typing import Any, TypeVar, Union, cast from typing import Any, TypeVar, Union, cast
from uuid import UUID from uuid import UUID
from attrs import define as _attrs_define from attrs import define as _attrs_define
from attrs import field as _attrs_field from attrs import field as _attrs_field
from dateutil.parser import isoparse
from ..models.blank_enum import BlankEnum
from ..models.education_degree_enum import EducationDegreeEnum
from ..models.gender_enum import GenderEnum
from ..types import UNSET, Unset from ..types import UNSET, Unset
T = TypeVar("T", bound="PatchedAccount") T = TypeVar("T", bound="PatchedAccount")
@ -27,10 +21,6 @@ class PatchedAccount:
last_name (Union[Unset, str]): last_name (Union[Unset, str]):
name (Union[Unset, str]): name (Union[Unset, str]):
avatar (Union[None, Unset, str]): avatar (Union[None, Unset, str]):
gender (Union[BlankEnum, GenderEnum, None, Unset]):
education_degree (Union[BlankEnum, EducationDegreeEnum, None, Unset]):
education_field (Union[None, Unset, str]):
birthdate (Union[None, Unset, datetime.date]):
iban (Union[None, Unset, str]): iban (Union[None, Unset, str]):
iban_verified (Union[None, Unset, bool]): iban_verified (Union[None, Unset, bool]):
province (Union[None, Unset, int]): province (Union[None, Unset, int]):
@ -49,10 +39,6 @@ class PatchedAccount:
last_name: Union[Unset, str] = UNSET last_name: Union[Unset, str] = UNSET
name: Union[Unset, str] = UNSET name: Union[Unset, str] = UNSET
avatar: Union[None, Unset, str] = UNSET avatar: Union[None, Unset, str] = UNSET
gender: Union[BlankEnum, GenderEnum, None, Unset] = UNSET
education_degree: Union[BlankEnum, EducationDegreeEnum, None, Unset] = UNSET
education_field: Union[None, Unset, str] = UNSET
birthdate: Union[None, Unset, datetime.date] = UNSET
iban: Union[None, Unset, str] = UNSET iban: Union[None, Unset, str] = UNSET
iban_verified: Union[None, Unset, bool] = UNSET iban_verified: Union[None, Unset, bool] = UNSET
province: Union[None, Unset, int] = UNSET province: Union[None, Unset, int] = UNSET
@ -90,40 +76,6 @@ class PatchedAccount:
else: else:
avatar = self.avatar avatar = self.avatar
gender: Union[None, Unset, str]
if isinstance(self.gender, Unset):
gender = UNSET
elif isinstance(self.gender, GenderEnum):
gender = self.gender.value
elif isinstance(self.gender, BlankEnum):
gender = self.gender.value
else:
gender = self.gender
education_degree: Union[None, Unset, str]
if isinstance(self.education_degree, Unset):
education_degree = UNSET
elif isinstance(self.education_degree, EducationDegreeEnum):
education_degree = self.education_degree.value
elif isinstance(self.education_degree, BlankEnum):
education_degree = self.education_degree.value
else:
education_degree = self.education_degree
education_field: Union[None, Unset, str]
if isinstance(self.education_field, Unset):
education_field = UNSET
else:
education_field = self.education_field
birthdate: Union[None, Unset, str]
if isinstance(self.birthdate, Unset):
birthdate = UNSET
elif isinstance(self.birthdate, datetime.date):
birthdate = self.birthdate.isoformat()
else:
birthdate = self.birthdate
iban: Union[None, Unset, str] iban: Union[None, Unset, str]
if isinstance(self.iban, Unset): if isinstance(self.iban, Unset):
iban = UNSET iban = UNSET
@ -183,14 +135,6 @@ class PatchedAccount:
field_dict["name"] = name field_dict["name"] = name
if avatar is not UNSET: if avatar is not UNSET:
field_dict["avatar"] = avatar field_dict["avatar"] = avatar
if gender is not UNSET:
field_dict["gender"] = gender
if education_degree is not UNSET:
field_dict["education_degree"] = education_degree
if education_field is not UNSET:
field_dict["education_field"] = education_field
if birthdate is not UNSET:
field_dict["birthdate"] = birthdate
if iban is not UNSET: if iban is not UNSET:
field_dict["iban"] = iban field_dict["iban"] = iban
if iban_verified is not UNSET: if iban_verified is not UNSET:
@ -211,8 +155,8 @@ class PatchedAccount:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = dict(src_dict) d = src_dict.copy()
_uuid = d.pop("uuid", UNSET) _uuid = d.pop("uuid", UNSET)
uuid: Union[Unset, UUID] uuid: Union[Unset, UUID]
if isinstance(_uuid, Unset): if isinstance(_uuid, Unset):
@ -248,82 +192,6 @@ class PatchedAccount:
avatar = _parse_avatar(d.pop("avatar", UNSET)) avatar = _parse_avatar(d.pop("avatar", UNSET))
def _parse_gender(data: object) -> Union[BlankEnum, GenderEnum, None, Unset]:
if data is None:
return data
if isinstance(data, Unset):
return data
try:
if not isinstance(data, str):
raise TypeError()
gender_type_0 = GenderEnum(data)
return gender_type_0
except: # noqa: E722
pass
try:
if not isinstance(data, str):
raise TypeError()
gender_type_1 = BlankEnum(data)
return gender_type_1
except: # noqa: E722
pass
return cast(Union[BlankEnum, GenderEnum, None, Unset], data)
gender = _parse_gender(d.pop("gender", UNSET))
def _parse_education_degree(data: object) -> Union[BlankEnum, EducationDegreeEnum, None, Unset]:
if data is None:
return data
if isinstance(data, Unset):
return data
try:
if not isinstance(data, str):
raise TypeError()
education_degree_type_0 = EducationDegreeEnum(data)
return education_degree_type_0
except: # noqa: E722
pass
try:
if not isinstance(data, str):
raise TypeError()
education_degree_type_1 = BlankEnum(data)
return education_degree_type_1
except: # noqa: E722
pass
return cast(Union[BlankEnum, EducationDegreeEnum, None, Unset], data)
education_degree = _parse_education_degree(d.pop("education_degree", UNSET))
def _parse_education_field(data: object) -> Union[None, Unset, str]:
if data is None:
return data
if isinstance(data, Unset):
return data
return cast(Union[None, Unset, str], data)
education_field = _parse_education_field(d.pop("education_field", UNSET))
def _parse_birthdate(data: object) -> Union[None, Unset, datetime.date]:
if data is None:
return data
if isinstance(data, Unset):
return data
try:
if not isinstance(data, str):
raise TypeError()
birthdate_type_0 = isoparse(data).date()
return birthdate_type_0
except: # noqa: E722
pass
return cast(Union[None, Unset, datetime.date], data)
birthdate = _parse_birthdate(d.pop("birthdate", UNSET))
def _parse_iban(data: object) -> Union[None, Unset, str]: def _parse_iban(data: object) -> Union[None, Unset, str]:
if data is None: if data is None:
return data return data
@ -391,10 +259,6 @@ class PatchedAccount:
last_name=last_name, last_name=last_name,
name=name, name=name,
avatar=avatar, avatar=avatar,
gender=gender,
education_degree=education_degree,
education_field=education_field,
birthdate=birthdate,
iban=iban, iban=iban,
iban_verified=iban_verified, iban_verified=iban_verified,
province=province, province=province,

View file

@ -1,4 +1,3 @@
from collections.abc import Mapping
from typing import Any, TypeVar, Union from typing import Any, TypeVar, Union
from attrs import define as _attrs_define from attrs import define as _attrs_define
@ -43,8 +42,8 @@ class PatchedChangePassword:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = dict(src_dict) d = src_dict.copy()
old_password = d.pop("old_password", UNSET) old_password = d.pop("old_password", UNSET)
old_password_field = d.pop("old_password_field", UNSET) old_password_field = d.pop("old_password_field", UNSET)

View file

@ -1,4 +1,3 @@
from collections.abc import Mapping
from typing import Any, TypeVar, Union, cast from typing import Any, TypeVar, Union, cast
from uuid import UUID from uuid import UUID
@ -59,8 +58,8 @@ class PublicUser:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = dict(src_dict) d = src_dict.copy()
def _parse_avatar(data: object) -> Union[None, str]: def _parse_avatar(data: object) -> Union[None, str]:
if data is None: if data is None:

View file

@ -1,5 +1,4 @@
import datetime import datetime
from collections.abc import Mapping
from typing import Any, TypeVar, Union, cast from typing import Any, TypeVar, Union, cast
from attrs import define as _attrs_define from attrs import define as _attrs_define
@ -47,8 +46,8 @@ class RequestOTP:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = dict(src_dict) d = src_dict.copy()
phone_number = d.pop("phone_number") phone_number = d.pop("phone_number")
def _parse_otp_expire(data: object) -> Union[None, datetime.datetime]: def _parse_otp_expire(data: object) -> Union[None, datetime.datetime]:

View file

@ -1,5 +1,4 @@
import datetime import datetime
from collections.abc import Mapping
from typing import Any, TypeVar, Union, cast from typing import Any, TypeVar, Union, cast
from attrs import define as _attrs_define from attrs import define as _attrs_define
@ -48,8 +47,8 @@ class RequestOTT:
return field_dict return field_dict
@classmethod @classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
d = dict(src_dict) d = src_dict.copy()
application = d.pop("application") application = d.pop("application")
def _parse_ott(data: object) -> Union[None, str]: def _parse_ott(data: object) -> Union[None, str]:

View file

@ -1,68 +0,0 @@
from collections.abc import Mapping
from typing import Any, TypeVar
from uuid import UUID
from attrs import define as _attrs_define
from attrs import field as _attrs_field
T = TypeVar("T", bound="UserInquiry")
@_attrs_define
class UserInquiry:
"""
Attributes:
uuid (UUID):
phone_number (str):
"""
uuid: UUID
phone_number: str
additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
def to_dict(self) -> dict[str, Any]:
uuid = str(self.uuid)
phone_number = self.phone_number
field_dict: dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"uuid": uuid,
"phone_number": phone_number,
}
)
return field_dict
@classmethod
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
d = dict(src_dict)
uuid = UUID(d.pop("uuid"))
phone_number = d.pop("phone_number")
user_inquiry = cls(
uuid=uuid,
phone_number=phone_number,
)
user_inquiry.additional_properties = d
return user_inquiry
@property
def additional_keys(self) -> list[str]:
return list(self.additional_properties.keys())
def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]
def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value
def __delitem__(self, key: str) -> None:
del self.additional_properties[key]
def __contains__(self, key: str) -> bool:
return key in self.additional_properties

View file

@ -1,141 +0,0 @@
import os
def get_settings(key):
from django.conf import settings
return getattr(settings, 'BASE_DIR')
BASE_DIR = get_settings('BASE_DIR')
LOG_DIR = BASE_DIR / 'log'
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{asctime} [{levelname}] {name} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'standard': {
'format': '{asctime} [{levelname}] {name}: {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
},
'handlers': {
# هندلر برای نوشتن در فایل
'file': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'accounts.log'),
# 'maxBytes': 1024 * 1024 * 10, # 10 MB
'when': 'midnight',
'interval': 5,
'backupCount': 30,
'formatter': 'verbose',
'encoding': 'utf-8',
},
# هندلر برای خطاها در فایل جداگانه
'error_file': {
'level': 'ERROR',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'errors.log'),
# 'maxBytes': 1024 * 1024 * 10,
'when': 'midnight',
'interval': 5,
'backupCount': 30,
'formatter': 'verbose',
'encoding': 'utf-8',
},
# هندلر برای کنسول (فقط در حالت DEBUG)
'console': {
'level': 'DEBUG',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple',
},
# هندلر برای کنسول همیشه فعال (حتی در production)
'console_always': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'standard',
},
},
'loggers': {
# لاگر ریشه
'': { # empty string = root logger
'handlers': ['console', 'file', 'error_file'],
'level': 'INFO',
'propagate': True,
},
# لاگر اختصاصی برای Django
'django': {
'handlers': ['console', 'file'],
'level': 'INFO',
'propagate': False,
},
# لاگر اختصاصی برای درخواست‌ها
'django.request': {
'handlers': ['file', 'error_file', 'console'],
'level': 'ERROR',
'propagate': False,
},
# # لاگر اختصاصی برای برنامه خودتان
# 'root': {
# 'handlers': ['console', 'file', 'error_file'],
# 'level': 'DEBUG',
# 'propagate': False,
# },
},
}
# LOKI_BASE_PUBLIC_URL = config('LOKI_BASE_PUBLIC_URL', default=None, cast=str)
# {
# 'version': 1,
# 'disable_existing_loggers': False,
# 'formatters': {
# 'loki': {
# 'class': 'utils.logs.LokiFormatter', # required
# },
# },
#
# 'handlers': {
# 'loki': {
# 'level': 'DEBUG', # Log level. Required
# 'class': 'utils.logs.LokiHandler', # Required
# 'formatter': 'loki', # Loki formatter. Required
# 'timeout': 2, # Post request timeout, default is 0.5. Optional
# 'url': f'{LOKI_BASE_PUBLIC_URL}/loki/api/v1/push', # Loki url. Defaults to localhost. Optional.
# # 'auth': ("user", "password"), # Basic auth to authenticate with loki. Default is None (i.e. no auth). Optional
# 'tags': {"app": "accounts"}, # Tags / Labels to attach to the log. Optional, but strongly encoraged to use.
# 'mode': 'thread',
# # Push mode. Can be 'sync' or 'thread'. Sync is blocking, thread is non-blocking. Defaults to sync. Optional.
# },
# 'console': {
# 'level': 'DEBUG',
# 'class': 'logging.StreamHandler',
# # 'formatter': 'verbose',
# },
# },
# 'loggers': {
# '': {
# 'handlers': ['console', 'loki'],
# 'level': 'INFO',
# 'propagate': True,
# },
# },
# }

View file

@ -5,7 +5,7 @@ import sys
def main(): def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wallet.settings')
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:

View file

@ -22,5 +22,3 @@ psycopg[binary,pool]
django-redis django-redis
httpx httpx
packaging==25.0 packaging==25.0
xlsxwriter
django-admin-rangefilter

View file

@ -179,6 +179,3 @@ zope-event==6.1
# via gevent # via gevent
zope-interface==8.2 zope-interface==8.2
# via gevent # via gevent
xlsxwriter==3.2.3
# via -r requirements.in
django-admin-rangefilter==0.13.5

2
run.sh
View file

@ -5,4 +5,4 @@ while ! nc -z $DB_HOST 5432 ; do
done done
#python3 manage.py collectstatic --noinput #python3 manage.py collectstatic --noinput
python3 manage.py migrate python3 manage.py migrate
gunicorn main.wsgi:application --bind 0.0.0.0:8000 -w 4 gunicorn wallet.wsgi:application --bind 0.0.0.0:8000 -w 4

View file

@ -8,8 +8,7 @@ from gooyal_accounts_client import AuthenticatedClient
from gooyal_accounts_client.models import PublicUser, Application from gooyal_accounts_client.models import PublicUser, Application
from gooyal_accounts_client.api.users import users_api_users_retrieve from gooyal_accounts_client.api.users import users_api_users_retrieve
from gooyal_accounts_client.api.oauth2 import oauth2_apis_applications_retrieve from gooyal_accounts_client.api.oauth2 import oauth2_apis_applications_retrieve
from gooyal_accounts_client.models import Account from gooyal_accounts_client.types import Response
from gooyal_accounts_client.api.users import (users_api_users_details_retrieve)
def login_as_client_credentials(): def login_as_client_credentials():
@ -61,13 +60,6 @@ def get_user_details(uuid):
return data return data
def get_user_detailed_info(uuid):
client = get_client()
response: Account = users_api_users_details_retrieve.sync(uuid=uuid, client=client)
print(response)
return response
def get_application_details(uuid): def get_application_details(uuid):
client = get_client() client = get_client()
data: Application = oauth2_apis_applications_retrieve.sync(client=client, id=uuid) data: Application = oauth2_apis_applications_retrieve.sync(client=client, id=uuid)

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wallet.settings')
from utils.broker import get_rpc_broker_consumer from utils.broker import get_rpc_broker_consumer

View file

@ -47,7 +47,6 @@ INSTALLED_APPS = [
'crispy_forms', 'crispy_forms',
'crispy_bootstrap5', 'crispy_bootstrap5',
'django_filters', 'django_filters',
'rangefilter',
# 'jalali_date', # 'jalali_date',
# local apps # local apps
@ -126,7 +125,7 @@ SPECTACULAR_SETTINGS = {
# https://spec.openapis.org/oas/v3.0.3#oauth-flows-object # https://spec.openapis.org/oas/v3.0.3#oauth-flows-object
} }
ROOT_URLCONF = 'main.urls' ROOT_URLCONF = 'wallet.urls'
TEMPLATES = [ TEMPLATES = [
{ {
@ -149,7 +148,7 @@ AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', 'django.contrib.auth.backends.ModelBackend',
) )
WSGI_APPLICATION = 'main.wsgi.application' WSGI_APPLICATION = 'wallet.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases # https://docs.djangoproject.com/en/5.0/ref/settings/#databases
@ -257,6 +256,39 @@ CACHES = {
LOKI_BASE_PUBLIC_URL = config('LOKI_BASE_PUBLIC_URL', default=None, cast=str) LOKI_BASE_PUBLIC_URL = config('LOKI_BASE_PUBLIC_URL', default=None, cast=str)
from main.other_settings.logging import LOGGING LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'loki': {
'class': 'utils.logs.LokiFormatter', # required
},
},
'handlers': {
'loki': {
'level': 'DEBUG', # Log level. Required
'class': 'utils.logs.LokiHandler', # Required
'formatter': 'loki', # Loki formatter. Required
'timeout': 2, # Post request timeout, default is 0.5. Optional
'url': f'{LOKI_BASE_PUBLIC_URL}/loki/api/v1/push', # Loki url. Defaults to localhost. Optional.
# 'auth': ("user", "password"), # Basic auth to authenticate with loki. Default is None (i.e. no auth). Optional
'tags': {"app": "wallet"}, # Tags / Labels to attach to the log. Optional, but strongly encoraged to use.
'mode': 'thread', # Push mode. Can be 'sync' or 'thread'. Sync is blocking, thread is non-blocking. Defaults to sync. Optional.
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
# 'formatter': 'verbose',
},
},
'loggers': {
'': {
'handlers': ['console', 'loki'],
'level': 'INFO',
'propagate': True,
},
},
}
ALLOWED_NEGATIVE_BALANCE_APPLICATIONS = config('ALLOWED_NEGATIVE_BALANCE_APPLICATIONS', cast=lambda v: [s.strip() for s in v.split(',')]) ALLOWED_NEGATIVE_BALANCE_APPLICATIONS = config('ALLOWED_NEGATIVE_BALANCE_APPLICATIONS', cast=lambda v: [s.strip() for s in v.split(',')])

View file

@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wallet.settings')
application = get_wsgi_application() application = get_wsgi_application()