Compare commits

..
Author SHA1 Message Date
Abdalhamid Alhamad a7028fa64c feat: sms using twillio 2024-12-29 09:46:02 +03:00
abdalhamid99 0750509a85 Merge pull request #21 from HamzaSha1/feat/notifications
Feat/notifications
2024-12-24 13:08:29 +03:00
Abdalhamid Alhamad 4d9ebe729e Merge branch 'mvp-1' of github.com:HamzaSha1/zod-backend into feat/notifications 2024-12-24 13:00:58 +03:00
Abdalhamid Alhamad bb8cc33d53 fix: register fcm token on enabling push notification 2024-12-24 13:00:54 +03:00
abdalhamid99 e933cacdcf Merge pull request #20 from HamzaSha1/feat/notifications
feat: working on push notifications journey
2024-12-24 12:18:32 +03:00
Abdalhamid Alhamad 3719498c2f feat: working on push notifications journey 2024-12-24 12:10:49 +03:00
abdalhamid99 c7470302bd Merge pull request #19 from HamzaSha1/feat/gifts-journey
feat: gift journeys
2024-12-22 16:47:01 +03:00
Abdalhamid Alhamad 5e9e83cb74 feat: gift journeys 2024-12-22 16:34:02 +03:00
abdalhamid99 4cef58580e Merge pull request #18 from HamzaSha1/feat/logout
Feat/logout
2024-12-22 10:55:24 +03:00
Abdalhamid Alhamad 0ba09cbf8b Merge branch 'mvp-1' of github.com:HamzaSha1/zod-backend into feat/logout 2024-12-22 10:48:03 +03:00
Abdalhamid Alhamad 28a2cb5d75 fix: fix exception in case of logout 2024-12-22 10:47:59 +03:00
abdalhamid99 4961a192ea Merge pull request #17 from HamzaSha1/feat/logout
Feat/logout
2024-12-19 16:59:16 +03:00
Abdalhamid Alhamad 8ab47f3835 Merge branch 'mvp-1' of github.com:HamzaSha1/zod-backend into feat/logout 2024-12-19 16:50:44 +03:00
Abdalhamid Alhamad 8112fb81a2 feat: logout 2024-12-19 16:50:39 +03:00
abdalhamid99 2c3c862c4a Merge pull request #16 from HamzaSha1/feat/refresh-token
feat: refresh token
2024-12-19 16:49:25 +03:00
Abdalhamid Alhamad 93f5d83825 feat: refresh token 2024-12-19 16:25:39 +03:00
abdalhamid99 ea60ac3d7b Merge pull request #15 from HamzaSha1/feat/allowance-journey
feat: allowance journey
2024-12-19 15:27:00 +03:00
Abdalhamid Alhamad 0748695f23 fix: remove comments from allowance entity 2024-12-19 15:21:44 +03:00
Abdalhamid Alhamad a201692c0c feat: allowance journey 2024-12-19 15:18:35 +03:00
abdalhamid99 fd6c1d1442 Merge pull request #14 from HamzaSha1/feat/money-requests
feat: working on money requests journrey
2024-12-18 13:17:29 +03:00
Abdalhamid Alhamad ed57ce6e91 feat: working on money requests jounrey 2024-12-18 12:57:23 +03:00
abdalhamid99 33453b193f Merge pull request #13 from HamzaSha1/feat/register-junior-via-qrcode
feat: onboard junior by qrcode
2024-12-15 17:00:42 +03:00
Abdalhamid Alhamad b0972f1a0a feat: onbard junior by qrcode 2024-12-15 16:46:49 +03:00
abdalhamid99 7437403756 Merge pull request #12 from HamzaSha1/feat/saving-goals
feat: working on saving goals journey for juniors
2024-12-15 12:53:27 +03:00
Abdalhamid Alhamad 4d2f6f57f4 feat: working on saving goals jounrey for juniors 2024-12-15 12:44:59 +03:00
Abdalhamid Alhamad 24d990592d fix: fix tasks submission journey 2024-12-12 15:22:04 +03:00
Abdalhamid Alhamad 5b7b7ff689 fix: fix oci bucket name in generating signed url 2024-12-12 13:30:11 +03:00
abdalhamid99 6fccacd085 Merge pull request #11 from HamzaSha1/feat/customer-settings
feat: update customer profile picture and notifications settings
2024-12-12 13:23:28 +03:00
Abdalhamid Alhamad 51fa61dbc6 feat: update customer profile picture and notifications settings 2024-12-12 13:15:47 +03:00
abdalhamid99 4867a5f858 Merge pull request #10 from HamzaSha1/feat/tasks-default-logo
feat: seed default task logos
2024-12-12 11:22:20 +03:00
Abdalhamid Alhamad 687b6a5c6d feat: seed default task logos 2024-12-12 11:14:38 +03:00
abdalhamid99 e6ed1772f7 Merge pull request #9 from HamzaSha1/feat/signed-urls
Feat/signed urls
2024-12-12 10:01:52 +03:00
Abdalhamid Alhamad 1f0a14fee4 fix: fix magic number lint issue 2024-12-12 09:47:49 +03:00
Abdalhamid Alhamad eb70828ae0 Merge branch 'mvp-1' of github.com:HamzaSha1/zod-backend into feat/signed-urls 2024-12-12 09:46:48 +03:00
Abdalhamid Alhamad 220a03cc46 feat: working on signed url for private files 2024-12-12 09:46:38 +03:00
Abdalhamid Alhamad 39b1e76bb5 fix: import tasks migration 2024-12-11 17:49:15 +03:00
abdalhamid99 83fc634d25 Merge pull request #8 from HamzaSha1/feat/tasks
feat: tasks journey
2024-12-11 11:14:21 +03:00
Abdalhamid Alhamad 35b434bc3d fix: fix multiple submissions 2024-12-11 11:09:55 +03:00
Abdalhamid Alhamad 749ee5457f feat: tasks jounrey 2024-12-11 10:27:51 +03:00
abdalhamid99 d539073f29 Merge pull request #7 from HamzaSha1/feat/roles-guard
feat: protecting endpoints by roles
2024-12-10 10:19:32 +03:00
Abdalhamid Alhamad 66e1bb0f28 feat: protecting endpoint by roles 2024-12-10 10:11:47 +03:00
abdalhamid99 577f91b796 Merge pull request #6 from HamzaSha1/feat/junior-theme
feat: set theme for junior users
2024-12-10 09:30:42 +03:00
Abdalhamid Alhamad 7ed37c30e1 feat: set theme for junior users 2024-12-10 09:23:30 +03:00
abdalhamid99 c2f63ccc72 Merge pull request #5 from HamzaSha1/feat/create-juniors
feat: create junior
2024-12-09 13:18:03 +03:00
Abdalhamid Alhamad 970a41c895 feat: create junior 2024-12-09 13:11:18 +03:00
abdalhamid99 3fd29b3905 Merge pull request #4 from HamzaSha1/feat/forget-password
feat:forget password
2024-12-08 13:26:18 +03:00
Abdalhamid Alhamad 7f7fef3f89 fix: fix pipeline 2024-12-08 13:21:46 +03:00
Abdalhamid Alhamad 90ee8023e6 feat:forget password 2024-12-08 13:15:38 +03:00
abdalhamid99 c486d558ad Merge pull request #3 from HamzaSha1/feat/user-registration
Feat/user registration
2024-12-05 11:50:36 +03:00
Abdalhamid Alhamad 85569af770 Merge branch 'mvp-1' of github.com:HamzaSha1/zod-backend into feat/user-registration 2024-12-05 11:43:21 +03:00
Abdalhamid Alhamad f97bb08c5c fix: handle duplicate emails 2024-12-05 11:42:56 +03:00
abdalhamid99 6f6e3f7e7b Merge pull request #2 from HamzaSha1/feat/user-registration
feat: registration journrey for parents
2024-12-05 11:34:58 +03:00
Abdalhamid Alhamad 26b2d153fd fix: fix unit test 2024-12-05 11:27:36 +03:00
Abdalhamid Alhamad 2577f2dcac feat: registration jounrey for parents 2024-12-05 11:20:50 +03:00
abdalhamid99 e4b69a406f Merge pull request #1 from HamzaSha1/feat/upload-files-to-oci-bucket
Feat/upload files to oci bucket
2024-12-02 12:27:16 +03:00
Abdalhamid Alhamad 2fdce34ffc fix: fix linting issues 2024-12-02 12:23:03 +03:00
Abdalhamid Alhamad 2bf9dea771 fix: remove access type from document 2024-12-02 12:13:03 +03:00
Abdalhamid Alhamad 847c078735 feat: upload document to oci bucket 2024-12-02 12:07:10 +03:00
abdalhamid99 14e70a63b8 Update README.md 2024-11-21 09:12:27 +03:00
Oracle Public Cloud User 05872b5170 feat:mvp1 initial commit 2024-11-21 06:07:08 +00:00
533 changed files with 28400 additions and 8153 deletions
+31
View File
@@ -0,0 +1,31 @@
NODE_ENV=dev
PORT=5000
DB_HOST=
DB_PASS=
DB_USER=postgres
DB_PORT=5432
DB_NAME=
MIGRATIONS_RUN=true
SWAGGER_API_DOCS_PATH="/api-docs"
JWT_ACCESS_TOKEN_SECRET=your_access_token_secret
JWT_ACCESS_TOKEN_EXPIRY=1d
JWT_REFRESH_TOKEN_SECRET=your_refresh_token_secret
JWT_REFRESH_TOKEN_EXPIRY=1d
USE_MOCK=true
OCI_TENANCY_ID=
OCI_USER_ID=
OCI_FINGERPRINT=
OCI_PRIVATE_KEY=
OCI_PASSPHRASE=""
OCI_NAMESPACE=
OCI_REGION=
MAIL_HOST=smtp.gmail.com
MAIL_USER=[email protected]
MAIL_PASSWORD=
MAIL_PORT=587
MAIL_FROM=UBA
+47
View File
@@ -0,0 +1,47 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'plugin:security/recommended'],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js', 'node_modules', 'dist', 'coverage', 'jest*\\.ts', 'typeorm*\\.ts'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' },
],
'@typescript-eslint/no-inferrable-types': 'off',
'func-names': ['error', 'as-needed'],
'no-underscore-dangle': ['error'],
'require-await': ['error'],
'no-console': ['error'],
'no-multi-assign': ['error'],
'no-magic-numbers': ['error', { ignoreArrayIndexes: true }],
'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 1, maxBOF: 0 }],
'max-len': [
'error',
{ code: 120, tabWidth: 2, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: true },
],
},
overrides: [
{
files: ['*spec.ts'],
rules: {
'no-magic-numbers': ['off'],
},
},
],
};
+85
View File
@@ -0,0 +1,85 @@
name: CI Zod
on:
pull_request:
branches:
- '*'
push:
branches:
- dev
- main
env:
NODE_ENV: development
TAG: dev
jobs:
install_dependencies:
name: Install Dependencies
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 22.4.1
- name: Install Dependencies
run: npm install
- name: Cache Node Modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node_modules-
run_tests:
name: Run Tests
runs-on: ubuntu-latest
needs: install_dependencies
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 22.4.1
- name: Install Dependencies
run: npm install
- name: Run Lint
run: npm run lint
- name: Run Tests with Coverage
run: npm run test:cov
build_app:
name: Build Application
runs-on: ubuntu-latest
needs: run_tests
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 22.4.1
- name: Install Dependencies
run: npm install
- name: Build Application
run: npm run build
- name: Upload Build Artifacts
uses: actions/upload-artifact@v3
with:
name: dist
path: dist/
+53 -22
View File
@@ -1,24 +1,55 @@
/static
/media
.idea/
*.pyc
media/
*.name
*.iml
# compiled output
/dist
/node_modules
/build
# Logs
logs
*.log
*.xml
*.pyo
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
.idea
venv/*
static/*
*.pem
*.sqlite3
/migrations/__pycache__/
/__pycache__/
/*.pyc
*/__pycache__/*.pyc
__pycache__/
*.env
ve/*
celerybeat-schedule
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# temp directory
.temp
.tmp
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+6
View File
@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"tabWidth": 2,
"printWidth": 120
}
+6
View File
@@ -0,0 +1,6 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}
-11
View File
@@ -1,11 +0,0 @@
FROM python:3.9
ENV PYTHONUNBUFFERED 1
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY . .
RUN apt-get update
RUN apt-get install wkhtmltopdf -y
RUN apt install -y gdal-bin python3-gdal
RUN pip install -r requirements.txt
WORKDIR /usr/src/app
+33
View File
@@ -0,0 +1,33 @@
# Base image
FROM node:22.4.1-alpine
RUN apk update && \
apk upgrade && \
apk add ca-certificates curl && \
update-ca-certificates
#saudi timezone
ENV TZ=Asia/Riyadh
# Create app directory
RUN mkdir /app && chown node:node /app
WORKDIR /app
# finally switch user to node
USER node
# A wildcard is used to ensure both package.json, package-lock.json and package-lock.json are copied
COPY --chown=node:node package*.json ./
# Install app dependencies
RUN npm install --frozen-lockfile
# Bundle app source
COPY --chown=node:node . .
# Creates a "dist" folder with the production build
RUN npm run build
# Start the server using the production build
CMD [ "node", "dist/main" ]
+99 -1
View File
@@ -1 +1,99 @@
# ZODBank-Backend
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
</p>
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Project setup
```bash
$ npm install
```
## Compile and run the project
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Run tests
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Deployment
When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information.
If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps:
```bash
$ npm install -g mau
$ mau deploy
```
With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure.
## Resources
Check out a few resources that may come in handy when working with NestJS:
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks.
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
-49
View File
@@ -1,49 +0,0 @@
"""Account admin"""
from django.contrib import admin
"""Import django app"""
from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete, UserDeviceDetails
# Register your models here.
@admin.register(UserDelete)
class UserDeleteAdmin(admin.ModelAdmin):
"""User profile admin"""
list_display = ['user', 'old_email', 'd_email']
def __str__(self):
"""Return delete user"""
return self.user
@admin.register(UserNotification)
class UserNotificationAdmin(admin.ModelAdmin):
"""User profile admin"""
list_display = ['user', 'push_notification', 'email_notification', 'sms_notification']
def __str__(self):
"""Return image url"""
return self.image_url
@admin.register(DefaultTaskImages)
class DefaultTaskImagesAdmin(admin.ModelAdmin):
"""User profile admin"""
list_display = ['task_name', 'image_url']
def __str__(self):
"""Return image url"""
return self.image_url
@admin.register(UserEmailOtp)
class UserEmailOtpAdmin(admin.ModelAdmin):
"""User Email otp admin"""
list_display = ['email']
def __str__(self):
"""Return object in email and otp format"""
return self.email + '-' + self.otp
@admin.register(UserDeviceDetails)
class UserDeviceDetailsAdmin(admin.ModelAdmin):
"""User profile admin"""
list_display = ['user', 'device_id']
def __str__(self):
"""Return user email"""
return self.user.email
-9
View File
@@ -1,9 +0,0 @@
"""Account app file"""
"""Import Django"""
from django.apps import AppConfig
class AccountConfig(AppConfig):
"""default configurations"""
default_auto_field = 'django.db.models.BigAutoField'
name = 'account'
-40
View File
@@ -1,40 +0,0 @@
"""middleware file"""
"""Django import"""
from rest_framework import status
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer
"""App django"""
from account.utils import custom_error_response
from account.models import UserDeviceDetails
from base.messages import ERROR_CODE, SUCCESS_CODE
# Custom middleware
# when user login with
# multiple device simultaneously
# It restricted login in
# multiple devices only
# user can login in single
# device at a time"""
class CustomMiddleware(object):
"""Custom middleware"""
def __init__(self, get_response):
"""response"""
self.get_response = get_response
def __call__(self, request):
# Code to be executed before the view is called
response = self.get_response(request)
# Code to be executed after the view is called
device_id = request.META.get('HTTP_DEVICE_ID')
if request.user.is_authenticated:
"""device details"""
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
if device_id and not device_details:
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND)
# Set content type header to "application/json"
response['Content-Type'] = 'application/json'
# Render the response as JSON
renderer = JSONRenderer()
response.content = renderer.render(response.data)
return response
-65
View File
@@ -1,65 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-23 12:05
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserEmailOtp',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.EmailField(max_length=254)),
('otp', models.CharField(max_length=10)),
('is_verified', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('expired_at', models.DateTimeField(blank=True, null=True)),
('is_active', models.BooleanField(default=True)),
],
options={
'db_table': 'user_email_otp',
},
),
migrations.CreateModel(
name='UserPhoneOtp',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('country_code', models.IntegerField()),
('phone', models.CharField(max_length=17)),
('otp', models.CharField(max_length=10)),
('is_verified', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('expired_at', models.DateTimeField(blank=True, null=True)),
('is_active', models.BooleanField(default=True)),
],
options={
'db_table': 'user_phone_otp',
},
),
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user_type', models.CharField(blank=True, choices=[('1', 'junior'), ('2', 'guardian'), ('3', 'superuser')], default=None, max_length=15, null=True)),
('is_verified', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('is_active', models.BooleanField(default=False)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_profile', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_profile',
},
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-29 12:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='useremailotp',
name='user_type',
field=models.CharField(blank=True, choices=[('1', 'junior'), ('2', 'guardian'), ('3', 'superuser')], default=None, max_length=15, null=True),
),
]
@@ -1,26 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-07 10:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0002_useremailotp_user_type'),
]
operations = [
migrations.CreateModel(
name='DefaultTaskImages',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('task_name', models.CharField(max_length=15)),
('image_url', models.URLField(blank=True, default=None, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'default_task_image',
},
),
]
-31
View File
@@ -1,31 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-10 09:24
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('account', '0003_defaulttaskimages'),
]
operations = [
migrations.CreateModel(
name='UserDelete',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('old_email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Original Email')),
('d_email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Dummy Email')),
('is_active', models.BooleanField(default=True)),
('reason', models.TextField(blank=True, max_length=500, null=True, verbose_name='Reason for Leaving')),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='delete_information_set', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_delete_information',
},
),
]
@@ -1,31 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-10 12:40
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('account', '0004_userdelete'),
]
operations = [
migrations.CreateModel(
name='UserNotification',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('push_notification', models.BooleanField(default=True)),
('email_notification', models.BooleanField(default=True)),
('sms_notification', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user_notification', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_notification',
},
),
]
@@ -1,21 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-11 11:26
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('account', '0005_usernotification'),
]
operations = [
migrations.AlterModelOptions(
name='useremailotp',
options={'verbose_name': 'User Email OTP', 'verbose_name_plural': 'User Email OTP'},
),
migrations.AlterModelOptions(
name='usernotification',
options={'verbose_name': 'User Notification', 'verbose_name_plural': 'User Notification'},
),
]
@@ -1,21 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-14 09:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('account', '0006_alter_useremailotp_options_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='defaulttaskimages',
options={'verbose_name': 'Default Task images', 'verbose_name_plural': 'Default Task images'},
),
migrations.AlterModelOptions(
name='userdelete',
options={'verbose_name': 'Deleted User', 'verbose_name_plural': 'Deleted User'},
),
]
@@ -1,31 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-14 11:08
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('account', '0007_alter_defaulttaskimages_options_and_more'),
]
operations = [
migrations.CreateModel(
name='UserDeviceDetails',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('device_id', models.CharField(max_length=500)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user_device_details', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'User Device Details',
'verbose_name_plural': 'User Device Details',
'db_table': 'user_device_details',
},
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-20 11:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0008_userdevicedetails'),
]
operations = [
migrations.AlterField(
model_name='userdevicedetails',
name='device_id',
field=models.CharField(blank=True, max_length=500, null=True),
),
]
-167
View File
@@ -1,167 +0,0 @@
"""Account module file"""
"""Django import"""
from django.db import models
from django.contrib.auth.models import User
"""App import"""
from base.constants import USER_TYPE
# Create your models here.
class UserProfile(models.Model):
"""
User details
"""
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_profile')
user_type = models.CharField(max_length=15, choices=USER_TYPE, null=True, blank=True, default=None)
is_verified = models.BooleanField(default=False)
# OTP validity
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=False)
class Meta(object):
""" Meta information """
db_table = 'user_profile'
def __str__(self):
"""return phone as an object"""
return f'{self.user}'
class UserPhoneOtp(models.Model):
"""
This class is used to verify user email and their contact no.
"""
"""user details"""
country_code = models.IntegerField()
phone = models.CharField(max_length=17)
"""otp details"""
otp = models.CharField(max_length=10)
is_verified = models.BooleanField(default=False)
# OTP validity
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
expired_at = models.DateTimeField(blank=True, null=True)
is_active = models.BooleanField(default=True)
class Meta(object):
""" Meta information """
db_table = 'user_phone_otp'
def __str__(self):
"""return phone as an object"""
return self.phone
class UserEmailOtp(models.Model):
"""
This class is used to verify user email and their contact no.
"""
"""user details"""
email = models.EmailField()
"""otp details"""
otp = models.CharField(max_length=10)
is_verified = models.BooleanField(default=False)
user_type = models.CharField(max_length=15, choices=USER_TYPE, null=True, blank=True, default=None)
# OTP validity
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
expired_at = models.DateTimeField(blank=True, null=True)
is_active = models.BooleanField(default=True)
class Meta(object):
""" Meta information """
db_table = 'user_email_otp'
verbose_name = 'User Email OTP'
verbose_name_plural = 'User Email OTP'
def __str__(self):
"""return phone as an object"""
return self.email
class DefaultTaskImages(models.Model):
"""Default images upload in oss bucket"""
task_name = models.CharField(max_length=15)
image_url = models.URLField(null=True, blank=True, default=None)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta information """
db_table = 'default_task_image'
verbose_name = 'Default Task images'
verbose_name_plural = 'Default Task images'
def __str__(self):
"""return phone as an object"""
return self.task_name
class UserDelete(models.Model):
"""
User delete information
"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='delete_information_set')
"""Old email"""
old_email = models.EmailField(blank=True, null=True, verbose_name='Original Email')
"""Dummy email"""
d_email = models.EmailField(blank=True, null=True, verbose_name='Dummy Email')
is_active = models.BooleanField(default=True)
"""reason for leaving"""
reason = models.TextField(max_length=500, blank=True, null=True, verbose_name='Reason for Leaving')
created_at = models.DateTimeField(auto_now_add=True)
class Meta(object):
""" Meta information """
db_table = 'user_delete_information'
verbose_name = 'Deleted User'
verbose_name_plural = 'Deleted User'
def __str__(self):
return self.user.email
class UserNotification(models.Model):
"""
User notification details
"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_notification')
"""Push Notification"""
push_notification = models.BooleanField(default=True)
"""Email Notification"""
email_notification = models.BooleanField(default=True)
"""SMS Notification"""
sms_notification = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta information """
db_table = 'user_notification'
verbose_name = 'User Notification'
verbose_name_plural = 'User Notification'
def __str__(self):
return self.user.email
class UserDeviceDetails(models.Model):
"""
User notification details
"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_device_details')
"""Device ID"""
device_id = models.CharField(max_length=500, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta information """
db_table = 'user_device_details'
verbose_name = 'User Device Details'
verbose_name_plural = 'User Device Details'
def __str__(self):
return self.user.email
-371
View File
@@ -1,371 +0,0 @@
"""Account serializer"""
"""Django Import"""
# Import Refresh token of jwt
from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework_simplejwt.tokens import RefreshToken
import secrets
"""App import"""
# Import guardian's model,
# Import junior's model,
# Import account's model,
# Import constant from
# base package,
# Import messages from
# base package,
# Import some functions
# from utils file"""
from guardian.models import Guardian
from junior.models import Junior
from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp
from base.constants import GUARDIAN, JUNIOR, SUPERUSER, NUMBER
from base.messages import ERROR_CODE, SUCCESS_CODE, STATUS_CODE_ERROR
from .utils import delete_user_account_condition_social, delete_user_account_condition
# In this serializer file
# define google login serializer
# update junior profile,
# update guardian profile,
# super admin serializer,
# reset password,
# forgot password,
# change password,
# basic junior serializer,
# basic guardian serializer,
# user delete account serializer,
# user notification serializer,
# update user notification serializer,
# default task's images serializer,
# upload default task's images serializer,
# email verification serializer,
# phone otp serializer
# create all serializer here
class GoogleLoginSerializer(serializers.Serializer):
"""google login serializer"""
access_token = serializers.CharField(max_length=5000, required=True)
class Meta(object):
"""meta class"""
fields = ('access_token',)
class UpdateJuniorProfileImageSerializer(serializers.ModelSerializer):
"""update junior image"""
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'image']
def update(self, instance, validated_data):
"""update image """
junior_image = validated_data.get('image', instance.image)
instance.image = junior_image
instance.save()
return instance
class UpdateGuardianImageSerializer(serializers.ModelSerializer):
"""update guardian image"""
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id','image']
def update(self, instance, validated_data):
"""update image """
instance.image = validated_data.get('image', instance.image)
instance.save()
return instance
class ResetPasswordSerializer(serializers.Serializer):
"""Reset Password after verification"""
verification_code = serializers.CharField(max_length=10)
password = serializers.CharField(required=True)
class Meta(object):
"""Meta info"""
model = User
def create(self, validated_data):
verification_code = validated_data.pop('verification_code')
password = validated_data.pop('password')
# fetch email otp object of the user
user_opt_details = UserEmailOtp.objects.filter(otp=verification_code, is_verified=True).last()
if user_opt_details:
user_details = User.objects.filter(email=user_opt_details.email).last()
if user_details:
if user_details.check_password(password):
raise serializers.ValidationError({"details":ERROR_CODE['2001'],"code":"400", "status":"failed"})
user_details.set_password(password)
user_details.save()
return {'password':password}
return user_opt_details
return ''
class ChangePasswordSerializer(serializers.Serializer):
"""Update Password after verification"""
current_password = serializers.CharField(max_length=100)
new_password = serializers.CharField(required=True)
class Meta(object):
"""Meta info"""
model = User
def validate_current_password(self, value):
user = self.context
# check old password
if self.context.password not in ('', None) and user.check_password(value):
return value
raise serializers.ValidationError(ERROR_CODE['2015'])
def create(self, validated_data):
new_password = validated_data.pop('new_password')
current_password = validated_data.pop('current_password')
"""Check new password is different from current password"""
if new_password == current_password:
raise serializers.ValidationError({"details": ERROR_CODE['2026']})
user_details = User.objects.filter(email=self.context).last()
if user_details:
user_details.set_password(new_password)
user_details.save()
return {'password':new_password}
return ''
class ForgotPasswordSerializer(serializers.Serializer):
"""Forget password serializer"""
email = serializers.EmailField()
class AdminLoginSerializer(serializers.ModelSerializer):
"""admin login serializer"""
email = serializers.EmailField(required=True)
password = serializers.CharField(required=True)
class Meta:
"""
meta class
"""
model = User
fields = ('email', 'password')
def validate(self, attrs):
user = User.objects.filter(email__iexact=attrs['email'], is_superuser=True
).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first()
if not user:
raise serializers.ValidationError({'details': ERROR_CODE['2002']})
elif not user.check_password(attrs['password']):
raise serializers.ValidationError({'details': ERROR_CODE['2002']})
self.context.update({'user': user})
return attrs
def create(self, validated_data):
"""
used to return the user object after validation
"""
return self.context['user']
class SuperUserSerializer(serializers.ModelSerializer):
"""Super admin serializer"""
user_type = serializers.SerializerMethodField('get_user_type')
auth_token = serializers.SerializerMethodField('get_auth_token')
refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj):
refresh = RefreshToken.for_user(obj)
access_token = str(refresh.access_token)
return access_token
def get_refresh_token(self, obj):
refresh = RefreshToken.for_user(obj)
refresh_token = str(refresh)
return refresh_token
def get_user_type(self, obj):
"""user type"""
return str(NUMBER['three'])
class Meta(object):
"""Meta info"""
model = User
fields = ['id', 'auth_token', 'refresh_token', 'username', 'email', 'first_name',
'last_name', 'is_active', 'user_type']
class GuardianSerializer(serializers.ModelSerializer):
"""guardian serializer"""
user_type = serializers.SerializerMethodField('get_user_type')
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
auth_token = serializers.SerializerMethodField('get_auth_token')
refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj):
refresh = RefreshToken.for_user(obj.user)
access_token = str(refresh.access_token)
return access_token
def get_refresh_token(self, obj):
refresh = RefreshToken.for_user(obj.user)
refresh_token = str(refresh)
return refresh_token
def get_user_type(self, obj):
"""user type"""
email_verified = UserEmailOtp.objects.filter(email=obj.user.username).last()
if email_verified and email_verified.user_type is not None:
return email_verified.user_type
return str(NUMBER['two'])
def get_auth(self, obj):
"""user email address"""
return obj.user.username
def get_first_name(self, obj):
"""user first name"""
return obj.user.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.user.last_name
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active',
'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type', 'country_name']
class JuniorSerializer(serializers.ModelSerializer):
"""junior serializer"""
user_type = serializers.SerializerMethodField('get_user_type')
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
auth_token = serializers.SerializerMethodField('get_auth_token')
refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj):
refresh = RefreshToken.for_user(obj.auth)
access_token = str(refresh.access_token)
return access_token
def get_refresh_token(self, obj):
refresh = RefreshToken.for_user(obj.auth)
refresh_token = str(refresh)
return refresh_token
def get_user_type(self, obj):
email_verified = UserEmailOtp.objects.filter(email=obj.auth.username).last()
if email_verified and email_verified.user_type is not None:
return email_verified.user_type
return str(NUMBER['one'])
def get_auth(self, obj):
return obj.auth.username
def get_first_name(self, obj):
return obj.auth.first_name
def get_last_name(self, obj):
return obj.auth.last_name
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_password_set',
'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited']
class EmailVerificationSerializer(serializers.ModelSerializer):
"""Email verification serializer"""
class Meta(object):
"""Meta info"""
model = UserEmailOtp
fields = '__all__'
class DefaultTaskImagesSerializer(serializers.ModelSerializer):
"""Update Password after verification"""
class Meta(object):
"""Meta info"""
model = DefaultTaskImages
fields = ['id', 'task_name', 'image_url']
def create(self, validated_data):
# create default task object
data = DefaultTaskImages.objects.create(**validated_data)
return data
class DefaultTaskImagesDetailsSerializer(serializers.ModelSerializer):
"""Update Password after verification"""
class Meta(object):
"""Meta info"""
model = DefaultTaskImages
fields = '__all__'
class UserDeleteSerializer(serializers.ModelSerializer):
"""User Delete Serializer"""
class Meta(object):
"""Meta Information"""
model = UserDelete
fields = ['id','reason']
def create(self, validated_data):
user = self.context['user']
user_type = str(self.context['user_type'])
data = validated_data.get('reason')
passwd = self.context['password']
signup_method = self.context['signup_method']
random_num = secrets.randbelow(10001)
user_tb = User.objects.filter(id=user.id).last()
user_type_datas = UserEmailOtp.objects.filter(email=user.email).last()
# check password and sign up method
if user_tb and user_tb.check_password(passwd) and signup_method == str(NUMBER['one']):
user_type_data = user_type_datas.user_type
instance = delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num)
return instance
elif user_tb and passwd is None and signup_method in ['2','3']:
inst = delete_user_account_condition_social(user, user_type, user_tb, data, random_num)
return inst
else:
raise serializers.ValidationError({"details": ERROR_CODE['2031'], "code": "400", "status": "failed"})
class UserNotificationSerializer(serializers.ModelSerializer):
"""User Notification serializer"""
class Meta(object):
"""Meta info"""
model = UserNotification
fields = '__all__'
class UpdateUserNotificationSerializer(serializers.ModelSerializer):
"""Update User Notification serializer"""
class Meta(object):
"""Meta info"""
model = UserNotification
fields = ['push_notification', 'email_notification', 'sms_notification']
def create(self, validated_data):
instance = UserNotification.objects.filter(user=self.context).last()
if instance:
# change notification status
instance.push_notification = validated_data.get('push_notification',instance.push_notification)
instance.email_notification = validated_data.get('email_notification', instance.email_notification)
instance.sms_notification = validated_data.get('sms_notification', instance.sms_notification)
instance.save()
else:
instance = UserNotification.objects.create(user=self.context)
return instance
class UserPhoneOtpSerializer(serializers.ModelSerializer):
"""User Phone serializers"""
class Meta(object):
"""Meta info"""
model = UserPhoneOtp
fields = '__all__'
@@ -1,54 +0,0 @@
<!DOCTYPE HTML>
{% block subject %}DinDin{% endblock %}
{% load static %}
{% block html %}
<html lang="en" style="height: 100%;">
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>Zod Bank | OTP</title>
<style type="text/css">
@media all and (max-width: 599px) {
.block {
display: block !important;
width: 100%;
}
.top-space {
padding: 15px 0 0 !important;
}
}
</style>
</head>
<body style="margin: 0; padding: 0; background: #f7f8f9; height: 100%;">
<!-- begin template body -->
<table style="background:#f7f8f9; border: 0; border-collapse: separate; border-spacing: 0px; margin: auto; width: 100%; font-family: Arial, Helvetica, sans-serif; height: 100%;" aria-describedby="email-data-wrapper">
<tr>
<td style="padding: 0;">
<table style="background-color: white; border-collapse: separate; border-spacing: 0px; border: 1px solid #e4e8eb; width: 100%; max-width: 600px; margin-right:auto; margin-left: auto;
font-family: Arial, Helvetica, sans-serif;" aria-describedby="email-data-wrapper">
<tr>
<td style="padding: 41px 30px 40px; height: 39px; background: url({% static 'images/backgrounds/email_template.png' %}) left center no-repeat; background-size: cover;">
</td>
</tr>
{% block plain %}
{% endblock %}
<tr>
<td style="padding: 0 27px;">
<p style="margin: 0; font-size: 14px; line-height: 20px; color: #505050; font-weight: 400;">-</p>
<p style="margin: 0; font-size: 14px; line-height: 20px; color: #505050; font-weight: 400;">Cheers!</p>
<p style="margin: 0 0 30px; font-size: 14px; line-height: 20px; color: #505050; font-weight: 700;">Zod Bank Team</p>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<!-- end template body -->
</body>
</html>
{% endblock %}
@@ -1,23 +0,0 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
OTP Verification
{% endblock %}
{% block plain %}
<tr>
<td style="padding: 0 27px 15px;">
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
Hi User,
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
You are receiving this email for email verification. Please use <b>{{ otp }} </b>as the verification code for your email address & username.
</p>
</td>
</tr>
{% endblock %}
@@ -1,23 +0,0 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
Password Reset Verification Code
{% endblock %}
{% block plain %}
<tr>
<td style="padding: 0 27px 15px;">
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
Hi User,
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
You are receiving this email for reset password verification. Please use <b>{{ verification_code }} </b>as the verification code.
</p>
</td>
</tr>
{% endblock %}
@@ -1,23 +0,0 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
Approval Email
{% endblock %}
{% block plain %}
<tr>
<td style="padding: 0 27px 15px;">
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
Hello,
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
Please approve {{full_name}} for accessing the Zod bank platform as a junior.
</p>
</td>
</tr>
{% endblock %}
@@ -1,24 +0,0 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
Invitation Email
{% endblock %}
{% block plain %}
<tr>
<td style="padding: 0 27px 15px;">
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
Hi {{full_name}},
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
You are receiving this email for joining the ZOD bank platform. Please use <b>{{ url }} </b> link to join the platform.
<br> Your credentials are:- username = <b>{{email}}</b> and password <b>{{password}}</b><br> <br>Below are the steps to complete the account and how to use this platform.
</p>
</td>
</tr>
{% endblock %}
@@ -1,22 +0,0 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
{{subject}}
{% endblock %}
{% block plain %}
<tr>
<td style="padding: 0 27px 15px;">
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
Hi {{name}},
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
<b>{{name}}</b> have some queries and need some support. Please support them by using their email address <b> {{sender}}</b>. <br> <br> <b>Queries are:- </b> <br> {{ message }}
</p>
</td>
</tr>
{% endblock %}
-5
View File
@@ -1,5 +0,0 @@
"""Test cases file of account"""
"""Django import"""
from django.test import TestCase
# Create your tests here.
-71
View File
@@ -1,71 +0,0 @@
""" Urls files"""
"""Django import"""
from django.urls import path, include
"""Third party import"""
from rest_framework import routers
# Import view functions
# UserLogin views,
# SendPhoneOtp views,
# UserPhoneVerification views,
# UserEmailVerification views,
# ReSendEmailOtp views,
# ForgotPasswordAPIView views,
# ResetPasswordAPIView views,
# ChangePasswordAPIView views,
# UpdateProfileImage views,
# GoogleLoginViewSet views,
# SigninWithApple views,
# ProfileAPIViewSet views,
# UploadImageAPIViewSet views,
# DefaultImageAPIViewSet views,
# DeleteUserProfileAPIViewSet views,
# UserNotificationAPIViewSet views,
# UpdateUserNotificationAPIViewSet views,
# SendSupportEmail views,
# LogoutAPIView views,
# AccessTokenAPIView views"""
from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVerification, ReSendEmailOtp,
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView,
AdminLoginViewSet)
"""Router"""
router = routers.SimpleRouter()
"""API End points with router"""
router.register('user', UserLogin, basename='user')
"""super admin login"""
router.register('admin', AdminLoginViewSet, basename='admin')
"""google login end point"""
router.register('google-login', GoogleLoginViewSet, basename='admin')
router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp')
router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification')
"""email verification end point"""
router.register('user-email-verification', UserEmailVerification, basename='user-email-verification')
"""Resend email otp end point"""
router.register('resend-email-otp', ReSendEmailOtp, basename='resend-email-otp')
"""Profile end point"""
router.register('profile', ProfileAPIViewSet, basename='profile')
"""Upload default task image end point"""
router.register('upload-default-task-image', UploadImageAPIViewSet, basename='upload-default-task-image')
"""Fetch default task image end point"""
router.register('default-task-image', DefaultImageAPIViewSet, basename='default-task-image')
"""Delete user account"""
router.register('delete', DeleteUserProfileAPIViewSet, basename='delete')
"""user account notification"""
router.register('user-notification', UserNotificationAPIViewSet, basename='user-notification')
"""update user account notification"""
router.register('update-user-notification', UpdateUserNotificationAPIViewSet, basename='update-user-notification')
"""Define url pattern"""
urlpatterns = [
path('api/v1/', include(router.urls)),
path('api/v1/forgot-password/', ForgotPasswordAPIView.as_view()),
path('api/v1/reset-password/', ResetPasswordAPIView.as_view()),
path('api/v1/change-password/', ChangePasswordAPIView.as_view()),
path('api/v1/update-profile-image/', UpdateProfileImage.as_view()),
path('api/v1/apple-login/', SigninWithApple.as_view(), name='signup_with_apple'),
path('api/v1/send-support-email/', SendSupportEmail.as_view(), name='send-support-email'),
path('api/v1/logout/', LogoutAPIView.as_view(), name='logout'),
path('api/v1/generate-token/', AccessTokenAPIView.as_view(), name='generate-token')
]
-233
View File
@@ -1,233 +0,0 @@
"""Account utils"""
"""Import django"""
from django.conf import settings
from rest_framework import viewsets, status
from rest_framework.response import Response
"""Third party Django app"""
from templated_email import send_templated_mail
import jwt
import string
from datetime import datetime
from calendar import timegm
from uuid import uuid4
# Import secrets module for generating random number
import secrets
from rest_framework import serializers
# Django App Import
# Import models from junior App,
# Import models from guardian App,
# Import models from account App,
# Import messages from base package"""
from junior.models import Junior
from guardian.models import Guardian
from account.models import UserDelete
from base.messages import ERROR_CODE
from django.utils import timezone
# Define delete
# user account condition,
# Define delete
# user account
# condition for social
# login account,
# Update junior account,
# Update guardian account,
# Define custom email for otp verification,
# Define support email for user's query,
# Define custom success response,
# Define custom error response,
# Generate access token,
# refresh token by using jwt,
# Define function for generating
# guardian code, junior code,
# referral code,
# Define function for generating
# alphanumeric code
def delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num):
"""delete user account"""
if user_type == '1' and user_type_data == '1':
junior_account_update(user_tb)
elif user_type == '2' and user_type_data == '2':
guardian_account_update(user_tb)
else:
raise serializers.ValidationError({"details": ERROR_CODE['2030'], "code": "400", "status": "failed"})
user_tb.email = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower()
user_tb.username = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower()
d_email = user_tb.email
o_mail = user.email
# update user email with dummy email
user_tb.save()
"""create object in user delete model"""
instance = UserDelete.objects.create(user=user_tb, d_email=d_email, old_email=o_mail,
is_active=True, reason=data)
return instance
def delete_user_account_condition_social(user, user_type,user_tb, data, random_num):
"""delete user account"""
if user_type == '1':
junior_account_update(user_tb)
elif user_type == '2':
guardian_account_update(user_tb)
else:
raise serializers.ValidationError({"details": ERROR_CODE['2030'], "code": "400", "status": "failed"})
user_tb.email = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower()
user_tb.username = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower()
dummy_email = user_tb.email
old_mail = user.email
# update user email with dummy email
user_tb.save()
"""create object in user delete model"""
instance_data = UserDelete.objects.create(user=user_tb, d_email=dummy_email, old_email=old_mail,
is_active=True, reason=data)
return instance_data
def junior_account_update(user_tb):
"""junior account delete"""
junior_data = Junior.objects.filter(auth__email=user_tb.email).first()
if junior_data:
# Update junior account
junior_data.is_active = False
junior_data.is_verified = False
junior_data.guardian_code = '{}'
junior_data.save()
def guardian_account_update(user_tb):
"""update guardian account after delete the user account"""
guardian_data = Guardian.objects.filter(user__email=user_tb.email).first()
if guardian_data:
# Update guardian account
guardian_data.is_active = False
guardian_data.is_verified = False
guardian_data.save()
jun_data = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code))
"""Disassociate relation between guardian and junior"""
for data in jun_data:
data.guardian_code.remove(guardian_data.guardian_code)
data.save()
def send_otp_email(recipient_email, otp):
"""Send otp on email with template"""
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [recipient_email]
"""Send otp on email"""
send_templated_mail(
template_name='email_otp_verification.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'otp': otp
}
)
return otp
def send_support_email(name, sender, subject, message):
"""Send otp on email with template"""
to_email = [settings.EMAIL_FROM_ADDRESS]
from_email = settings.DEFAULT_ADDRESS
"""Send support email to zod bank support team"""
send_templated_mail(
template_name='support_mail.email',
from_email=from_email,
recipient_list=to_email,
context={
'name': name.title(),
'sender': sender,
'subject': subject,
'message': message
}
)
return name
def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count=None):
"""Custom response code"""
if not data:
"""when data is none"""
data = None
return Response({"data": data, "message": detail, "status": "success", "code": response_status, "count": count})
def custom_error_response(detail, response_status):
"""
function is used for getting same global error response for all
:param detail: error message .
:param response_status: http status.
:return: Json response
"""
if not detail:
"""when details is empty"""
detail = {}
return Response({"error": detail, "status": "failed", "code": response_status})
def get_user_data(attrs):
"""
used to decode token
"""
user_data = jwt.decode(jwt=attrs['token'], options={'verify_signature': False},
algorithms=['RS256'])
return user_data
def generate_jwt_token(token_type: str, now_time: int, data: dict = dict):
"""
used to generate jwt token
"""
if type(data) == type:
data = {}
"""Update data dictionary"""
data.update({
'token_type': token_type,
'iss': 'your_site_url',
'iat': timegm(datetime.utcnow().utctimetuple()),
'jti': uuid4().hex
})
"""Access and Refresh token"""
TOKEN_TYPE = ["access", "refresh"]
if token_type == TOKEN_TYPE[1]:
"""Refresh token"""
exp = now_time + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME']
else:
"""access token"""
exp = now_time + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME']
data.update({
"exp": timegm(exp.utctimetuple())
})
signing_key = secrets.token_hex(32)
return jwt.encode(payload=data, key=signing_key,
algorithm='HS256')
def get_token(data: dict = dict):
""" create access and refresh token """
now_time = datetime.utcnow()
"""generate access token"""
access = generate_jwt_token('access', now_time, data)
"""generate refresh token"""
refresh = generate_jwt_token('refresh', now_time, data)
return {
'access': access,
'refresh': refresh
}
def generate_alphanumeric_code(length):
"""Generate alphanumeric code"""
alphabet = string.ascii_letters + string.digits
code = ''.join(secrets.choice(alphabet) for _ in range(length))
return code
def generate_code(value, user_id):
"""generate referral, junior and guardian code"""
code = value + str(user_id).zfill(3)
return code
OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1)
-577
View File
@@ -1,577 +0,0 @@
"""Account view """
from notifications.utils import remove_fcm_token
# django imports
from datetime import datetime, timedelta
from rest_framework import viewsets, status, views
from rest_framework.decorators import action
import random
import logging
from django.utils import timezone
import jwt
from django.contrib.auth import logout
from django.contrib.auth import authenticate, login
from rest_framework.permissions import IsAuthenticated
from templated_email import send_templated_mail
import google.oauth2.credentials
import google.auth.transport.requests
from rest_framework import status
import requests
from rest_framework.response import Response
from rest_framework import mixins
from django.conf import settings
# local imports
from guardian.models import Guardian
from junior.models import Junior
from guardian.utils import upload_image_to_alibaba
from account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification
from django.contrib.auth.models import User
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer,
DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer,
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer,
AdminLoginSerializer)
from rest_framework_simplejwt.tokens import RefreshToken
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, ZOD, JUN, GRD
from guardian.tasks import generate_otp
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
generate_code, OTP_EXPIRY)
from junior.serializers import JuniorProfileSerializer
from guardian.serializers import GuardianProfileSerializer
class GoogleLoginMixin(object):
"""google login mixin"""
@staticmethod
def google_login(request):
"""google login function"""
access_token = request.data.get('access_token')
user_type = request.data.get('user_type')
if not access_token:
return Response({'error': 'Access token is required.'}, status=status.HTTP_400_BAD_REQUEST)
try:
# Validate the access token and obtain the user's email and name
credentials = google.oauth2.credentials.Credentials.from_authorized_user_info(
info={
'access_token': access_token,
'token_uri': 'https://oauth2.googleapis.com/token',
'client_id': settings.GOOGLE_CLIENT_ID,
'client_secret': settings.GOOGLE_CLIENT_SECRET,
'refresh_token': None,
}
)
user_info_endpoint = f'https://www.googleapis.com/oauth2/v3/userinfo?access_token={access_token}'
headers = {'Authorization': f'Bearer {credentials.token}'}
response = requests.get(user_info_endpoint, headers=headers)
response.raise_for_status()
user_info = response.json()
email = user_info['email']
first_name = user_info['given_name']
last_name = user_info['family_name']
profile_picture = user_info['picture']
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
# Check if the user exists in your database or create a new user
# ...
user_data = User.objects.filter(email__iexact=email)
if user_data.exists():
if str(user_type) == '1':
junior_query = Junior.objects.filter(auth=user_data.last()).last()
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.filter(user=user_data.last()).last()
serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
if not User.objects.filter(email__iexact=email).exists():
user_obj = User.objects.create(username=email, email=email, first_name=first_name, last_name=last_name)
if str(user_type) == '1':
junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True,
image=profile_picture, signup_method='2',
junior_code=generate_code(JUN, user_obj.id),
referral_code=generate_code(ZOD, user_obj.id)
)
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
image=profile_picture,signup_method='2',
guardian_code=generate_code(GRD, user_obj.id),
referral_code=generate_code(ZOD, user_obj.id)
)
serializer = GuardianSerializer(guardian_query)
# Return a JSON response with the user's email and name
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
class GoogleLoginViewSet(GoogleLoginMixin, viewsets.GenericViewSet):
"""Google login viewset"""
serializer_class = GoogleLoginSerializer
def create(self, request):
"""create method"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
return self.google_login(request)
class SigninWithApple(views.APIView):
"""This API is for sign in with Apple for app."""
def post(self, request):
token = request.data.get("access_token")
user_type = request.data.get("user_type")
try:
decoded_data = jwt.decode(token, options={"verify_signature": False})
user_data = {"email": decoded_data.get('email'), "username": decoded_data.get('email'), "is_active": True}
if decoded_data.get("email"):
try:
user = User.objects.get(email=decoded_data.get("email"))
if str(user_type) == '1':
junior_query = Junior.objects.filter(auth=user).last()
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.filter(user=user).last()
serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
except User.DoesNotExist:
user = User.objects.create(**user_data)
if str(user_type) == '1':
junior_query = Junior.objects.create(auth=user, is_verified=True, is_active=True,
signup_method='3',
junior_code=generate_code(JUN, user.id),
referral_code=generate_code(ZOD, user.id))
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
signup_method='3',
guardian_code=generate_code(GRD, user.id),
referral_code=generate_code(ZOD, user.id))
serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
except Exception as e:
logging.error(e)
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class UpdateProfileImage(views.APIView):
"""Update profile image"""
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
image = request.data['image']
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
filename = f"images/{image.name}"
image_url = upload_image_to_alibaba(image, filename)
image_data = image_url
if str(request.data['user_type']) == '1':
junior_query = Junior.objects.filter(auth=request.user).last()
serializer = UpdateJuniorProfileImageSerializer(junior_query,
data={'image':image_data}, partial=True)
elif str(request.data['user_type']) == '2':
guardian_query = Guardian.objects.filter(user=request.user).last()
serializer = UpdateGuardianImageSerializer(guardian_query,
data={'image':image_data}, partial=True)
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3017'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
logging.error(e)
return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST)
class ChangePasswordAPIView(views.APIView):
"""change password"""
serializer_class = ChangePasswordSerializer
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = ChangePasswordSerializer(context=request.user, data=request.data)
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3007'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class ResetPasswordAPIView(views.APIView):
"""Reset password"""
def post(self, request):
serializer = ResetPasswordSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3006'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class ForgotPasswordAPIView(views.APIView):
"""Forgot password"""
def post(self, request):
serializer = ForgotPasswordSerializer(data=request.data)
if serializer.is_valid():
email = serializer.validated_data['email']
try:
User.objects.get(email=email)
except User.DoesNotExist:
return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND)
verification_code = generate_otp()
# Send the verification code to the user's email
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [email]
send_templated_mail(
template_name='email_reset_verification.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'verification_code': verification_code
}
)
expiry = OTP_EXPIRY
user_data, created = UserEmailOtp.objects.get_or_create(email=email)
if created:
user_data.expired_at = expiry
user_data.save()
if user_data:
user_data.otp = verification_code
user_data.expired_at = expiry
user_data.save()
return custom_response(SUCCESS_CODE['3015'],
response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class SendPhoneOtp(viewsets.ModelViewSet):
"""Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer
def create(self, request, *args, **kwargs):
otp = generate_otp()
phone_number = self.request.data['phone']
if phone_number.isdigit() and len(phone_number) == 10:
phone_otp, created = UserPhoneOtp.objects.get_or_create(country_code=self.request.data['country_code'],
phone=self.request.data['phone'])
if phone_otp:
phone_otp.otp = otp
phone_otp.save()
return custom_response(None, {'phone_otp':otp}, response_status=status.HTTP_200_OK)
return custom_error_response(ERROR_CODE['2020'], response_status=status.HTTP_400_BAD_REQUEST)
class UserPhoneVerification(viewsets.ModelViewSet):
"""Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer
def list(self, request, *args, **kwargs):
try:
phone_data = UserPhoneOtp.objects.filter(phone=self.request.GET.get('phone'),
otp=self.request.GET.get('otp')).last()
if phone_data:
phone_data.is_verified = True
phone_data.save()
return custom_response(SUCCESS_CODE['3012'], response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
except Exception:
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
class UserLogin(viewsets.ViewSet):
"""User login"""
@action(methods=['post'], detail=False)
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
device_id = request.META.get('HTTP_DEVICE_ID')
user = authenticate(request, username=username, password=password)
try:
if user is not None:
login(request, user)
guardian_data = Guardian.objects.filter(user__username=username, is_verified=True).last()
if guardian_data:
serializer = GuardianSerializer(guardian_data).data
junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last()
if junior_data:
serializer = JuniorSerializer(junior_data).data
device_details, created = UserDeviceDetails.objects.get_or_create(user=user)
if device_details:
device_details.device_id = device_id
device_details.save()
return custom_response(SUCCESS_CODE['3003'], serializer, response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED)
except Exception as e:
logging.error(e)
email_verified = UserEmailOtp.objects.filter(email=username).last()
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
data = {"auth_token":access_token, "refresh_token":refresh_token, "is_profile_complete": False,
"user_type": email_verified.user_type,
}
is_verified = False
if email_verified:
is_verified = email_verified.is_verified
if not is_verified:
otp = generate_otp()
email_verified.otp = otp
email_verified.save()
data.update({"email_otp":otp})
send_otp_email(username, otp)
return custom_response(ERROR_CODE['2024'], {"email_otp": otp, "is_email_verified": is_verified},
response_status=status.HTTP_200_OK)
data.update({"is_email_verified": is_verified})
return custom_response(SUCCESS_CODE['3003'], data, response_status=status.HTTP_200_OK)
@action(methods=['post'], detail=False)
def admin_login(self, request):
email = request.data.get('email')
password = request.data.get('password')
user = User.objects.filter(email__iexact=email, is_superuser=True
).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first()
if not user:
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST)
elif not user.check_password(password):
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST)
serializer = SuperUserSerializer(user)
return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
class AdminLoginViewSet(viewsets.GenericViewSet):
"""
admin login api
"""
serializer_class = AdminLoginSerializer
@action(methods=['post'], url_name='login', url_path='login', detail=False)
def admin_login(self, request, *args, **kwargs):
"""
:param request:
:return:
"""
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
data = {"auth_token": access_token, "refresh_token": refresh_token, "username": user.username,
"email": user.email, "first_name": user.first_name, "last_name": user.last_name,
"is_active": user.is_active, "user_type": '3', "is_superuser": user.is_superuser}
return custom_response(None, data)
class UserEmailVerification(viewsets.ModelViewSet):
"""User Email verification"""
serializer_class = EmailVerificationSerializer
queryset = UserEmailOtp.objects.all()
def list(self, request, *args, **kwargs):
try:
user_obj = User.objects.filter(username=self.request.GET.get('email')).last()
email_data = UserEmailOtp.objects.filter(email=self.request.GET.get('email'),
otp=self.request.GET.get('otp')).last()
if email_data:
input_datetime_str = str(email_data.expired_at)
input_format = "%Y-%m-%d %H:%M:%S.%f%z"
output_format = "%Y-%m-%d %H:%M:%S.%f"
input_datetime = datetime.strptime(input_datetime_str, input_format)
output_datetime_str = input_datetime.strftime(output_format)
format_str = "%Y-%m-%d %H:%M:%S.%f"
datetime_obj = datetime.strptime(output_datetime_str, format_str)
if datetime.today() > datetime_obj:
return custom_error_response(ERROR_CODE["2029"], response_status=status.HTTP_400_BAD_REQUEST)
email_data.is_verified = True
email_data.save()
if email_data.user_type == '1':
junior_data = Junior.objects.filter(auth__email=self.request.GET.get('email')).last()
if junior_data:
junior_data.is_verified = True
junior_data.save()
else:
guardian_data = Guardian.objects.filter(user__email=self.request.GET.get('email')).last()
if guardian_data:
guardian_data.is_verified = True
guardian_data.save()
refresh = RefreshToken.for_user(user_obj)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
return custom_response(SUCCESS_CODE['3011'], {"auth_token":access_token,
"refresh_token":refresh_token},
response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
logging.error(e)
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
class ReSendEmailOtp(viewsets.ModelViewSet):
"""Send otp on phone"""
queryset = UserEmailOtp.objects.all()
serializer_class = EmailVerificationSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
otp = generate_otp()
if User.objects.filter(email=request.data['email']):
expiry = OTP_EXPIRY
email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email'])
if created:
email_data.expired_at = expiry
email_data.save()
if email_data:
email_data.otp = otp
email_data.expired_at = expiry
email_data.save()
send_otp_email(request.data['email'], otp)
return custom_response(SUCCESS_CODE['3016'], response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2023"], response_status=status.HTTP_400_BAD_REQUEST)
class ProfileAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset"""
queryset = User.objects.all()
serializer_class = JuniorProfileSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""profile view"""
if str(self.request.GET.get('user_type')) == '1':
junior_data = Junior.objects.filter(auth=self.request.user).last()
if junior_data:
serializer = JuniorProfileSerializer(junior_data)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
elif str(self.request.GET.get('user_type')) == '2':
guardian_data = Guardian.objects.filter(user=self.request.user).last()
if guardian_data:
serializer = GuardianProfileSerializer(guardian_data)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class UploadImageAPIViewSet(viewsets.ModelViewSet):
"""upload task image"""
queryset = DefaultTaskImages.objects.all()
serializer_class = DefaultTaskImagesSerializer
def create(self, request, *args, **kwargs):
"""upload images"""
image_data = request.data['image_url']
filename = f"default_task_images/{image_data.name}"
if image_data.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
image = upload_image_to_alibaba(image_data, filename)
image_data = image
request.data['image_url'] = image_data
serializer = DefaultTaskImagesSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
class DefaultImageAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset"""
queryset = DefaultTaskImages.objects.all()
serializer_class = DefaultTaskImagesDetailsSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""profile view"""
queryset = DefaultTaskImages.objects.all()
serializer = DefaultTaskImagesSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
""" Delete user API view set """
@action(detail=False, methods=['POST'], url_path='user-account',serializer_class=UserDeleteSerializer,
permission_classes=[IsAuthenticated])
def account(self, request):
user_type = str(request.data['user_type'])
password = request.data.get('password')
signup_method = str(request.data.get('signup_method'))
serializer = self.get_serializer(data=request.data, context={'request': request, 'user': request.user,
'user_type': user_type,
'password': password,
'signup_method':signup_method})
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3005'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class UserNotificationAPIViewSet(viewsets.ModelViewSet):
"""notification viewset"""
queryset = UserNotification.objects.all()
serializer_class = UserNotificationSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""profile view"""
queryset = self.queryset.filter(user=request.user)
serializer = UserNotificationSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
"""Update notification viewset"""
queryset = UserNotification.objects.all()
serializer_class = UpdateUserNotificationSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
"""profile view"""
serializer = UpdateUserNotificationSerializer(data=request.data,
context=request.user)
if serializer.is_valid():
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
class SendSupportEmail(views.APIView):
"""support email api"""
permission_classes = (IsAuthenticated,)
def post(self, request):
name = request.data.get('name')
sender = request.data.get('email')
subject = request.data.get('subject')
message = request.data.get('message')
if name and sender and subject and message:
try:
send_support_email(name, sender, subject, message)
return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
else:
return custom_error_response(ERROR_CODE['2033'], response_status=status.HTTP_400_BAD_REQUEST)
class LogoutAPIView(views.APIView):
"""Log out API"""
permission_classes = (IsAuthenticated,)
def post(self, request):
remove_fcm_token(
request.auth.payload['user_id'],
request.META['HTTP_AUTHORIZATION'].split(" ")[1],
request.data.get('registration_id', ""))
logout(request)
request.session.flush()
return custom_response(SUCCESS_CODE['3020'], response_status=status.HTTP_200_OK)
class AccessTokenAPIView(views.APIView):
"""generate access token API"""
def post(self, request):
# Assuming you have a refresh_token string
refresh_token = request.data['refresh_token']
# Create a RefreshToken instance from the refresh token string
refresh = RefreshToken(refresh_token)
# Generate a new access token
access_token = str(refresh.access_token)
data = {"auth_token": access_token}
return custom_response(None, data, response_status=status.HTTP_200_OK)
-3
View File
@@ -1,3 +0,0 @@
"""
This is init module of the Project Zod Bank
"""
-30
View File
@@ -1,30 +0,0 @@
"""
Common send_mail function
"""
import logging
from django.core.mail import EmailMultiAlternatives
def send_mail(subject, message, from_email, recipient_list, html_message=None, cc=None,
fail_silently=False):
"""
Send Email
:param subject:
:param message:
:param from_email:
:param recipient_list:
:param html_message:
:param cc:
:param fail_silently:
:return:
"""
try:
mail = EmailMultiAlternatives(subject, message, from_email, recipient_list, cc)
if html_message:
mail.attach_alternative(html_message, 'text/html')
return mail.send(fail_silently)
except Exception as e:
logging.error(e)
return False
-112
View File
@@ -1,112 +0,0 @@
"""
This module contains constants used throughout the project
"""
import os
# GOOGLE_URL used for interact with google server to verify user existence.
#GOOGLE_URL = "https://www.googleapis.com/plus/v1/"
# Define Code prefix word
# for guardian code,
# junior code,
# referral code"""
ZOD = 'ZOD'
JUN = 'JUN'
GRD = 'GRD'
# Define number variable
# from zero to
# twenty and
# some standard
# number"""
NUMBER = {
'point_zero': 0.0, 'zero': 0, 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6, 'seven': 7,
'eight': 8, 'nine': 9, 'ten': 10, 'eleven': 11, 'twelve': 12, 'thirteen': 13, 'fourteen': 14, 'fifteen': 15,
'sixteen': 16, 'seventeen': 17, 'eighteen': 18, 'nineteen': 19, 'twenty': 20,
'twenty_one': 21, 'twenty_two': 22,'twenty_three': 23, 'twenty_four': 24, 'twenty_five': 25,
'thirty': 30, 'forty': 40, 'fifty': 50, 'sixty': 60, 'seventy': 70, 'eighty': 80, 'ninty': 90,
'ninety_nine': 99, 'hundred': 100, 'thirty_six_hundred': 3600
}
# Super Admin string constant for 'role'
SUPER_ADMIN = "Super Admin"
# Define jwt_token_expiration time in minutes for now token will expire after 3 days
JWT_TOKEN_EXPIRATION = 3 * 24 * 60
# Define common file extention
FILE_EXTENSION = ("gif", "jpeg", "jpg", "png", "svg")
# Define file size in bytes(5MB = 5 * 1024 * 1024)
FILE_SIZE = 5 * 1024 * 1024
# String constant for configurable date for allocation lock period
ALLOCATION_LOCK_DATE = 1
"""user type"""
USER_TYPE = (
('1', 'junior'),
('2', 'guardian'),
('3', 'superuser')
)
"""gender"""
GENDERS = (
('1', 'Male'),
('2', 'Female')
)
# Task status"""
TASK_STATUS = (
('1', 'pending'),
('2', 'in-progress'),
('3', 'rejected'),
('4', 'requested'),
('5', 'completed'),
('6', 'expired')
)
# sign up method
SIGNUP_METHODS = (
('1', 'manual'),
('2', 'google'),
('3', 'apple')
)
# relationship
RELATIONSHIP = (
('1', 'parent'),
('2', 'legal_guardian')
)
"""
Define task status
in a number"""
PENDING = 1
IN_PROGRESS = 2
REJECTED = 3
REQUESTED = 4
COMPLETED = 5
EXPIRED = 6
TASK_POINTS = 5
# duplicate name used defined in constant PROJECT_NAME
PROJECT_NAME = 'Zod Bank'
# define user type constant
GUARDIAN = 'guardian'
JUNIOR = 'junior'
SUPERUSER = 'superuser'
# numbers used as a constant
# Define the byte into kb
BYTE_IMAGE_SIZE = 1024
# validate file size
MAX_FILE_SIZE = 1024 * 1024 * 5
ARTICLE_SURVEY_POINTS = 5
MAX_ARTICLE_CARD = 6
# min and max survey
MIN_ARTICLE_SURVEY = 5
MAX_ARTICLE_SURVEY = 10
# real time url
time_url = "http://worldtimeapi.org/api/timezone/Asia/Riyadh"
ARTICLE_CARD_IMAGE_FOLDER = 'article-card-images'
-16
View File
@@ -1,16 +0,0 @@
"""
This module contains constants used throughout the project
"""
from zod_bank.settings import BUCKET_NAME
# Define S3 folder url
S3_FOLDER_DIR = {
'user_image': 'user_image/',
}
# S3 bucket url
S3_URL = "https://"+BUCKET_NAME+".s3.amazonaws.com/"
S3_FOLDER_URL = {
'user_image_file': S3_URL+S3_FOLDER_DIR['user_image'],
}
-168
View File
@@ -1,168 +0,0 @@
"""
This module contains all the messages used all across the project
"""
ERROR_CODE_REQUIRED = {
# Error code for email address
"1000": ["Required email address not found."],
# Error code for password
"1001": ["Required password not found."],
# Error code for Required Post parameters
"1002": ["Required POST parameters not found."],
# Error code for Required Get parameters
"1003": ["Required GET parameters not found."],
# Error code for Required Headers
"1004": ["Required headers were not found."],
# Error code for Required Put parameters
"1005": ["Required PUT parameters not found."],
# Error code for Required query parameters
"1006": ["Required query parameters is not valid."],
# Error code for Required Head parameters
"1008": ["Required HEAD parameters not found."]
}
# Error code
ERROR_CODE = {
"2000": "Invalid email address. Please enter a registered email.",
"2001": "This is your existing password. Please choose other one",
"2002": "Invalid username or password.",
"2003": "An account already exists with this email address.",
"2004": "User not found.",
"2005": "Your account has been activated.",
"2006": "Your account is not activated.",
"2007": "Your account already activated.",
"2008": "The OTP entered is not correct.",
"2009": "The user provided cannot be found or the reset password token has become invalid/timed out.",
"2010": "Invalid Link.",
"2011": "Your profile has not been completed yet.",
"2012": "Phone number already used",
"2013": "Invalid token.",
"2014": "Your old password doesn't match.",
"2015": "Invalid old password.",
"2016": "Invalid search.",
"2017": "{model} object with {pk} does not exist",
"2018": "Attached File not found",
"2019": "Invalid Referral code",
"2020": "Enter valid mobile number",
"2021": "Already register",
"2022": "Invalid Guardian code",
"2023": "Invalid user",
# email not verified
"2024": "Email not verified",
"2025": "Invalid input. Expected a list of strings.",
# check old and new password
"2026": "New password should not same as old password",
"2027": "data should contain `identityToken`",
"2028": "You are not authorized person to sign up on this platform",
"2029": "Validity of otp verification has expired. Please request a new one.",
"2030": "Use correct user type and token",
# invalid password
"2031": "Invalid password",
"2032": "Failed to send email",
"2033": "Missing required fields",
"2034": "Junior is not associated",
# image size
"2035": "Image should not be 0 kb",
"2036": "Choose valid user",
# log in multiple device msg
"2037": "You are already log in another device",
"2038": "Choose valid action for task",
# card length limit
"2039": "Add at least one article card or maximum 6",
"2040": "Add at least 5 article survey or maximum 10",
# add article msg
"2041": "Article with given id doesn't exist.",
"2042": "Article Card with given id doesn't exist.",
"2043": "Article Survey with given id doesn't exist.",
"2044": "Task does not exist",
"2045": "Invalid guardian",
# past due date
"2046": "Due date must be future date",
# invalid junior id msg
"2047": "Invalid Junior ID ",
"2048": "Choose right file for image",
# task request
"2049": "This task is already requested ",
"2059": "Already exist junior",
# task status
"2060": "Task does not exist or not in pending state",
"2061": "Please insert image or check the image is valid or not.",
# email not null
"2062": "Please enter email address",
"2063": "Unauthorized access.",
"2064": "To change your password first request an OTP and get it verify then change your password.",
"2065": "Passwords do not match. Please try again.",
"2066": "Task does not exist or not in expired state",
"2067": "Action not allowed. User type missing.",
"2068": "No guardian associated with this junior"
}
"""Success message code"""
SUCCESS_CODE = {
"3000": "ok",
# Success code for password
"3001": "Sign up successfully",
# Success code for Thank you
"3002": "Thank you for contacting us! Our Consumer Experience Team will reach out to you shortly.",
# Success code for account activation
"3003": "Log in successful",
# Success code for password reset
"3004": "Password reset link has been sent to your email address",
# Success code for link verified
"3005": "Your account is deleted successfully.",
# Success code for password reset
"3006": "Password reset successful. You can now log in with your new password.",
# Success code for password update
"3007": "Your password has been changed successfully.",
# Success code for valid link
"3008": "You have a valid link.",
# Success code for logged out
"3009": "You have successfully logged out!",
# Success code for check all fields
"3010": "All fields are valid",
"3011": "Email OTP Verified successfully",
"3012": "Phone OTP Verified successfully",
"3013": "Valid Guardian code",
"3014": "Password has been updated successfully.",
"3015": "Verification code has been sent on your email.",
"3016": "An OTP has been sent on your email.",
"3017": "Profile image update successfully",
"3018": "Task created successfully",
"3019": "Support Email sent successfully",
"3020": "Logged out successfully.",
"3021": "Add junior successfully",
"3022": "Remove junior successfully",
"3023": "Junior is approved successfully",
"3024": "Junior request is rejected successfully",
"3025": "Task is approved successfully",
"3026": "Task is rejected successfully",
"3027": "Article has been created successfully.",
"3028": "Article has been updated successfully.",
"3029": "Article has been deleted successfully.",
"3030": "Article Card has been removed successfully.",
"3031": "Article Survey has been removed successfully.",
"3032": "Task request sent successfully",
"3033": "Valid Referral code",
"3034": "Invite guardian successfully",
"3035": "Task started successfully",
"3036": "Task reassign successfully",
"3037": "Profile has been updated successfully.",
"3038": "Status has been changed successfully.",
# notification read
"3039": "Notification read successfully"
}
"""status code error"""
STATUS_CODE_ERROR = {
# Status code for Invalid Input
"4001": ["Invalid input."],
# Status code for Authentication credentials
"4002": ["Authentication credentials were not provided."],
# Status code for Permission
"4003": ["You do not have permission to perform this action."],
# Status code for not found
"4004": ["Not found."],
# Status code for method not allowed
"4005": ["Method not allowed."]
}
-17
View File
@@ -1,17 +0,0 @@
"""
Custom routers for job sourcing .
"""
# third party imports
from rest_framework.routers import DefaultRouter
class OptionalSlashRouter(DefaultRouter):
"""
optional slash router class
"""
def __init__(self):
"""
explicitly appending '/' in urls if '/' doesn't exists for making common url patterns .
"""
super(OptionalSlashRouter, self).__init__()
self.trailing_slash = '/?'
-29
View File
@@ -1,29 +0,0 @@
"""
web_admin tasks file
"""
# third party imports
from celery import shared_task
from templated_email import send_templated_mail
# django imports
from django.conf import settings
@shared_task
def send_email_otp(email, verification_code):
"""
used to send otp on email
:param email: e-mail
:param verification_code: otp
"""
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [email]
send_templated_mail(
template_name='email_reset_verification.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'verification_code': verification_code
}
)
return True
-177
View File
@@ -1,177 +0,0 @@
"""
This file used for file uploaded
"""
import datetime
# python imports
import logging
import mimetypes
import os
import boto3
from django.core.files.storage import FileSystemStorage
# django imports
from django.utils.crypto import get_random_string
from rest_framework.status import HTTP_400_BAD_REQUEST, HTTP_200_OK
from base import constants
from base.constants import NUMBER
# local import
from zod_bank.settings import base_settings as settings
from zod_bank.settings.base_settings import BASE_DIR
def image_upload(folder, file_name, data):
"""
Function to upload files
:param folder:folder location string
:param file_name:file_name without ext string
:param data:data file obj
:return:Dictionary
"""
status = HTTP_400_BAD_REQUEST
img_name = None
error = None
try:
s3_client = boto3.client('s3',
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_DEFAULT_REGION
)
bucket_name = settings.BUCKET_NAME
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/tmp')
fss = FileSystemStorage()
file = fss.save('tmp/' + str(file_name), data)
fss.url(file)
tmp_file = os.path.join(MEDIA_ROOT, str(file_name))
s3_client.upload_file(
tmp_file, bucket_name, folder + str(file_name),
ExtraArgs={'ACL': 'public-read', 'ContentType': data.content_type}
)
os.unlink(tmp_file)
img_name = file_name
status = HTTP_200_OK
except Exception as e:
error = e
logging.error(e)
return status, error, img_name
def file_delete(folder, file_name):
"""
To delete common file
:param folder: folder name str
:param file_name: file_name string type
"""
status = HTTP_400_BAD_REQUEST
error = None
try:
s3_client = boto3.client('s3',
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_DEFAULT_REGION
)
s3_client.delete_object(Bucket=settings.BUCKET_NAME, Key=str(folder) + str(file_name))
status = HTTP_200_OK
except Exception as e:
error = e
return status, error
def get_aws_obj(folder, file_name):
"""
To get aws file obj
:param folder: folder string type
:param file_name: file_name string type
"""
status = HTTP_400_BAD_REQUEST
obj = None
try:
s3_client = boto3.client('s3',
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_DEFAULT_REGION
)
file_name = folder + file_name
obj = s3_client.get_object(Bucket=settings.BUCKET_NAME, Key=file_name)
status = HTTP_200_OK
except Exception as e:
logging.error(e)
return status, obj
def upload_image(post_data, folder):
"""
:param post_data:
:param folder: string type
:return:
"""
upload_obj = None
# Check Post data
if post_data:
date_now = datetime.datetime.now()
file_extension = os.path.splitext(str(post_data.name))
file_extension = file_extension[constants.NUMBER['one']].split(".")[constants.NUMBER['one']].lower()
rand = get_random_string(NUMBER['twelve'])
image_name = str(rand) + date_now.strftime("%s") + "." + file_extension
upload_obj = image_upload(folder, image_name, post_data)
return upload_obj
def upload_voice_kit_image(post_data, folder, image_dir):
"""
:param post_data:
:param folder: string type
:param image_dir: image_dir
:return:
"""
upload_obj = None
# Check Post data
if post_data:
date_now = datetime.datetime.now()
file_extension = os.path.splitext(str(post_data))
file_extension = file_extension[constants.NUMBER['one']].split(".")[constants.NUMBER['one']].lower()
rand = get_random_string(NUMBER['twelve'])
image_name = str(rand) + date_now.strftime("%s") + "." + file_extension
upload_obj = voice_kit_image_upload(folder, image_name, post_data, image_dir)
return upload_obj
def voice_kit_image_upload(folder, file_name, data, image_dir):
"""
Function to upload files
:param folder:folder location string
:param file_name:file_name without ext string
:param data:data file obj
:return:Dictionary
"""
status = HTTP_400_BAD_REQUEST
img_name = None
error = None
try:
s3_client = boto3.client('s3',
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_DEFAULT_REGION
)
bucket_name = settings.BUCKET_NAME
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/tmp')
fss = FileSystemStorage()
with open(image_dir+data, 'rb') as f:
file = fss.save('tmp/' + str(file_name), f)
fss.url(file)
tmp_file = os.path.join(MEDIA_ROOT, str(file_name))
s3_client.upload_file(
tmp_file, bucket_name, folder + str(file_name),
ExtraArgs={'ACL': 'public-read', 'ContentType': mimetypes.guess_type(file_name)[0]}
)
os.unlink(tmp_file)
img_name = file_name
status = HTTP_200_OK
except Exception as e:
error = e
logging.error(e)
return status, error, img_name
Binary file not shown.
-39
View File
@@ -1,39 +0,0 @@
version: '3'
services:
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "8000:8000"
volumes:
- ./nginx:/etc/nginx/conf.d
- .:/usr/src/app
depends_on:
- web
web:
build: .
container_name: django
restart: always
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
volumes:
- .:/usr/src/app
broker:
image: rabbitmq:3.7
container_name: rabbitmq
volumes:
- .:/usr/src/app
ports:
- 5673:5673
worker:
build: .
image: celery
container_name: dev_celery
restart: "always"
command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E"
volumes:
- .:/usr/src/app
depends_on:
- broker
-23
View File
@@ -1,23 +0,0 @@
"""Guardian admin"""
"""Third party Django app"""
from django.contrib import admin
"""Import Django app"""
from .models import Guardian, JuniorTask
# Register your models here.
@admin.register(Guardian)
class GuardianAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['user', 'family_name']
def __str__(self):
"""Return email id"""
return self.user__email
@admin.register(JuniorTask)
class TaskAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['id', 'task_name', 'task_status', 'junior', 'due_date', 'points', 'created_at', 'updated_at']
def __str__(self):
"""Return email id"""
return str(self.task_name) + str(self.points)
-9
View File
@@ -1,9 +0,0 @@
"""Guardian app file"""
"""Third party Django app"""
from django.apps import AppConfig
class CustodianConfig(AppConfig):
"""Guardian config"""
default_auto_field = 'django.db.models.BigAutoField'
name = 'guardian'
-43
View File
@@ -1,43 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-23 12:05
from django.conf import settings
import django.contrib.postgres.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Guardian',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('country_code', models.IntegerField(blank=True, null=True)),
('phone', models.CharField(blank=True, default=None, max_length=31, null=True)),
('family_name', models.CharField(blank=True, default=None, max_length=50, null=True)),
('gender', models.CharField(blank=True, choices=[('1', 'Male'), ('2', 'Female')], default=None, max_length=15, null=True)),
('dob', models.DateField(blank=True, default=None, max_length=15, null=True)),
('guardian_code', models.CharField(blank=True, default=None, max_length=10, null=True)),
('junior_code', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, default=None, max_length=10, null=True), null=True, size=None)),
('referral_code', models.CharField(blank=True, default=None, max_length=10, null=True)),
('referral_code_used', models.CharField(blank=True, default=None, max_length=10, null=True)),
('is_active', models.BooleanField(default=True)),
('is_complete_profile', models.BooleanField(default=False)),
('passcode', models.IntegerField(blank=True, default=None, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guardian_profile', to=settings.AUTH_USER_MODEL, verbose_name='Email')),
],
options={
'verbose_name': 'Guardian',
'db_table': 'guardians',
},
),
]
@@ -1,17 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-27 06:15
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('guardian', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='guardian',
name='junior_code',
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-27 13:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0002_remove_guardian_junior_code'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=30, null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-28 06:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0003_guardian_country_name'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to='images/'),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-29 06:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0004_guardian_image'),
]
operations = [
migrations.AlterField(
model_name='guardian',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to=''),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-29 12:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0005_alter_guardian_image'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='is_verified',
field=models.BooleanField(default=False),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-30 10:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0006_guardian_is_verified'),
]
operations = [
migrations.AlterField(
model_name='guardian',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=100, null=True),
),
]
-36
View File
@@ -1,36 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-04 09:24
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('junior', '0006_alter_junior_country_name'),
('guardian', '0007_alter_guardian_country_name'),
]
operations = [
migrations.CreateModel(
name='JuniorTask',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('task_name', models.CharField(max_length=100)),
('task_description', models.CharField(max_length=500)),
('points', models.IntegerField(default=5)),
('due_date', models.DateField(blank=True, null=True)),
('image', models.ImageField(blank=True, default=None, null=True, upload_to='')),
('task_status', models.CharField(blank=True, choices=[('1', 'pending'), ('2', 'in-progress'), ('3', 'rejected'), ('4', 'requested'), ('5', 'completed')], default='pending', max_length=15, null=True)),
('is_active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('guardian', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guardian', to='guardian.guardian', verbose_name='Guardian')),
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior', to='junior.junior', verbose_name='Junior')),
],
options={
'verbose_name': 'Junior Task',
'db_table': 'junior_task',
},
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-04 12:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0008_juniortask'),
]
operations = [
migrations.AlterField(
model_name='juniortask',
name='image',
field=models.URLField(blank=True, default=None, null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-04 13:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0009_alter_juniortask_image'),
]
operations = [
migrations.AlterField(
model_name='juniortask',
name='task_status',
field=models.CharField(choices=[('1', 'pending'), ('2', 'in-progress'), ('3', 'rejected'), ('4', 'requested'), ('5', 'completed')], default='pending', max_length=15),
),
]
@@ -1,28 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-05 11:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0010_alter_juniortask_task_status'),
]
operations = [
migrations.AddField(
model_name='juniortask',
name='default_image',
field=models.ImageField(blank=True, default=None, null=True, upload_to=''),
),
migrations.AddField(
model_name='juniortask',
name='is_approved',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='juniortask',
name='task_status',
field=models.CharField(choices=[('1', 'pending'), ('2', 'in-progress'), ('3', 'rejected'), ('4', 'requested'), ('5', 'completed')], default=1, max_length=15),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-06 05:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0011_juniortask_default_image_juniortask_is_approved_and_more'),
]
operations = [
migrations.AlterField(
model_name='juniortask',
name='default_image',
field=models.URLField(blank=True, default=None, null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-06 06:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0012_alter_juniortask_default_image'),
]
operations = [
migrations.AlterField(
model_name='guardian',
name='image',
field=models.URLField(blank=True, default=None, null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-11 11:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0013_alter_guardian_image'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='signup_method',
field=models.CharField(choices=[('1', 'manual'), ('2', 'google'), ('3', 'apple')], default='1', max_length=31),
),
]
@@ -1,21 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-14 09:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('guardian', '0014_guardian_signup_method'),
]
operations = [
migrations.AlterModelOptions(
name='guardian',
options={'verbose_name': 'Guardian', 'verbose_name_plural': 'Guardian'},
),
migrations.AlterModelOptions(
name='juniortask',
options={'verbose_name': 'Junior Task', 'verbose_name_plural': 'Junior Task'},
),
]
@@ -1,28 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-18 07:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0015_alter_guardian_options_alter_juniortask_options'),
]
operations = [
migrations.AddField(
model_name='juniortask',
name='completed_on',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='juniortask',
name='rejected_on',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='juniortask',
name='requested_on',
field=models.DateTimeField(blank=True, null=True),
),
]
@@ -1,23 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-24 13:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0016_juniortask_completed_on_juniortask_rejected_on_and_more'),
]
operations = [
migrations.AddField(
model_name='juniortask',
name='is_invited',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='juniortask',
name='is_password_set',
field=models.BooleanField(default=True),
),
]
@@ -1,21 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-24 13:23
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('guardian', '0017_juniortask_is_invited_juniortask_is_password_set'),
]
operations = [
migrations.RemoveField(
model_name='juniortask',
name='is_invited',
),
migrations.RemoveField(
model_name='juniortask',
name='is_password_set',
),
]
@@ -1,23 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-24 13:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0018_remove_juniortask_is_invited_and_more'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='is_invited',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='guardian',
name='is_password_set',
field=models.BooleanField(default=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-25 07:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0019_guardian_is_invited_guardian_is_password_set'),
]
operations = [
migrations.AlterField(
model_name='juniortask',
name='task_status',
field=models.CharField(choices=[('1', 'pending'), ('2', 'in-progress'), ('3', 'rejected'), ('4', 'requested'), ('5', 'completed'), ('6', 'expired')], default=1, max_length=15),
),
]
View File
-136
View File
@@ -1,136 +0,0 @@
"""Guardian model file"""
"""Third party Django app"""
from django.db import models
from django.contrib.auth import get_user_model
"""Import Django app"""
from base.constants import GENDERS, TASK_STATUS, PENDING, TASK_POINTS, SIGNUP_METHODS
"""import Junior model"""
import junior.models
"""Add user model"""
User = get_user_model()
# Create your models here.
# Define junior model with
# various fields like
# phone, country code,
# country name,
# gender,
# date of birth,
# profile image,
# signup method,
# guardian code,
# referral code,
# referral code that used by the guardian
# is invited guardian
# profile is active or not
# profile is complete or not
# passcode
# guardian is verified or not
"""Define junior task model"""
# define name of the Task
# task description
# points of the task
# default image of the task
# image uploaded by junior
# task status
# task approved or not
# Create your models here.
class Guardian(models.Model):
"""Guardian model"""
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='guardian_profile', verbose_name='Email')
"""Contact details"""
country_code = models.IntegerField(blank=True, null=True)
phone = models.CharField(max_length=31, null=True, blank=True, default=None)
"""country name of the guardian"""
country_name = models.CharField(max_length=100, null=True, blank=True, default=None)
"""Image info"""
image = models.URLField(null=True, blank=True, default=None)
"""Personal info"""
family_name = models.CharField(max_length=50, null=True, blank=True, default=None)
"""gender of the guardian"""
gender = models.CharField(choices=GENDERS, max_length=15, null=True, blank=True, default=None)
"""date of birth of the guardian"""
dob = models.DateField(max_length=15, null=True, blank=True, default=None)
# invited junior"""
is_invited = models.BooleanField(default=False)
# Profile activity"""
is_password_set = models.BooleanField(default=True)
"""Profile activity"""
is_active = models.BooleanField(default=True)
"""guardian is verified or not"""
is_verified = models.BooleanField(default=False)
"""guardian profile is complete or not"""
is_complete_profile = models.BooleanField(default=False)
"""passcode of the guardian profile"""
passcode = models.IntegerField(null=True, blank=True, default=None)
"""Sign up method"""
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
"""Guardian Codes"""
guardian_code = models.CharField(max_length=10, null=True, blank=True, default=None)
"""Referral code"""
referral_code = models.CharField(max_length=10, null=True, blank=True, default=None)
"""Referral code that is used by guardian while signup"""
referral_code_used = models.CharField(max_length=10, null=True, blank=True, default=None)
"""Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
db_table = 'guardians'
"""verbose name of the model"""
verbose_name = 'Guardian'
"""change another name"""
verbose_name_plural = 'Guardian'
def __str__(self):
"""Return email id"""
return f'{self.user}'
class JuniorTask(models.Model):
"""Junior Task details model"""
guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE, related_name='guardian', verbose_name='Guardian')
"""task details"""
task_name = models.CharField(max_length=100)
"""task description"""
task_description = models.CharField(max_length=500)
"""points of the task"""
points = models.IntegerField(default=TASK_POINTS)
"""last date of the task"""
due_date = models.DateField(auto_now_add=False, null=True, blank=True)
"""Images of task that is upload by guardian"""
default_image = models.URLField(null=True, blank=True, default=None)
"""image that is uploaded by junior"""
image = models.URLField(null=True, blank=True, default=None)
"""associated junior with the task"""
junior = models.ForeignKey('junior.Junior', on_delete=models.CASCADE, related_name='junior', verbose_name='Junior')
"""task status"""
task_status = models.CharField(choices=TASK_STATUS, max_length=15, default=PENDING)
"""task is active or not"""
is_active = models.BooleanField(default=True)
"""Task is approved or not"""
is_approved = models.BooleanField(default=False)
"""request task on particular date"""
requested_on = models.DateTimeField(auto_now_add=False, null=True, blank=True)
"""reject task on particular date"""
rejected_on = models.DateTimeField(auto_now_add=False, null=True, blank=True)
"""complete task on particular date"""
completed_on = models.DateTimeField(auto_now_add=False, null=True, blank=True)
"""Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
db_table = 'junior_task'
"""verbose name of the model"""
verbose_name = 'Junior Task'
verbose_name_plural = 'Junior Task'
def __str__(self):
"""Return email id"""
return f'{self.task_name}'
-430
View File
@@ -1,430 +0,0 @@
"""Serializer of Guardian"""
# third party imports
import logging
from rest_framework import serializers
# Import Refresh token of jwt
from rest_framework_simplejwt.tokens import RefreshToken
from django.db import transaction
from django.contrib.auth.models import User
from datetime import datetime, time
import pytz
from django.utils import timezone
# Import guardian's model,
# Import junior's model,
# Import account's model,
# Import constant from
# base package,
# Import messages from
# base package,
# Import some functions
# local imports
from .models import Guardian, JuniorTask
from account.models import UserProfile, UserEmailOtp, UserNotification
from account.utils import generate_code
from junior.serializers import JuniorDetailSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, JUN, ZOD, GRD
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
# notification's constant
from notifications.constants import TASK_POINTS, TASK_REJECTED
# send notification function
from notifications.utils import send_notification
# In this serializer file
# define user serializer,
# create guardian serializer,
# task serializer,
# guardian serializer,
# task details serializer,
# top junior serializer,
# guardian profile serializer,
# approve junior serializer,
# approve task serializer,
class UserSerializer(serializers.ModelSerializer):
"""User serializer"""
auth_token = serializers.SerializerMethodField('get_auth_token')
class Meta(object):
"""Meta info"""
model = User
fields = ['id', 'email', 'password', 'auth_token']
def get_auth_token(self, obj):
"""generate auth token"""
refresh = RefreshToken.for_user(obj)
access_token = str(refresh.access_token)
return access_token
def create(self, validated_data):
"""fetch data"""
email = validated_data.get('email')
user_type = self.context
password = validated_data.get('password')
try:
"""Create user profile"""
user = User.objects.create_user(username=email, email=email, password=password)
UserNotification.objects.get_or_create(user=user)
if user_type == str(NUMBER['one']):
# create junior profile
Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
referral_code=generate_code(ZOD, user.id))
if user_type == str(NUMBER['two']):
# create guardian profile
Guardian.objects.create(user=user, guardian_code=generate_code(GRD, user.id),
referral_code=generate_code(ZOD, user.id))
return user
except Exception as e:
"""Error handling"""
logging.error(e)
otp = UserEmailOtp.objects.filter(email=email).last()
otp_verified = False
if otp and otp.is_verified:
otp_verified = True
raise serializers.ValidationError({"details":ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
"code": 400, "status":"failed",
})
# update guardian profile
class CreateGuardianSerializer(serializers.ModelSerializer):
"""Create guardian serializer"""
"""Basic info"""
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
email = serializers.SerializerMethodField('get_email')
"""Contact details"""
phone = serializers.CharField(max_length=20, required=False)
country_code = serializers.IntegerField(required=False)
# basic info
family_name = serializers.CharField(max_length=100, required=False)
dob = serializers.DateField(required=False)
# code info
referral_code = serializers.CharField(max_length=100, required=False)
# image info
image = serializers.URLField(required=False)
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id', 'first_name', 'last_name', 'email', 'phone', 'family_name', 'gender', 'country_code',
'dob', 'referral_code', 'passcode', 'is_complete_profile', 'referral_code_used',
'country_name', 'image']
def get_first_name(self,obj):
"""first name of guardian"""
return obj.user.first_name
def get_last_name(self,obj):
"""last name of guardian"""
return obj.user.last_name
def get_email(self,obj):
"""emailof guardian"""
return obj.user.email
def create(self, validated_data):
"""Create guardian profile"""
user = User.objects.filter(username=self.context['user']).last()
if user:
"""Save first and last name of guardian"""
if self.context.get('first_name') != '' and self.context.get('first_name') is not None:
user.first_name = self.context.get('first_name')
if self.context.get('last_name') != '' and self.context.get('last_name') is not None:
user.last_name = self.context.get('last_name')
user.save()
"""Create guardian data"""
guardian = Guardian.objects.filter(user=self.context['user']).last()
if guardian:
"""update details according to the data get from request"""
guardian.gender = validated_data.get('gender',guardian.gender)
guardian.family_name = validated_data.get('family_name', guardian.family_name)
guardian.dob = validated_data.get('dob',guardian.dob)
"""Update country code and phone number"""
guardian.phone = validated_data.get('phone', guardian.phone)
guardian.country_code = validated_data.get('country_code', guardian.country_code)
guardian.passcode = validated_data.get('passcode', guardian.passcode)
guardian.country_name = validated_data.get('country_name', guardian.country_name)
guardian.image = validated_data.get('image', guardian.image)
"""Complete profile of the junior if below all data are filled"""
complete_profile_field = all([guardian.gender, guardian.country_name,
guardian.dob, user.first_name,
guardian.image])
guardian.is_complete_profile = False
if complete_profile_field:
guardian.is_complete_profile = True
referral_code_used = validated_data.get('referral_code_used')
update_referral_points(guardian.referral_code, referral_code_used)
guardian.referral_code_used = validated_data.get('referral_code_used', guardian.referral_code_used)
guardian.save()
return guardian
def save(self, **kwargs):
"""Save the data into junior table"""
with transaction.atomic():
instance = super().save(**kwargs)
return instance
class TaskSerializer(serializers.ModelSerializer):
"""Task serializer"""
class Meta(object):
"""Meta info"""
model = JuniorTask
fields = ['id', 'task_name','task_description','points', 'due_date', 'junior', 'default_image']
def validate_due_date(self, value):
"""validation on due date"""
if value < datetime.today().date():
raise serializers.ValidationError({"details": ERROR_CODE['2046'],
"code": 400, "status": "failed",
})
return value
def create(self, validated_data):
"""create default task image data"""
validated_data['guardian'] = Guardian.objects.filter(user=self.context['user']).last()
# update image of the task
images = self.context['image']
validated_data['default_image'] = images
instance = JuniorTask.objects.create(**validated_data)
return instance
class GuardianDetailSerializer(serializers.ModelSerializer):
"""junior serializer"""
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
def get_auth(self, obj):
"""user email address"""
return obj.user.username
def get_first_name(self, obj):
"""user first name"""
return obj.user.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.user.last_name
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at']
class TaskDetailsSerializer(serializers.ModelSerializer):
"""Task detail serializer"""
junior = JuniorDetailSerializer()
remaining_time = serializers.SerializerMethodField('get_remaining_time')
is_expired = serializers.SerializerMethodField('get_is_expired')
def get_remaining_time(self, obj):
""" remaining time to complete task"""
due_date_datetime = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time
# current_datetime = real_time()
# new code
due_date_datetime = due_date_datetime.astimezone(pytz.utc)
current_datetime = timezone.now().astimezone(pytz.utc)
# Perform the subtraction
if due_date_datetime > current_datetime:
time_difference = due_date_datetime - current_datetime
time_only = convert_timedelta_into_datetime(time_difference)
return str(time_difference.days) + ' days ' + str(time_only)
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
def get_is_expired(self, obj):
""" task expired or not"""
if obj.due_date < datetime.today().date():
return True
return False
class Meta(object):
"""Meta info"""
model = JuniorTask
fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image',
'requested_on', 'rejected_on', 'completed_on', 'is_expired',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TaskDetailsjuniorSerializer(serializers.ModelSerializer):
"""Task detail serializer"""
guardian = GuardianDetailSerializer()
remaining_time = serializers.SerializerMethodField('get_remaining_time')
def get_remaining_time(self, obj):
""" remaining time to complete task"""
due_date_datetime = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time
# current_datetime = real_time()
# new code
due_date_datetime = due_date_datetime.astimezone(pytz.utc)
current_datetime = timezone.now().astimezone(pytz.utc)
# Perform the subtraction
if due_date_datetime > current_datetime:
time_difference = due_date_datetime - current_datetime
time_only = convert_timedelta_into_datetime(time_difference)
return str(time_difference.days) + ' days ' + str(time_only)
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
class Meta(object):
"""Meta info"""
model = JuniorTask
fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image',
'requested_on', 'rejected_on', 'completed_on',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TopJuniorSerializer(serializers.ModelSerializer):
"""Top junior serializer"""
junior = JuniorDetailSerializer()
position = serializers.IntegerField()
class Meta(object):
"""Meta info"""
model = JuniorPoints
fields = ['id', 'junior', 'total_points', 'position', 'created_at', 'updated_at']
def to_representation(self, instance):
"""Convert instance to representation"""
representation = super().to_representation(instance)
representation['position'] = instance.position
return representation
class GuardianProfileSerializer(serializers.ModelSerializer):
"""junior serializer"""
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
total_count = serializers.SerializerMethodField('get_total_count')
complete_field_count = serializers.SerializerMethodField('get_complete_field_count')
notification_count = serializers.SerializerMethodField('get_notification_count')
def get_auth(self, obj):
"""user email address"""
return obj.user.username
def get_first_name(self, obj):
"""user first name"""
return obj.user.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.user.last_name
def get_total_count(self, obj):
"""total fields count"""
return NUMBER['five']
def get_complete_field_count(self, obj):
"""total filled fields count"""
total_field_list = [obj.user.first_name, obj.country_name,
obj.gender, obj.dob, obj.image]
# count total complete field
total_complete_field = [data for data in total_field_list if data != '' and data is not None]
return len(total_complete_field)
def get_notification_count(self, obj):
"""total notification count"""
return 0
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id', 'email', 'first_name', 'last_name', 'country_name','country_code', 'phone', 'gender', 'dob',
'guardian_code', 'notification_count', 'total_count', 'complete_field_count', 'referral_code',
'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method',
'updated_at', 'passcode']
class ApproveJuniorSerializer(serializers.ModelSerializer):
"""approve junior serializer"""
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'guardian_code']
def create(self, validated_data):
"""update guardian code"""
instance = self.context['junior']
instance.guardian_code = [self.context['guardian_code']]
instance.guardian_code_approved = True
instance.save()
return instance
class ApproveTaskSerializer(serializers.ModelSerializer):
"""approve task serializer"""
class Meta(object):
"""Meta info"""
model = JuniorTask
fields = ['id', 'task_status', 'is_approved']
def create(self, validated_data):
"""update task status """
with transaction.atomic():
instance = self.context['task_instance']
junior = self.context['junior']
junior_details = Junior.objects.filter(id=junior).last()
junior_data, created = JuniorPoints.objects.get_or_create(junior=junior_details)
if self.context['action'] == str(NUMBER['one']):
# approve the task
instance.task_status = str(NUMBER['five'])
instance.is_approved = True
# update total task point
junior_data.total_points = junior_data.total_points + instance.points
# update complete time of task
# instance.completed_on = real_time()
instance.completed_on = timezone.now().astimezone(pytz.utc)
send_notification.delay(TASK_POINTS, None, junior_details.auth.id, {})
else:
# reject the task
instance.task_status = str(NUMBER['three'])
instance.is_approved = False
# update total task point
junior_data.total_points = junior_data.total_points - instance.points
# update reject time of task
# instance.rejected_on = real_time()
instance.rejected_on = timezone.now().astimezone(pytz.utc)
send_notification.delay(TASK_REJECTED, None, junior_details.auth.id, {})
instance.save()
junior_data.save()
return junior_details
class GuardianDetailListSerializer(serializers.ModelSerializer):
"""Guardian serializer"""
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
email = serializers.SerializerMethodField('get_email')
image = serializers.SerializerMethodField('get_image')
guardian_id = serializers.SerializerMethodField('get_guardian_id')
class Meta(object):
"""Meta info"""
model = JuniorGuardianRelationship
fields = ['guardian_id', 'first_name', 'last_name', 'email', 'relationship', 'image', 'created_at',
'updated_at']
def get_guardian_id(self,obj):
"""first name of guardian"""
return obj.guardian.id
def get_first_name(self,obj):
"""first name of guardian"""
return obj.guardian.user.first_name
def get_last_name(self,obj):
"""last name of guardian"""
return obj.guardian.user.last_name
def get_email(self,obj):
"""emailof guardian"""
return obj.guardian.user.email
def get_image(self,obj):
"""first name of guardian"""
return obj.guardian.image
-7
View File
@@ -1,7 +0,0 @@
"""task files"""
"""Django import"""
import secrets
def generate_otp():
"""generate random otp"""
digits = "0123456789"
return "".join(secrets.choice(digits) for _ in range(6))
-5
View File
@@ -1,5 +0,0 @@
"""Test file of Guardian"""
"""Third party Django app"""
from django.test import TestCase
# Create your tests here.
-45
View File
@@ -1,45 +0,0 @@
""" Urls files"""
"""Django import"""
from django.urls import path, include
from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView,
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView,
GuardianListAPIView)
"""Third party import"""
from rest_framework import routers
"""Define Router"""
router = routers.SimpleRouter()
# API End points with router
# in this file
# we define various api end point
# that is covered in this guardian
# section API:- like
# sign-up, create guardian profile,
# create-task,
# all task list, top junior,
# filter-task"""
"""Sign up API"""
router.register('sign-up', SignupViewset, basename='sign-up')
# Create guardian profile API"""
router.register('create-guardian-profile', UpdateGuardianProfile, basename='update-guardian-profile')
# Create Task API"""
router.register('create-task', CreateTaskAPIView, basename='create-task')
# All Task list API"""
router.register('all-task-list', AllTaskListAPIView, basename='all-task-list')
# Task list bases on the status API"""
router.register('task-list', TaskListAPIView, basename='task-list')
# Leaderboard API"""
router.register('top-junior', TopJuniorListAPIView, basename='top-junior')
# Search Task list on the bases of status, due date, and task title API"""
router.register('filter-task', SearchTaskListAPIView, basename='filter-task')
# Approve junior API"""
router.register('approve-junior', ApproveJuniorAPIView, basename='approve-junior')
# Approve junior API"""
router.register('approve-task', ApproveTaskAPIView, basename='approve-task')
# guardian list API"""
router.register('guardian-list', GuardianListAPIView, basename='guardian-list')
# Define Url pattern"""
urlpatterns = [
path('api/v1/', include(router.urls)),
]
-109
View File
@@ -1,109 +0,0 @@
"""Utiles file of guardian"""
"""Django import"""
import oss2
"""Import setting"""
from django.conf import settings
import logging
import requests
from django.core.exceptions import ObjectDoesNotExist
"""Import tempfile"""
import tempfile
# Import date time module's function
from datetime import datetime, time
# import Number constant
from base.constants import NUMBER, time_url
# Import Junior's model
from junior.models import Junior, JuniorPoints
# Import guardian's model
from .models import JuniorTask
# Import app from celery
from zod_bank.celery import app
# notification's constant
from notifications.constants import REFERRAL_POINTS
# send notification function
from notifications.utils import send_notification
# Define upload image on
# ali baba cloud
# firstly save image
# in temporary file
# then check bucket name
# then upload on ali baba
# bucket and reform the image url"""
# fetch real time data without depend on system time
# convert time delta into date time object
# update junior total points
# update referral points
# if any one use referral code of the junior
# junior earn 5 points
def upload_image_to_alibaba(image, filename):
"""upload image on oss alibaba bucket"""
# Save the image object to a temporary file
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
"""write image in temporary file"""
if type(image) == bytes:
temp_file.write(image)
else:
temp_file.write(image.read())
"""auth of bucket"""
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
"""fetch bucket details"""
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
# Upload the temporary file to Alibaba OSS
bucket.put_object_from_file(filename, temp_file.name)
"""create perfect url for image"""
new_filename = filename.replace(' ', '%20')
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}"
def real_time():
"""fetch real time from world time api"""
url = time_url
response = requests.get(url)
if response.status_code == 200:
data = response.json()
time_str = data['datetime']
realtime = datetime.fromisoformat(time_str.replace('Z', '+00:00')).replace(tzinfo=None)
return realtime
else:
logging.error("Could not fetch error")
return None
def convert_timedelta_into_datetime(time_difference):
"""convert date time"""
# convert timedelta into datetime format
hours = time_difference.seconds // NUMBER['thirty_six_hundred']
minutes = (time_difference.seconds // NUMBER['sixty']) % NUMBER['sixty']
seconds = time_difference.seconds % NUMBER['sixty']
microseconds = time_difference.microseconds
# Create a new time object with the extracted time components
time_only = time(hours, minutes, seconds, microseconds)
return time_only
def update_referral_points(referral_code, referral_code_used):
"""Update benefit of referral points"""
junior_queryset = Junior.objects.filter(referral_code=referral_code_used).last()
if junior_queryset and junior_queryset.referral_code != referral_code:
# create data if junior points is not exist
junior_query, created = JuniorPoints.objects.get_or_create(junior=junior_queryset)
if junior_query:
# update junior total points and referral points
junior_query.total_points = junior_query.total_points + NUMBER['five']
junior_query.referral_points = junior_query.referral_points + NUMBER['five']
junior_query.save()
send_notification.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {})
@app.task
def update_expired_task_status(data=None):
"""
Update task of the status if due date is in past
"""
try:
task_status = [str(NUMBER['one']), str(NUMBER['two']), str(NUMBER['four'])]
JuniorTask.objects.filter(due_date__lt=datetime.today().date(),
task_status__in=task_status).update(task_status=str(NUMBER['six']))
except ObjectDoesNotExist as e:
logging.error(str(e))
-384
View File
@@ -1,384 +0,0 @@
"""Views of Guardian"""
# django imports
# Import IsAuthenticated
# Import viewsets and status
# Import PageNumberPagination
# Import User
# Import timezone
from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status
from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User
from rest_framework.filters import SearchFilter
from django.utils import timezone
# Import guardian's model,
# Import junior's model,
# Import account's model,
# Import constant from
# base package,
# Import messages from
# base package,
# Import some functions
# from utils file
# Import account's serializer
# Import account's task
# Import notification constant
# Import send_notification function
from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializer, TaskDetailsSerializer,
TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer,
GuardianDetailListSerializer)
from .models import Guardian, JuniorTask
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from account.models import UserEmailOtp, UserNotification
from .tasks import generate_otp
from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER
from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_CREATED, LEADERBOARD_RANKING
from notifications.utils import send_notification
""" Define APIs """
# Define Signup API,
# update guardian profile,
# list of all task
# list of task according to the status of the task
# create task API
# search task by name of the task API
# top junior API,
# approve junior API
# approve task API
# Create your views here.
# create approve task API
class SignupViewset(viewsets.ModelViewSet):
"""Signup view set"""
queryset = User.objects.all()
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
"""Create user profile"""
try:
if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]:
serializer = UserSerializer(context=request.data['user_type'], data=request.data)
if serializer.is_valid():
user = serializer.save()
"""Generate otp"""
otp = generate_otp()
# expire otp after 1 day
expiry = OTP_EXPIRY
# create user email otp object
UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
user_type=str(request.data['user_type']), expired_at=expiry)
"""Send email to the register user"""
send_otp_email(request.data['email'], otp)
# send push notification for registration
send_notification.delay(REGISTRATION, None, user.id, {})
return custom_response(SUCCESS_CODE['3001'],
response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class UpdateGuardianProfile(viewsets.ViewSet):
"""Update guardian profile"""
queryset = Guardian.objects.all()
serializer_class = CreateGuardianSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
"""Create guardian profile"""
try:
data = request.data
image = request.data.get('image')
image_url = ''
if image:
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
filename = f"images/{image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(image, filename)
data = {"image":image_url}
serializer = CreateGuardianSerializer(context={"user":request.user,
"first_name":request.data.get('first_name'),
"last_name": request.data.get('last_name'),
"image":image_url},
data=data)
if serializer.is_valid():
"""save serializer"""
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class AllTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
queryset = JuniorTask.objects.all()
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
queryset = JuniorTask.objects.filter(guardian__user=request.user)
# use TaskDetailsSerializer serializer
serializer = TaskDetailsSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
# class TaskListAPIView(viewsets.ModelViewSet):
# """Update guardian profile"""
# serializer_class = TaskDetailsSerializer
# permission_classes = [IsAuthenticated]
# pagination_class = PageNumberPagination
# http_method_names = ('get',)
#
# def list(self, request, *args, **kwargs):
# """Create guardian profile"""
# try:
# status_value = self.request.GET.get('status')
# search = self.request.GET.get('search')
# if search and str(status_value) == '0':
# queryset = JuniorTask.objects.filter(guardian__user=request.user,
# task_name__icontains=search).order_by('due_date', 'created_at')
# elif search and str(status_value) != '0':
# queryset = JuniorTask.objects.filter(guardian__user=request.user,task_name__icontains=search,
# task_status=status_value).order_by('due_date', 'created_at')
# if search is None and str(status_value) == '0':
# queryset = JuniorTask.objects.filter(guardian__user=request.user).order_by('due_date', 'created_at')
# elif search is None and str(status_value) != '0':
# queryset = JuniorTask.objects.filter(guardian__user=request.user,
# task_status=status_value).order_by('due_date','created_at')
# paginator = self.pagination_class()
# # use Pagination
# paginated_queryset = paginator.paginate_queryset(queryset, request)
# # use TaskDetailsSerializer serializer
# serializer = TaskDetailsSerializer(paginated_queryset, many=True)
# return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
# except Exception as e:
# return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class TaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
search_fields = ['task_name', ]
pagination_class = PageNumberPagination
http_method_names = ('get',)
def get_queryset(self):
queryset = JuniorTask.objects.filter(guardian__user=self.request.user
).prefetch_related('junior', 'junior__auth'
).order_by('due_date', 'created_at')
queryset = self.filter_queryset(queryset)
return queryset
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
status_value = self.request.GET.get('status')
queryset = self.get_queryset()
if status_value and status_value != '0':
queryset = queryset.filter(task_status=status_value)
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class CreateTaskAPIView(viewsets.ModelViewSet):
"""create task for junior"""
serializer_class = TaskSerializer
queryset = JuniorTask.objects.all()
http_method_names = ('post', )
def create(self, request, *args, **kwargs):
try:
image = request.data['default_image']
junior = request.data['junior']
allowed_extensions = ['.jpg', '.jpeg', '.png']
if not any(extension in str(image) for extension in allowed_extensions):
return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST)
if not junior.isnumeric():
"""junior value must be integer"""
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
data = request.data
if 'https' in str(image):
image_data = image
else:
filename = f"images/{image}"
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
image_url = upload_image_to_alibaba(image, filename)
image_data = image_url
data.pop('default_image')
# use TaskSerializer serializer
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data)
if serializer.is_valid():
# save serializer
serializer.save()
junior_id = Junior.objects.filter(id=junior).last()
send_notification.delay(TASK_CREATED, None, junior_id.auth.id, {})
return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class SearchTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
def get_queryset(self):
"""Get the queryset for the view"""
title = self.request.GET.get('title')
# fetch junior query
junior_queryset = JuniorTask.objects.filter(guardian__user=self.request.user, task_name__icontains=title)\
.order_by('due_date', 'created_at')
return junior_queryset
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
try:
queryset = self.get_queryset()
paginator = self.pagination_class()
# use pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskSerializer serializer
serializer = TaskDetailsSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class TopJuniorListAPIView(viewsets.ModelViewSet):
"""Top juniors list"""
serializer_class = TopJuniorSerializer
permission_classes = [IsAuthenticated]
queryset = JuniorPoints.objects.all()
def get_serializer_context(self):
# context list
context = super().get_serializer_context()
context.update({'view': self})
return context
def list(self, request, *args, **kwargs):
"""Fetch junior list of those who complete their tasks"""
try:
junior_total_points = self.get_queryset().order_by('-total_points')
# Update the position field for each JuniorPoints object
for index, junior in enumerate(junior_total_points):
junior.position = index + 1
send_notification.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {})
junior.save()
serializer = self.get_serializer(junior_total_points, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ApproveJuniorAPIView(viewsets.ViewSet):
"""approve junior by guardian"""
serializer_class = ApproveJuniorSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""Get the queryset for the view"""
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last()
return guardian, junior_queryset
def create(self, request, *args, **kwargs):
""" junior list"""
try:
queryset = self.get_queryset()
# action 1 is use for approve and 2 for reject
if request.data['action'] == '1':
# use ApproveJuniorSerializer serializer
serializer = ApproveJuniorSerializer(context={"guardian_code": queryset[0].guardian_code,
"junior": queryset[1], "action": request.data['action']},
data=request.data)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
else:
queryset[1].guardian_code = None
queryset[1].save()
return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ApproveTaskAPIView(viewsets.ViewSet):
"""approve junior by guardian"""
serializer_class = ApproveTaskSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""Get the queryset for the view"""
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# task query
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
guardian=guardian,
junior=self.request.data.get('junior_id')).last()
return guardian, task_queryset
def create(self, request, *args, **kwargs):
""" junior list"""
# action 1 is use for approve and 2 for reject
try:
queryset = self.get_queryset()
# use ApproveJuniorSerializer serializer
serializer = ApproveTaskSerializer(context={"guardian_code": queryset[0].guardian_code,
"task_instance": queryset[1],
"action": str(request.data['action']),
"junior": self.request.data['junior_id']},
data=request.data)
unexpected_task_status = [str(NUMBER['five']), str(NUMBER['six'])]
if (str(request.data['action']) == str(NUMBER['one']) and serializer.is_valid()
and queryset[1] and queryset[1].task_status not in unexpected_task_status):
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3025'], response_status=status.HTTP_200_OK)
elif (str(request.data['action']) == str(NUMBER['two']) and serializer.is_valid()
and queryset[1] and queryset[1].task_status not in unexpected_task_status):
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3026'], response_status=status.HTTP_200_OK)
else:
return custom_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class GuardianListAPIView(viewsets.ModelViewSet):
"""Guardian list of assosicated junior"""
serializer_class = GuardianDetailListSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def list(self, request, *args, **kwargs):
""" junior list"""
try:
guardian_data = JuniorGuardianRelationship.objects.filter(junior__auth__email=self.request.user)
# fetch junior object
if guardian_data:
# use GuardianDetailListSerializer serializer
serializer = GuardianDetailListSerializer(guardian_data, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(ERROR_CODE['2068'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
+35
View File
@@ -0,0 +1,35 @@
import type { Config as JestConfig } from 'jest';
import { pathsToModuleNameMapper } from 'ts-jest';
import { compilerOptions as tsConfigCompilerOptions } from './tsconfig.json';
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
type JestProjectConfig = Exclude<ArrayElement<JestConfig['projects']>, string>;
const baseJestConfig: JestConfig = {
// ...
};
const baseJestProjectConfig: JestProjectConfig = {
testEnvironment: 'node',
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
moduleNameMapper: pathsToModuleNameMapper(tsConfigCompilerOptions.paths, { prefix: '<rootDir>' }),
globalSetup: '<rootDir>/test/global-setup.ts',
};
const projects: { name?: string; rootDir: string }[] = [{ rootDir: '.' }];
export function buildJestConfig(config?: {
jestConfigs?: JestConfig;
jestProjectConfigs?: JestProjectConfig;
}): JestConfig {
return {
...baseJestConfig,
...config?.jestConfigs,
projects: projects.map(({ name, rootDir }) => ({
...baseJestProjectConfig,
...config?.jestProjectConfigs,
...(name && { displayName: name }),
rootDir,
})),
};
}
+16
View File
@@ -0,0 +1,16 @@
import type { Config } from 'jest';
import { buildJestConfig } from './jest.base.config';
const config: Config = buildJestConfig({
jestProjectConfigs: {
testRegex: '.*\\.spec\\.ts$',
coveragePathIgnorePatterns: ['__testing__', 'entities', '<rootDir>/src/db', 'index.ts'],
},
jestConfigs: {
coverageDirectory: 'coverage',
collectCoverageFrom: ['<rootDir>/src/**/*.(t|j)s'],
coverageReporters: ['clover', 'json', 'lcov', 'text', 'text-summary', 'cobertura'],
},
});
export default config;
View File
-29
View File
@@ -1,29 +0,0 @@
"""Junior admin"""
"""Third party Django app"""
from django.contrib import admin
"""Import Django app"""
from .models import Junior, JuniorPoints, JuniorGuardianRelationship
# Register your models here.
@admin.register(Junior)
class JuniorAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['auth', 'guardian_code']
def __str__(self):
"""Return email id"""
return self.auth__email
@admin.register(JuniorPoints)
class JuniorPointsAdmin(admin.ModelAdmin):
"""Junior Points Admin"""
list_display = ['junior', 'total_points', 'position']
def __str__(self):
"""Return email id"""
return self.junior.auth.email
@admin.register(JuniorGuardianRelationship)
class JuniorGuardianRelationshipAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['guardian', 'junior', 'relationship']
-8
View File
@@ -1,8 +0,0 @@
"""App file"""
"""Import AppConfig"""
from django.apps import AppConfig
class JuniorConfig(AppConfig):
"""Junior config"""
default_auto_field = 'django.db.models.BigAutoField'
name = 'junior'
-42
View File
@@ -1,42 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-23 12:05
from django.conf import settings
import django.contrib.postgres.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Junior',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('phone', models.CharField(blank=True, default=None, max_length=31, null=True)),
('country_code', models.IntegerField(blank=True, null=True)),
('gender', models.CharField(blank=True, choices=[('1', 'Male'), ('2', 'Female')], default=None, max_length=10, null=True)),
('dob', models.DateField(blank=True, default=None, max_length=15, null=True)),
('junior_code', models.CharField(blank=True, default=None, max_length=10, null=True)),
('guardian_code', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, default=None, max_length=10, null=True), null=True, size=None)),
('referral_code', models.CharField(blank=True, default=None, max_length=10, null=True)),
('referral_code_used', models.CharField(blank=True, default=None, max_length=10, null=True)),
('is_active', models.BooleanField(default=True)),
('is_complete_profile', models.BooleanField(default=False)),
('passcode', models.IntegerField(blank=True, default=None, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('auth', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_profile', to=settings.AUTH_USER_MODEL, verbose_name='Email')),
],
options={
'verbose_name': 'Junior',
'db_table': 'junior',
},
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-27 13:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='junior',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=30, null=True),
),
]
-18
View File
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-28 10:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0002_junior_country_name'),
]
operations = [
migrations.AddField(
model_name='junior',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to='images/'),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-29 06:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0003_junior_image'),
]
operations = [
migrations.AlterField(
model_name='junior',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to=''),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-29 12:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0004_alter_junior_image'),
]
operations = [
migrations.AddField(
model_name='junior',
name='is_verified',
field=models.BooleanField(default=False),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-06-30 10:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0005_junior_is_verified'),
]
operations = [
migrations.AlterField(
model_name='junior',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=100, null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-06 12:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0006_alter_junior_country_name'),
]
operations = [
migrations.AlterField(
model_name='junior',
name='image',
field=models.URLField(blank=True, default=None, null=True),
),
]
-28
View File
@@ -1,28 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-09 12:40
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('junior', '0007_alter_junior_image'),
]
operations = [
migrations.CreateModel(
name='JuniorPoints',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('total_task_points', models.IntegerField(blank=True, default=0, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('junior', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='junior_points', to='junior.junior')),
],
options={
'verbose_name': 'Junior Task Points',
'db_table': 'junior_task_points',
},
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-10 07:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0008_juniorpoints'),
]
operations = [
migrations.AddField(
model_name='juniorpoints',
name='position',
field=models.IntegerField(blank=True, default=99999, null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-11 11:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0009_juniorpoints_position'),
]
operations = [
migrations.AddField(
model_name='junior',
name='signup_method',
field=models.CharField(choices=[('1', 'manual'), ('2', 'google'), ('3', 'apple')], default='1', max_length=31),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-12 06:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0010_junior_signup_method'),
]
operations = [
migrations.AddField(
model_name='junior',
name='relationship',
field=models.CharField(blank=True, choices=[('1', 'parent'), ('2', 'legal_guardian')], default='1', max_length=31, null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-12 10:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0011_junior_relationship'),
]
operations = [
migrations.AddField(
model_name='junior',
name='is_invited',
field=models.BooleanField(default=False),
),
]
@@ -1,21 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-14 09:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('junior', '0012_junior_is_invited'),
]
operations = [
migrations.AlterModelOptions(
name='junior',
options={'verbose_name': 'Junior', 'verbose_name_plural': 'Junior'},
),
migrations.AlterModelOptions(
name='juniorpoints',
options={'verbose_name': 'Junior Task Points', 'verbose_name_plural': 'Junior Task Points'},
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-18 09:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0013_alter_junior_options_alter_juniorpoints_options'),
]
operations = [
migrations.AddField(
model_name='junior',
name='is_password_set',
field=models.BooleanField(default=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-19 09:40
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('junior', '0014_junior_is_password_set'),
]
operations = [
migrations.RenameField(
model_name='juniorpoints',
old_name='total_task_points',
new_name='total_points',
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-19 11:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0015_rename_total_task_points_juniorpoints_total_points'),
]
operations = [
migrations.AddField(
model_name='juniorpoints',
name='referral_points',
field=models.IntegerField(blank=True, default=0, null=True),
),
]
@@ -1,31 +0,0 @@
# Generated by Django 4.2.2 on 2023-07-25 07:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('guardian', '0020_alter_juniortask_task_status'),
('junior', '0016_juniorpoints_referral_points'),
]
operations = [
migrations.CreateModel(
name='JuniorGuardianRelationship',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('relationship', models.CharField(blank=True, choices=[('1', 'parent'), ('2', 'legal_guardian')], default='1', max_length=31, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('guardian', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guardian_relation', to='guardian.guardian', verbose_name='Guardian')),
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_relation', to='junior.junior', verbose_name='Junior')),
],
options={
'verbose_name': 'Junior Guardian Relation',
'verbose_name_plural': 'Junior Guardian Relation',
'db_table': 'junior_guardian_relation',
},
),
]
@@ -1,22 +0,0 @@
# Generated by Django 4.2.2 on 2023-08-02 11:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0017_juniorguardianrelationship'),
]
operations = [
migrations.RemoveField(
model_name='junior',
name='relationship',
),
migrations.AddField(
model_name='junior',
name='guardian_code_approved',
field=models.BooleanField(default=False),
),
]
View File
-139
View File
@@ -1,139 +0,0 @@
"""Junior model """
"""Import django"""
from django.db import models
"""Import get_user_model function"""
from django.contrib.auth import get_user_model
"""Import ArrayField"""
from django.contrib.postgres.fields import ArrayField
"""Import django app"""
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP
# Import guardian's model
from guardian.models import Guardian
"""Define User model"""
User = get_user_model()
# Create your models here.
# Define junior model with
# various fields like
# phone, country code,
# country name,
# gender,
# date of birth,
# profile image,
# relationship type of the guardian
# signup method,
# guardian code,
# junior code,
# referral code,
# referral code that used by the junior
# is invited junior
# profile is active or not
# profile is complete or not
# passcode
# junior is verified or not
"""Define junior points model"""
# points of the junior
# position of the junior
# define junior guardian relation model
class Junior(models.Model):
"""Junior model"""
auth = models.ForeignKey(User, on_delete=models.CASCADE, related_name='junior_profile', verbose_name='Email')
# Contact details"""
phone = models.CharField(max_length=31, null=True, blank=True, default=None)
country_code = models.IntegerField(blank=True, null=True)
# country name of the guardian"""
country_name = models.CharField(max_length=100, null=True, blank=True, default=None)
# Personal info"""
gender = models.CharField(max_length=10, choices=GENDERS, null=True, blank=True, default=None)
# Date of birth"""
dob = models.DateField(max_length=15, null=True, blank=True, default=None)
# Image of the junior"""
image = models.URLField(null=True, blank=True, default=None)
# Sign up method"""
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
# Codes"""
junior_code = models.CharField(max_length=10, null=True, blank=True, default=None)
# Guardian Codes"""
guardian_code = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None),null=True)
# Referral code"""
referral_code = models.CharField(max_length=10, null=True, blank=True, default=None)
# Referral code that is used by junior while signup"""
referral_code_used = models.CharField(max_length=10, null=True, blank=True, default=None)
# invited junior"""
is_invited = models.BooleanField(default=False)
# Profile activity"""
is_active = models.BooleanField(default=True)
# check password is set or not
is_password_set = models.BooleanField(default=True)
# junior profile is complete or not"""
is_complete_profile = models.BooleanField(default=False)
# passcode of the junior profile"""
passcode = models.IntegerField(null=True, blank=True, default=None)
# junior is verified or not"""
is_verified = models.BooleanField(default=False)
"""guardian code is approved or not"""
guardian_code_approved = models.BooleanField(default=False)
# Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
db_table = 'junior'
verbose_name = 'Junior'
# another name of the model"""
verbose_name_plural = 'Junior'
def __str__(self):
"""Return email id"""
return f'{self.auth}'
class JuniorPoints(models.Model):
"""Junior model"""
junior = models.OneToOneField(Junior, on_delete=models.CASCADE, related_name='junior_points')
# Total earned points"""
total_points = models.IntegerField(blank=True, null=True, default=0)
# referral points"""
referral_points = models.IntegerField(blank=True, null=True, default=0)
# position of the junior"""
position = models.IntegerField(blank=True, null=True, default=99999)
# Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
db_table = 'junior_task_points'
verbose_name = 'Junior Task Points'
# another name of the model"""
verbose_name_plural = 'Junior Task Points'
def __str__(self):
"""Return email id"""
return f'{self.junior.auth}'
class JuniorGuardianRelationship(models.Model):
"""Junior Guardian relationship model"""
guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE, related_name='guardian_relation',
verbose_name='Guardian')
# associated junior with the task
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='junior_relation', verbose_name='Junior')
# relation between guardian and junior"""
relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True,
default='1')
"""Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
db_table = 'junior_guardian_relation'
"""verbose name of the model"""
verbose_name = 'Junior Guardian Relation'
verbose_name_plural = 'Junior Guardian Relation'
def __str__(self):
"""Return email id"""
return f'{self.guardian.user}'
-483
View File
@@ -1,483 +0,0 @@
"""Serializer file for junior"""
# third party imports
import pytz
# django imports
from rest_framework import serializers
from django.contrib.auth.models import User
from django.db import transaction
from datetime import datetime
from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken
# local imports
from account.utils import send_otp_email, generate_code
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from guardian.tasks import generate_otp
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED
from guardian.models import Guardian, JuniorTask
from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
from notifications.utils import send_notification
from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION,
TASK_SUBMITTED)
class ListCharField(serializers.ListField):
"""Serializer for Array field"""
child = serializers.CharField()
def to_representation(self, data):
"""to represent the data"""
return data
def to_internal_value(self, data):
"""internal value"""
if isinstance(data, list):
return data
raise serializers.ValidationError({"details":ERROR_CODE['2025']})
class CreateJuniorSerializer(serializers.ModelSerializer):
"""Create junior serializer"""
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
email = serializers.SerializerMethodField('get_email')
phone = serializers.CharField(max_length=20, required=False)
country_code = serializers.IntegerField(required=False)
dob = serializers.DateField(required=False)
referral_code = serializers.CharField(max_length=100, required=False)
guardian_code = ListCharField(required=False)
image = serializers.URLField(required=False)
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'first_name', 'last_name', 'email', 'phone', 'gender', 'country_code', 'dob', 'referral_code',
'passcode', 'is_complete_profile', 'guardian_code', 'referral_code_used',
'country_name', 'image', 'is_invited']
def get_first_name(self,obj):
"""first name of junior"""
return obj.auth.first_name
def get_last_name(self,obj):
"""last name of junior"""
return obj.auth.last_name
def get_email(self,obj):
"""email of junior"""
return obj.auth.email
def create(self, validated_data):
"""Create junior profile"""
guardian_code = validated_data.get('guardian_code',None)
user = User.objects.filter(username=self.context['user']).last()
if user:
"""Save first and last name of junior"""
if self.context.get('first_name') != '' and self.context.get('first_name') is not None:
user.first_name = self.context.get('first_name')
if self.context.get('last_name') != '' and self.context.get('last_name') is not None:
user.last_name = self.context.get('last_name')
user.save()
"""Create junior data"""
junior = Junior.objects.filter(auth=self.context['user']).last()
if junior:
"""update details according to the data get from request"""
junior.gender = validated_data.get('gender',junior.gender)
"""Update guardian code"""
junior.guardian_code = validated_data.get('guardian_code', junior.guardian_code)
"""condition for guardian code"""
if guardian_code:
junior.guardian_code = guardian_code
guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last()
if guardian_data:
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior)
junior.dob = validated_data.get('dob', junior.dob)
junior.passcode = validated_data.get('passcode', junior.passcode)
junior.country_name = validated_data.get('country_name', junior.country_name)
"""Update country code and phone number"""
junior.phone = validated_data.get('phone', junior.phone)
junior.country_code = validated_data.get('country_code', junior.country_code)
junior.image = validated_data.get('image', junior.image)
"""Complete profile of the junior if below all data are filled"""
complete_profile_field = all([junior.gender, junior.country_name, junior.image,
junior.dob, user.first_name])
junior.is_complete_profile = False
if complete_profile_field:
junior.is_complete_profile = True
referral_code_used = validated_data.get('referral_code_used')
update_referral_points(junior.referral_code, referral_code_used)
junior.referral_code_used = validated_data.get('referral_code_used', junior.referral_code_used)
junior.save()
return junior
def save(self, **kwargs):
"""Save the data into junior table"""
with transaction.atomic():
instance = super().save(**kwargs)
return instance
class JuniorDetailSerializer(serializers.ModelSerializer):
"""junior serializer"""
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
def get_auth(self, obj):
"""user email address"""
return obj.auth.username
def get_first_name(self, obj):
"""user first name"""
return obj.auth.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.auth.last_name
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at',
'image', 'updated_at']
class JuniorDetailListSerializer(serializers.ModelSerializer):
"""junior serializer"""
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
assigned_task = serializers.SerializerMethodField('get_assigned_task')
points = serializers.SerializerMethodField('get_points')
in_progress_task = serializers.SerializerMethodField('get_in_progress_task')
completed_task = serializers.SerializerMethodField('get_completed_task')
requested_task = serializers.SerializerMethodField('get_requested_task')
rejected_task = serializers.SerializerMethodField('get_rejected_task')
pending_task = serializers.SerializerMethodField('get_pending_task')
position = serializers.SerializerMethodField('get_position')
def get_auth(self, obj):
return obj.auth.username
def get_first_name(self, obj):
return obj.auth.first_name
def get_last_name(self, obj):
return obj.auth.last_name
def get_assigned_task(self, obj):
data = JuniorTask.objects.filter(junior=obj).count()
return data
def get_position(self, obj):
data = JuniorPoints.objects.filter(junior=obj).last()
if data:
return data.position
return 99999
def get_points(self, obj):
data = sum(JuniorTask.objects.filter(junior=obj, task_status=COMPLETED).values_list('points', flat=True))
return data
def get_in_progress_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=IN_PROGRESS).count()
return data
def get_completed_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=COMPLETED).count()
return data
def get_requested_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=REQUESTED).count()
return data
def get_rejected_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=REJECTED).count()
return data
def get_pending_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=PENDING).count()
return data
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task',
'requested_task', 'rejected_task', 'position', 'is_invited']
class JuniorProfileSerializer(serializers.ModelSerializer):
"""junior serializer"""
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
notification_count = serializers.SerializerMethodField('get_notification_count')
total_count = serializers.SerializerMethodField('get_total_count')
complete_field_count = serializers.SerializerMethodField('get_complete_field_count')
def get_auth(self, obj):
"""user email address"""
return obj.auth.username
def get_first_name(self, obj):
"""user first name"""
return obj.auth.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.auth.last_name
def get_notification_count(self, obj):
"""total notification count"""
return 0
def get_total_count(self, obj):
"""total fields count"""
return NUMBER['five']
def get_complete_field_count(self, obj):
"""total filled fields count"""
field_list = [obj.auth.first_name, obj.country_name,
obj.gender, obj.dob, obj.image]
complete_field = [data for data in field_list if data is not None and data != '']
return len(complete_field)
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method',
'is_invited', 'passcode', 'guardian_code_approved']
class AddJuniorSerializer(serializers.ModelSerializer):
"""Add junior serializer"""
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'gender','dob', 'is_invited']
def create(self, validated_data):
""" create junior"""
with transaction.atomic():
email = self.context['email']
guardian = self.context['user']
relationship = self.context['relationship']
full_name = self.context['first_name'] + ' ' + self.context['last_name']
guardian_data = Guardian.objects.filter(user__username=guardian).last()
user_data = User.objects.create(username=email, email=email,
first_name=self.context['first_name'],
last_name=self.context['last_name'])
password = User.objects.make_random_password()
user_data.set_password(password)
user_data.save()
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
dob=validated_data.get('dob'), is_invited=True,
guardian_code=[guardian_data.guardian_code],
junior_code=generate_code(JUN, user_data.id),
referral_code=generate_code(ZOD, user_data.id),
referral_code_used=guardian_data.referral_code,
is_password_set=False, is_verified=True)
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship)
"""Generate otp"""
otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1)
UserEmailOtp.objects.create(email=email, otp=otp_value,
user_type='1', expired_at=expiry_time, is_verified=True)
# add push notification
UserNotification.objects.get_or_create(user=user_data)
"""Notification email"""
junior_notification_email(email, full_name, email, password)
# push notification
send_notification.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {})
return junior_data
class RemoveJuniorSerializer(serializers.ModelSerializer):
"""User Update Serializer"""
class Meta(object):
"""Meta class"""
model = Junior
fields = ('id', 'is_invited')
def update(self, instance, validated_data):
if instance:
instance.is_invited = False
instance.guardian_code = '{}'
instance.save()
return instance
class CompleteTaskSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = JuniorTask
fields = ('id', 'image')
def update(self, instance, validated_data):
instance.image = validated_data.get('image', instance.image)
# instance.requested_on = real_time()
instance.requested_on = timezone.now().astimezone(pytz.utc)
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
send_notification.delay(TASK_SUBMITTED, None, instance.junior.auth.id, {})
send_notification.delay(TASK_ACTION, None, instance.guardian.user.id, {})
return instance
class JuniorPointsSerializer(serializers.ModelSerializer):
"""Junior points serializer"""
junior_id = serializers.SerializerMethodField('get_junior_id')
total_points = serializers.SerializerMethodField('get_points')
in_progress_task = serializers.SerializerMethodField('get_in_progress_task')
completed_task = serializers.SerializerMethodField('get_completed_task')
requested_task = serializers.SerializerMethodField('get_requested_task')
rejected_task = serializers.SerializerMethodField('get_rejected_task')
pending_task = serializers.SerializerMethodField('get_pending_task')
expired_task = serializers.SerializerMethodField('get_expired_task')
position = serializers.SerializerMethodField('get_position')
def get_junior_id(self, obj):
"""junior id"""
return obj.junior.id
def get_position(self, obj):
data = JuniorPoints.objects.filter(junior=obj.junior).last()
if data:
return data.position
return 99999
def get_points(self, obj):
"""total points"""
points = JuniorPoints.objects.filter(junior=obj.junior).last()
if points:
return points.total_points
def get_in_progress_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=IN_PROGRESS).count()
def get_completed_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=COMPLETED).count()
def get_requested_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=REQUESTED).count()
def get_rejected_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=REJECTED).count()
def get_pending_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=PENDING).count()
def get_expired_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=EXPIRED).count()
class Meta(object):
"""Meta info"""
model = Junior
fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task',
'requested_task', 'rejected_task', 'expired_task']
class AddGuardianSerializer(serializers.ModelSerializer):
"""Add guardian serializer"""
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id']
def create(self, validated_data):
""" invite and create guardian"""
with transaction.atomic():
email = self.context['email']
junior = self.context['user']
relationship = self.context['relationship']
full_name = self.context['first_name'] + ' ' + self.context['last_name']
junior_data = Junior.objects.filter(auth__username=junior).last()
instance = User.objects.filter(username=email).last()
if instance:
guardian_data = Guardian.objects.filter(user=instance).update(is_invited=True,
referral_code=generate_code(ZOD,
instance.id),
referral_code_used=junior_data.referral_code,
is_verified=True)
UserNotification.objects.get_or_create(user=instance)
return guardian_data
else:
user = User.objects.create(username=email, email=email,
first_name=self.context['first_name'],
last_name=self.context['last_name'])
password = User.objects.make_random_password()
user.set_password(password)
user.save()
guardian_data = Guardian.objects.create(user=user, is_invited=True,
referral_code=generate_code(ZOD, user.id),
referral_code_used=junior_data.referral_code,
is_password_set=False, is_verified=True)
"""Generate otp"""
otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1)
UserEmailOtp.objects.create(email=email, otp=otp_value,
user_type=str(NUMBER['two']), expired_at=expiry_time,
is_verified=True)
UserNotification.objects.get_or_create(user=user)
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship)
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail(email, full_name)
send_notification.delay(INVITED_GUARDIAN, None, junior_data.auth.id, {})
send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {})
return guardian_data
class StartTaskSerializer(serializers.ModelSerializer):
"""User task Serializer"""
task_duration = serializers.SerializerMethodField('get_task_duration')
def get_task_duration(self, obj):
""" remaining time to complete task"""
due_date = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time
# real_datetime = real_time()
# new code
due_date = due_date.astimezone(pytz.utc)
real_datetime = timezone.now().astimezone(pytz.utc)
# Perform the subtraction
if due_date > real_datetime:
time_difference = due_date - real_datetime
time_only = convert_timedelta_into_datetime(time_difference)
return str(time_difference.days) + ' days ' + str(time_only)
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
class Meta(object):
"""Meta class"""
model = JuniorTask
fields = ('id', 'task_duration')
def update(self, instance, validated_data):
instance.task_status = str(NUMBER['two'])
instance.save()
return instance
class ReAssignTaskSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = JuniorTask
fields = ('id', 'due_date')
def update(self, instance, validated_data):
instance.task_status = str(NUMBER['one'])
instance.due_date = validated_data.get('due_date')
instance.is_approved = False
instance.requested_on = None
instance.save()
return instance
-5
View File
@@ -1,5 +0,0 @@
"""Junior test file"""
"""Import TestCase"""
from django.test import TestCase
# Create your tests here.

Some files were not shown because too many files have changed in this diff Show More