Mbonea commited on
Commit
252d749
1 Parent(s): e003996
.github/workflows/build-push.yml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Build and Push Docker Image
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ env:
8
+ IMAGE_NAME: mbonea/trannybrickleberry
9
+
10
+ jobs:
11
+ build-and-push:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v2
15
+ - name: Login to Docker Hub
16
+ uses: docker/login-action@v1
17
+ with:
18
+ username: ${{ secrets.DOCKER_USERNAME }}
19
+ password: ${{ secrets.DOCKER_PASSWORD }}
20
+ - name: Build and Push
21
+ uses: docker/build-push-action@v2
22
+ with:
23
+ context: .
24
+ push: true
25
+ tags: ${{ env.IMAGE_NAME }}:latest
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ *.pyc
.vscode/settings.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "cSpell.words": ["websockets"]
3
+ }
App/Analytics/AnalyticsRoutes.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, status
2
+ from .Schemas import BaseRequest, editRequest
3
+ from .Model import Comments
4
+ from App.Users.Model import User
5
+ from App.Post.Model import Post
6
+
7
+ analytics_router = APIRouter(tags=["Analytics"])
App/Analytics/Model.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import orm
3
+ import psycopg2
4
+ import datetime
5
+ import pydantic
6
+ from App.modelInit import database, models
7
+ from App.Users.Model import User
8
+
9
+
10
+ class Analytics(orm.Model):
11
+ tablename = "Analytics"
12
+ registry = models
13
+ fields = {
14
+ "id": orm.Integer(primary_key=True),
15
+ "user": orm.ForeignKey(
16
+ User, on_delete=orm.CASCADE, allow_null=True
17
+ ), # Optional for unknown users.
18
+ "post": orm.ForeignKey(User, on_delete=orm.CASCADE),
19
+ "ip": orm.String(max_length=100),
20
+ "device": orm.String(max_length=100),
21
+ "country": orm.String(max_length=100),
22
+ "createdAt": orm.DateTime(index=True, default=datetime.datetime.now),
23
+ }
App/Analytics/Schemas.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Optional
2
+ from pydantic import EmailStr, BaseModel
3
+ from datetime import date, datetime, time, timedelta
4
+
5
+
6
+ class BaseRequest(BaseModel):
7
+ user: Optional[int]
8
+ id: Optional[int]
9
+ content: Optional[str]
10
+
11
+
12
+ class editRequest(BaseRequest):
13
+ updatedAt: datetime = datetime.now()
App/Authentication.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from fastapi import APIRouter, status
2
+ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
3
+
4
+ oauth_scheme = OAuth2PasswordBearer(tokenUrl="/user/login")
App/Settings.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ class Settings:
2
+ ALGORITHM = "HS256"
3
+ HASH = "86c5ceb27e1bf441130299c0209e5f35b88089f62c06b2b09d65772274f12057"
App/Transcription/Model.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import orm
3
+ import psycopg2
4
+ import datetime
5
+ import ujson
6
+ import pydantic
7
+ from passlib.context import CryptContext
8
+ from App.modelInit import database, models
9
+
10
+
11
+ class Transcriptions(orm.Model):
12
+ tablename = "transcriptions"
13
+ registry = models
14
+ fields = {
15
+ "id": orm.Integer(primary_key=True),
16
+ "tl_file_id": orm.String(max_length=100, index=True, default=""),
17
+ "user": orm.ForeignKey(User, on_delete=orm.CASCADE),
18
+ "createdAt": orm.DateTime(index=True, default=datetime.datetime.now),
19
+ "content": orm.JSON(default={}),
20
+ "transcription_state": orm.String(max_length=100, index=True, default="qued"),
21
+ }
App/Transcription/Schemas.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Optional
2
+ from pydantic import EmailStr, BaseModel
3
+ from passlib.context import CryptContext
4
+ from fastapi import UploadFile
5
+
6
+
7
+ class UserDetails(BaseModel):
8
+ userId: str
App/Transcription/TranscriptionRoutes.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, status, Form, UploadFile, File
2
+ from typing_extensions import Annotated
3
+ from .Schemas import UserDetails
4
+ import aiofiles
5
+ from celery.result import AsyncResult
6
+ from App.Worker import transcription_task
7
+
8
+ # from .Model import User
9
+ # from sqlalchemy import and_
10
+
11
+
12
+ transcription_router = APIRouter(tags=["User"])
13
+
14
+
15
+ @transcription_router.post("/uploadfile/")
16
+ async def create_file(file: UploadFile, userId: int = 1):
17
+ # Read the file contents
18
+ contents = await file.read()
19
+
20
+ # Write the file to disk asynchronously
21
+ async with aiofiles.open(file.filename, "wb") as f:
22
+ await f.write(contents)
23
+
24
+ # celery task
25
+ task = transcription_task.delay(file.filename)
26
+
27
+ return {"file_size": file.size, "file_name": file.filename, "task_id": task.id}
28
+
29
+
30
+ @transcription_router.get("/tasks/{task_id}")
31
+ async def get_status(task_id):
32
+ task_result = AsyncResult(task_id)
33
+ result = {
34
+ "task_id": task_id,
35
+ "task_status": task_result.status,
36
+ "task_result": task_result.result,
37
+ }
38
+ return result
App/Transcription/Utils/audio_transcription.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from faster_whisper import WhisperModel
2
+ from tqdm import tqdm
3
+ import os
4
+
5
+ model_size = "tiny"
6
+
7
+
8
+ def transcribe_file(state, file_path, model_size="tiny"):
9
+ result = {}
10
+ model = WhisperModel(model_size, device="cpu", compute_type="int8")
11
+ segments, info = model.transcribe(file_path, beam_size=5)
12
+
13
+ total_duration = round(info.duration, 2)
14
+ state.update_state(
15
+ state="PROGRESS",
16
+ meta={
17
+ "logs": "Detected language '%s' with probability %f"
18
+ % (info.language, info.language_probability),
19
+ },
20
+ )
21
+
22
+
23
+ with tqdm(total=total_duration, unit=" seconds") as pbar:
24
+ for segment in segments:
25
+ segment_duration = segment.end - segment.start
26
+ time_stamp = "[%.2fs -> %.2fs]" % (segment.start, segment.end)
27
+ result[time_stamp] = segment.text
28
+ state.update_state(
29
+ state="PROGRESS",
30
+ meta={
31
+ "done": segment.end,
32
+ "total": total_duration,
33
+ "percentage": f"{((segment.end / total_duration)*100)}",
34
+ },
35
+ )
36
+ pbar.update(segment_duration)
37
+
38
+ #delete file
39
+ os.remove(file_path)
40
+
41
+
42
+ return result
App/Users/Model.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import orm
3
+ import psycopg2
4
+ import datetime
5
+ import pydantic
6
+ from passlib.context import CryptContext
7
+ from App.modelInit import database, models
8
+
9
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
10
+
11
+
12
+ class User(orm.Model):
13
+ tablename = "users"
14
+ registry = models
15
+ fields = {
16
+ "id": orm.Integer(primary_key=True),
17
+ "name": orm.String(max_length=100, index=True),
18
+ "email": orm.String(max_length=100, index=True, unique=True),
19
+ "password": orm.String(max_length=100, index=True),
20
+ "phoneNumber": orm.String(max_length=100, index=True, allow_null=True),
21
+ "account_type": orm.Integer(index=True, default=1),
22
+ "createdAt": orm.DateTime(index=True, default=datetime.datetime.now),
23
+ "updatedAt": orm.DateTime(index=True, default=datetime.datetime.now),
24
+ "lastLogin": orm.DateTime(index=True, default=datetime.datetime.now),
25
+ }
26
+
27
+ def verify_password(self, plain_password):
28
+ return pwd_context.verify(plain_password, self.password)
App/Users/Schemas.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Optional
2
+ from pydantic import EmailStr, BaseModel
3
+ from passlib.context import CryptContext
4
+
5
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
6
+
7
+
8
+ class BaseRequest(BaseModel):
9
+ email: EmailStr
10
+ name: str
11
+ password: str
12
+ phoneNumber: Optional[str]
13
+
14
+ def hash_password(self):
15
+ self.password = pwd_context.hash(self.password)
App/Users/UserRoutes.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, status
2
+ from .Schemas import BaseRequest
3
+ from .Model import User
4
+ from sqlalchemy import and_
5
+ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
6
+
7
+
8
+ user_router = APIRouter(tags=["User"])
9
+
10
+
11
+ @user_router.post("/user/register")
12
+ async def register_user(user: BaseRequest):
13
+ data = await User.objects.filter(email=user.email).first()
14
+ if data != None:
15
+ return {"code": 400, "message": "user exists", "payload": None}
16
+ else:
17
+ user.hash_password()
18
+ sample = await User.objects.create(**user.dict())
19
+ return {"code": 200, "message": "success", "payload": None}
20
+
21
+
22
+ @user_router.post("/user/login")
23
+ async def register_user(user: BaseRequest):
24
+ db_user = await User.objects.filter(email=user.email).first()
25
+ if db_user:
26
+ if db_user.verify_password(user.password):
27
+ return {"code": 200, "message": "success", "payload": db_user.__dict__}
28
+ return {"code": 401, "message": "Invalid Credentials", "payload": None}
App/Worker.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from celery import Celery
2
+ import os
3
+ import time
4
+ from App import celery_config
5
+ from App.Transcription.Utils.audio_transcription import transcribe_file
6
+
7
+ celery = Celery()
8
+ celery.config_from_object(celery_config)
9
+
10
+
11
+ @celery.task(name="create_task", bind=True)
12
+ def transcription_task(self, file_path):
13
+ return transcribe_file(state=self, file_path=file_path, model_size="tiny")
App/__init__.py ADDED
File without changes
App/app.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from .Users.UserRoutes import user_router
3
+ from .modelInit import models, database
4
+ from .Transcription.TranscriptionRoutes import transcription_router
5
+
6
+ app = FastAPI()
7
+
8
+
9
+ @app.on_event("startup")
10
+ async def startup_event():
11
+ await models.create_all()
12
+ if not database.is_connected:
13
+ await database.connect()
14
+ print("connected!")
15
+
16
+
17
+ @app.on_event("shutdown")
18
+ async def shutdown_event():
19
+ if not database.is_connected:
20
+ await database.disconnect()
21
+ print("shutting down!")
22
+
23
+
24
+ @app.get("/")
25
+ async def landing_page():
26
+ return {"code": 200, "message": "still running"}
27
+
28
+
29
+ app.include_router(user_router)
30
+ app.include_router(transcription_router)
App/celery_config.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from os import environ
2
+ import ssl
3
+
4
+ task_serializer = "json"
5
+ result_serializer = "json"
6
+ accept_content = ["json"]
7
+ timezone = "Europe/Oslo"
8
+ enable_utc = True
9
+
10
+ broker_url = f"rediss://default:59c9ffda43f61cc18b44a3407a2a7793@master.transcription--legal-stuff--a96n-7tyr.addon.code.run:6379?ssl_cert_reqs=none"
11
+
12
+
13
+ result_backend = f"rediss://default:59c9ffda43f61cc18b44a3407a2a7793@master.transcription--legal-stuff--a96n-7tyr.addon.code.run:6379?ssl_cert_reqs=none"
14
+
15
+ # SSL/TLS and SNI configuration
16
+ broker_use_ssl = {
17
+ "ssl_cert_reqs": ssl.CERT_NONE,
18
+ "ssl_match_hostname": False,
19
+ "ssl_check_hostname": False,
20
+ "ssl_sni": "master.transcription--legal-stuff--a96n-7tyr.addon.code.run",
21
+ }
App/modelInit.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import databases
2
+ import orm
3
+ import psycopg2
4
+
5
+
6
+
7
+
8
+ #deployment
9
+ database = databases.Database(
10
+ "postgresql+asyncpg://postgres:PkkneZrSFsnJR6B@db.vfhoydxvxuesxhrcdnmx.supabase.co:5432/postgres"
11
+ )
12
+
13
+
14
+ #development
15
+
16
+ # database = databases.Database(
17
+ # "postgresql+asyncpg://postgres:zJAkapOh3IVt8R0h@db.mygpwobpwxttmilnxrtu.supabase.co:5432/postgres"
18
+ # )
19
+
20
+
21
+
22
+ models = orm.ModelRegistry(database=database)
App/utils.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from App.Users.Model import User
2
+ from App.Post.Model import Post
3
+ import asyncio
4
+ from fastapi import HTTPException
5
+
6
+
7
+ async def get_user_and_post(content):
8
+ try:
9
+ # user = None
10
+ # post = await Post.objects.get(id=content.postId)
11
+ # print(post.id)
12
+ user, post = await asyncio.gather(
13
+ *[User.objects.get(id=content.userId), Post.objects.get(id=content.postId)]
14
+ )
15
+ except:
16
+ raise HTTPException(status_code=400, detail="Invalid data")
17
+ return user, post
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Builder stage
2
+ FROM python:3.9.13-slim as builder
3
+
4
+ WORKDIR /srv
5
+
6
+ RUN apt-get update && apt-get install -y git ffmpeg aria2 \
7
+ && rm -rf /var/lib/apt/lists/*
8
+
9
+ COPY requirements.txt .
10
+ RUN pip install --no-cache-dir -r requirements.txt
11
+
12
+
13
+
14
+
15
+ COPY . /srv
16
+
17
+
18
+
19
+ CMD uvicorn App.app:app --host 0.0.0.0 --port 7860 & celery -A App.Worker.celery worker -c 1 --loglevel=info
20
+ EXPOSE 7860
README.md CHANGED
@@ -1,11 +1 @@
1
- ---
2
- title: Tranny
3
- emoji: 🌖
4
- colorFrom: red
5
- colorTo: red
6
- sdk: docker
7
- pinned: false
8
- license: creativeml-openrail-m
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ # Trasnscription
 
 
 
 
 
 
 
 
 
 
data.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
okteto-stack.yaml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: 1
2
+ services:
3
+ app:
4
+ build: .
5
+ command: uvicorn App.app:app --host 0.0.0.0 --port 8000
6
+ environment:
7
+ - DATABASE_URL=postgresql://user:password@db:5432/mydatabase
8
+ ports:
9
+ - 8000:8000
10
+
11
+ worker:
12
+ build: .
13
+ command: celery -A App.Worker.celery worker -c 1 --loglevel=info
14
+ resources:
15
+ memory: 3Gi
16
+ cpu: 1000m
requirements.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ asyncpg==0.27.0
2
+ click==8.1.3
3
+ databases==0.7.0
4
+ fastapi==0.92.0
5
+ Flask==2.2.2
6
+ greenlet==2.0.2
7
+ itsdangerous==2.1.2
8
+ orm==0.3.1
9
+ faster-whisper
10
+ aiofiles
11
+ psycopg2-binary==2.9.5
12
+ SQLAlchemy==1.4.46
13
+ starlette==0.25.0
14
+ typesystem==0.3.1
15
+ Werkzeug==2.2.2
16
+ passlib # for password hashing
17
+ pydantic[email]
18
+ uvicorn==0.21.1
19
+ gunicorn
20
+ ujson
21
+ orm[mysql]
22
+ celery
23
+ python-multipart
24
+ redis==4.5.5
25
+ python-multipart
26
+ asyncmy
stress_test.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import aiohttp
3
+
4
+ analysis = {"failed": 0, "passed": 0}
5
+
6
+
7
+ async def send_post_request(session, number=0):
8
+ async with session.post(
9
+ url="https://app-mbonea-mjema.cloud.okteto.net/post/create",
10
+ json={"content": {}, "recommendations": {}},
11
+ ) as response:
12
+ response_text = await response.text()
13
+ if response.status == 200:
14
+ analysis["passed"] += 1
15
+ print(f"Response from {number}: {response_text}")
16
+ else:
17
+ analysis["failed"] += 1
18
+
19
+
20
+ async def send_post_requests_async():
21
+ async with aiohttp.ClientSession() as session:
22
+ tasks = []
23
+ for number in range(1_000):
24
+ tasks.append(asyncio.ensure_future(send_post_request(session, number)))
25
+ await asyncio.gather(*tasks)
26
+
27
+
28
+ if __name__ == "__main__":
29
+ asyncio.run(send_post_requests_async())
30
+ print(analysis)