Navid Arabi commited on
Commit
1ef4e10
·
1 Parent(s): f0b4f1b
.gitignore CHANGED
@@ -1,2 +1,8 @@
1
  venv/
2
- .env
 
 
 
 
 
 
 
1
  venv/
2
+ .env
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+ .session
7
+ .sessions.json
8
+ sessions.pkl
__pycache__/config.cpython-311.pyc DELETED
Binary file (842 Bytes)
 
__pycache__/seed_db.cpython-311.pyc DELETED
Binary file (2.38 kB)
 
app.py CHANGED
@@ -1,11 +1,32 @@
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- from seed_db import seed
4
 
 
5
 
6
- with gr.Blocks() as demo:
7
- btn = gr.Button("Run!")
8
- output = gr.Textbox(label="Output")
9
- btn.click(fn=seed, outputs=output)
10
 
11
- demo.launch()
 
 
1
+ # app.py
2
+
3
  import gradio as gr
4
+ from pathlib import Path
5
+
6
+ from components.login_page import LoginPage
7
+ from components.dashboard_page import DashboardPage
8
+ from config import conf
9
+
10
+ CSS_FILE = Path(__file__).parent / "assets" / "styles.css"
11
+ custom_css = CSS_FILE.read_text(encoding="utf-8")
12
+
13
+
14
+ def build_app() -> gr.Blocks:
15
+ with gr.Blocks(title=conf.APP_TITLE, css=custom_css) as demo:
16
+ session = gr.State({})
17
+
18
+ gr.Markdown(f"### {conf.APP_TITLE}")
19
+
20
+ login_page = LoginPage()
21
+ dashboard_page = DashboardPage()
22
+
23
+ login_page.register_callbacks(dashboard_page, session)
24
+ dashboard_page.register_callbacks(login_page, session)
25
 
26
+ demo.queue(default_concurrency_limit=50)
27
 
28
+ return demo
29
 
 
 
 
 
30
 
31
+ if __name__ == "__main__":
32
+ build_app().launch()
assets/styles.css ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .header-row {
2
+ align-items: center;
3
+ padding: 12px 12px;
4
+ }
components/__init__.py ADDED
File without changes
components/dashboard_page.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # components/dashboard_page.py
2
+ import gradio as gr
3
+ from utils.auth import AuthService
4
+ from components.header import Header
5
+
6
+
7
+ class DashboardPage:
8
+ """UI elements + event wiring for the post-login dashboard."""
9
+
10
+ def __init__(self):
11
+ with gr.Column(visible=False) as self.container:
12
+ # ───── header (welcome + logout) ─────
13
+ self.header = Header()
14
+
15
+ # ─────────── main body ────────────
16
+ self.echo_box = gr.Textbox(label="Echo – whatever you type will be returned")
17
+ self.echo_btn = gr.Button("Echo")
18
+ self.echo_output = gr.Markdown()
19
+
20
+ # ─────────────────── event wiring ────────────────────
21
+ def register_callbacks(self, login_page, session_state):
22
+ # Echo button: prepend current username
23
+ self.echo_btn.click(
24
+ fn=AuthService.echo,
25
+ inputs=[self.echo_box, session_state],
26
+ outputs=self.echo_output,
27
+ )
28
+
29
+ # Header handles its own logout button
30
+ self.header.register_callbacks(login_page, self, session_state)
components/header.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # components/header.py
2
+ import gradio as gr
3
+ from utils.auth import AuthService
4
+
5
+
6
+ class Header:
7
+ def __init__(self):
8
+ with gr.Row(variant="panel", elem_classes="header-row") as self.container:
9
+ self.welcome = gr.Markdown()
10
+
11
+ self.logout_btn = gr.Button("Log out", scale=0, min_width=90)
12
+
13
+ # ---------------- wiring ----------------
14
+ def register_callbacks(self, login_page, dashboard_page, session_state):
15
+ self.logout_btn.click(
16
+ fn=AuthService.logout,
17
+ inputs=session_state,
18
+ outputs=[
19
+ login_page.container,
20
+ dashboard_page.container,
21
+ login_page.message,
22
+ ],
23
+ )
components/login_page.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # components/login_page.py
2
+ import gradio as gr
3
+ from utils.auth import AuthService
4
+
5
+
6
+ class LoginPage:
7
+ """UI elements + event wiring for the login screen (centered & narrow)."""
8
+
9
+ def __init__(self):
10
+ with gr.Row(visible=True) as self.container:
11
+ gr.Column(scale=1) # left spacer
12
+
13
+ with gr.Column(scale=0) as self.form_col:
14
+ # components with min_width control overall width
15
+ self.username = gr.Text(label="Username", min_width=300)
16
+ self.password = gr.Text(label="Password", type="password", min_width=300)
17
+ self.login_btn = gr.Button("Log in", min_width=300)
18
+
19
+ # ↓ removed min_width (Markdown doesn't accept it)
20
+ self.message = gr.Markdown()
21
+
22
+ gr.Column(scale=1) # right spacer
23
+
24
+ # event wiring unchanged …
25
+ def register_callbacks(self, dashboard_page, session_state):
26
+ header = dashboard_page.header
27
+
28
+ self.login_btn.click(
29
+ fn=AuthService.login,
30
+ inputs=[self.username, self.password, session_state],
31
+ outputs=[self.message, self.container, dashboard_page.container],
32
+ concurrency_limit=10,
33
+ ).then(
34
+ lambda s: f"### User: {s.get('user', '')}",
35
+ inputs=session_state,
36
+ outputs=header.welcome,
37
+ )
config.py CHANGED
@@ -1,13 +1,27 @@
 
 
1
  import os
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
 
 
 
3
 
4
- db_config = {
5
- 'user': os.environ.get('DB_USER'),
6
- 'password': os.environ.get('DB_PASSWORD'),
7
- 'host': os.environ.get('DB_HOST'),
8
- 'port': os.environ.get('DB_PORT'),
9
- 'database': os.environ.get('DB_NAME')
10
- }
11
 
12
- hf_token = os.environ.get('HF_TOKEN')
13
- hf_tts_ds_repo = os.environ.get('HF_TTS_DS_REPO')
 
1
+ # config.py
2
+
3
  import os
4
+ from pydantic_settings import BaseSettings
5
+
6
+
7
+ class Config(BaseSettings):
8
+ DB_USER: str = os.getenv("DB_USER")
9
+ DB_PASSWORD: str = os.getenv("DB_PASSWORD")
10
+ DB_HOST: str = os.getenv("DB_HOST", "localhost")
11
+ DB_PORT: str = os.getenv("DB_PORT", "3306")
12
+ DB_NAME: str = os.getenv("DB_NAME", "defaultdb")
13
+ HF_TOKEN: str = os.environ.get("HF_TOKEN")
14
+ HF_TTS_DS_REPO: str = os.environ.get("HF_TTS_DS_REPO")
15
+
16
+ APP_TITLE: str = "Gooya TTS Annotation Tools"
17
+
18
+ class Config:
19
+ env_file = ".env"
20
+ case_sensitive = True
21
 
22
+ @property
23
+ def db_url(self) -> str:
24
+ return f"mysql+pymysql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
25
 
 
 
 
 
 
 
 
26
 
27
+ conf = Config()
 
models.py → data/models.py RENAMED
@@ -1,89 +1,76 @@
1
  from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey
2
  from sqlalchemy.orm import relationship, declarative_base
3
- from pydantic import BaseModel, ConfigDict, StringConstraints
4
- from typing import Annotated
5
- from typing import List
6
- from datetime import datetime
7
 
8
  Base = declarative_base()
9
 
10
 
 
 
 
 
 
 
 
11
 
12
- class TTSDataOut(BaseModel):
13
- id: int
14
- filename: Annotated[str, StringConstraints(max_length=255)]
15
- sentence: str
16
-
17
  class Annotator(Base):
18
- __tablename__ = 'annotators'
19
 
20
  id = Column(Integer, primary_key=True)
21
  name = Column(String, nullable=False, unique=True)
22
  password = Column(String, nullable=False)
23
  last_login = Column(DateTime)
24
  is_active = Column(Boolean, default=True)
25
- annotations = relationship('Annotation', backref='annotator')
26
- validators = relationship('Validator', backref='annotator')
27
- annotation_intervals = relationship('AnnotationInterval', backref='annotator')
28
 
29
 
30
  class Validator(Base):
31
- __tablename__ = 'validators'
32
 
33
  id = Column(Integer, primary_key=True)
34
- annotator_id = Column(Integer, ForeignKey('annotators.id'))
35
- validator_id = Column(Integer, ForeignKey('annotators.id'))
36
 
37
 
38
  class AnnotationInterval(Base):
39
- __tablename__ = 'annotation_intervals'
40
 
41
  id = Column(Integer, primary_key=True)
42
- annotator_id = Column(Integer, ForeignKey('annotators.id'))
43
  start_index = Column(Integer, nullable=True)
44
  end_index = Column(Integer, nullable=True)
45
 
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  class AudioTrim(Base):
48
- __tablename__ = 'audio_trims'
49
 
50
  id = Column(Integer, primary_key=True)
51
  start = Column(Float, nullable=False)
52
  end = Column(Float, nullable=False)
53
- annotation_id = Column(Integer, ForeignKey('annotations.id'))
54
 
55
- class Annotation(Base):
56
- __tablename__ = 'annotations'
 
57
 
58
  id = Column(Integer, primary_key=True)
59
- annotated_subtitle = Column(String, nullable=False)
 
60
  validated = Column(Boolean, nullable=False)
61
- create_at = Column(DateTime, nullable=False)
62
- update_at = Column(DateTime, nullable=False)
63
- audio_trims = relationship('AudioTrim', cascade='all, delete', backref='annotation')
64
- json_data_id = Column(Integer, ForeignKey('json_data.id'))
65
- annotator_id = Column(Integer, ForeignKey('annotators.id'))
66
-
67
-
68
- class AudioTrimModel(BaseModel):
69
- start: float
70
- end: float
71
- model_config = ConfigDict(from_attributes=True)
72
-
73
- class AnnotationModel(BaseModel):
74
- annotator: str
75
- annotated_subtitle: str
76
- is_first_phase_accepted: bool
77
- create_at: datetime
78
- update_at: datetime
79
- audio_trims: List[AudioTrimModel]
80
- model_config = ConfigDict(from_attributes=True)
81
-
82
- class JsonDataModel(BaseModel):
83
- id: int
84
- voice_name: str
85
- original_subtitle: str
86
- ignore_it: bool
87
- is_approved_in_second_phase: bool
88
- annotations: List[AnnotationModel]
89
- model_config = ConfigDict(from_attributes=True)
 
1
  from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey
2
  from sqlalchemy.orm import relationship, declarative_base
 
 
 
 
3
 
4
  Base = declarative_base()
5
 
6
 
7
+ class TTSData(Base):
8
+ __tablename__ = "tts_data"
9
+
10
+ id = Column(Integer, primary_key=True)
11
+ filename = Column(String, nullable=False, unique=True)
12
+ sentence = Column(String, nullable=False)
13
+
14
 
 
 
 
 
 
15
  class Annotator(Base):
16
+ __tablename__ = "annotators"
17
 
18
  id = Column(Integer, primary_key=True)
19
  name = Column(String, nullable=False, unique=True)
20
  password = Column(String, nullable=False)
21
  last_login = Column(DateTime)
22
  is_active = Column(Boolean, default=True)
23
+ annotations = relationship("Annotation", backref="annotator")
24
+ validators = relationship("Validator", backref="annotator")
25
+ annotation_intervals = relationship("AnnotationInterval", backref="annotator")
26
 
27
 
28
  class Validator(Base):
29
+ __tablename__ = "validators"
30
 
31
  id = Column(Integer, primary_key=True)
32
+ annotator_id = Column(Integer, ForeignKey("annotators.id"))
33
+ validator_id = Column(Integer, ForeignKey("annotators.id"))
34
 
35
 
36
  class AnnotationInterval(Base):
37
+ __tablename__ = "annotation_intervals"
38
 
39
  id = Column(Integer, primary_key=True)
40
+ annotator_id = Column(Integer, ForeignKey("annotators.id"))
41
  start_index = Column(Integer, nullable=True)
42
  end_index = Column(Integer, nullable=True)
43
 
44
 
45
+ class Annotation(Base):
46
+ __tablename__ = "annotations"
47
+
48
+ id = Column(Integer, primary_key=True)
49
+ annotated_sentence = Column(String, nullable=False)
50
+ validated = Column(Boolean, nullable=False)
51
+ annotated_at = Column(DateTime, nullable=False)
52
+ annotator_id = Column(Integer, ForeignKey("annotators.id"))
53
+ audio_trims = relationship("AudioTrim", cascade="all, delete", backref="annotation")
54
+ validations = relationship(
55
+ "Validation", cascade="all, delete", backref="annotation"
56
+ )
57
+
58
+
59
  class AudioTrim(Base):
60
+ __tablename__ = "audio_trims"
61
 
62
  id = Column(Integer, primary_key=True)
63
  start = Column(Float, nullable=False)
64
  end = Column(Float, nullable=False)
65
+ annotation_id = Column(Integer, ForeignKey("annotations.id"))
66
 
67
+
68
+ class Validation(Base):
69
+ __tablename__ = "validations"
70
 
71
  id = Column(Integer, primary_key=True)
72
+ annotation_id = Column(Integer, ForeignKey("annotations.id"))
73
+ validator_id = Column(Integer, ForeignKey("validators.id"))
74
  validated = Column(Boolean, nullable=False)
75
+ description = Column(String, nullable=True)
76
+ validated_at = Column(DateTime, nullable=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
database.py DELETED
@@ -1,152 +0,0 @@
1
- from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DateTime, ForeignKey
2
- from sqlalchemy.orm import sessionmaker, relationship, declarative_base
3
- from pydantic import BaseModel, ConfigDict
4
- from typing import List
5
- from datetime import datetime
6
-
7
- # ایجاد مدل‌های SQLAlchemy
8
- Base = declarative_base()
9
-
10
- class Annotator(Base):
11
- __tablename__ = 'annotators'
12
-
13
- id = Column(Integer, primary_key=True)
14
- name = Column(String, nullable=False, unique=True)
15
- password = Column(String, nullable=False)
16
- last_login = Column(DateTime)
17
- is_active = Column(Boolean, default=True)
18
- annotations = relationship('Annotation', backref='annotator')
19
-
20
- class AudioTrim(Base):
21
- __tablename__ = 'audio_trims'
22
-
23
- id = Column(Integer, primary_key=True)
24
- start = Column(Float, nullable=False)
25
- end = Column(Float, nullable=False)
26
- annotation_id = Column(Integer, ForeignKey('annotations.id'))
27
-
28
- class Annotation(Base):
29
- __tablename__ = 'annotations'
30
-
31
- id = Column(Integer, primary_key=True)
32
- annotated_subtitle = Column(String, nullable=False)
33
- is_first_phase_accepted = Column(Boolean, nullable=False)
34
- create_at = Column(DateTime, nullable=False)
35
- update_at = Column(DateTime, nullable=False)
36
- audio_trims = relationship('AudioTrim', cascade='all, delete', backref='annotation')
37
- json_data_id = Column(Integer, ForeignKey('json_data.id'))
38
- annotator_id = Column(Integer, ForeignKey('annotators.id'))
39
-
40
- class JsonData(Base):
41
- __tablename__ = 'json_data'
42
-
43
- id = Column(Integer, primary_key=True)
44
- voice_name = Column(String, nullable=False)
45
- original_subtitle = Column(String, nullable=False)
46
- ignore_it = Column(Boolean, nullable=False)
47
- is_approved_in_second_phase = Column(Boolean, nullable=False)
48
- annotations = relationship('Annotation', cascade='all, delete', backref='json_data')
49
-
50
- # ایجاد مدل‌های Pydantic
51
- class AudioTrimModel(BaseModel):
52
- start: float
53
- end: float
54
- model_config = ConfigDict(from_attributes=True)
55
-
56
- class AnnotationModel(BaseModel):
57
- annotator: str
58
- annotated_subtitle: str
59
- is_first_phase_accepted: bool
60
- create_at: datetime
61
- update_at: datetime
62
- audio_trims: List[AudioTrimModel]
63
- model_config = ConfigDict(from_attributes=True)
64
-
65
- class JsonDataModel(BaseModel):
66
- id: int
67
- voice_name: str
68
- original_subtitle: str
69
- ignore_it: bool
70
- is_approved_in_second_phase: bool
71
- annotations: List[AnnotationModel]
72
- model_config = ConfigDict(from_attributes=True)
73
-
74
- # ایجاد جلسه
75
- DB_URL = "mysql+pymysql://user:password@localhost/testdb" # اطلاعات کاربری و دیتابیس را ویرایش کنید
76
- engine = create_engine(DB_URL)
77
- SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
78
-
79
- def initialize_database():
80
- Base.metadata.create_all(bind=engine)
81
-
82
- # تابع ذخیره داده‌ها در دیتابیس
83
- def save_json_data(json_data_dict):
84
- db = SessionLocal()
85
- try:
86
- # استفاده از Pydantic برای اعتبارسنجی داده‌ها
87
- json_data = JsonDataModel(**json_data_dict)
88
-
89
- for annotation in json_data.annotations:
90
- # تامین موجودیت annotator
91
- db_annotator = db.query(Annotator).filter_by(name=annotation.annotator).first()
92
- if not db_annotator:
93
- db_annotator = Annotator(name=annotation.annotator, password="default_password")
94
- db.add(db_annotator)
95
- db.commit() # اضافه کنید تا id اختصاص داده شود
96
-
97
- # تبدیل مدل Pydantic به مدل SQLAlchemy
98
- db_annotation = Annotation(
99
- annotated_subtitle=annotation.annotated_subtitle,
100
- is_first_phase_accepted=annotation.is_first_phase_accepted,
101
- create_at=annotation.create_at,
102
- update_at=annotation.update_at,
103
- audio_trims=[
104
- AudioTrim(start=trim.start, end=trim.end) for trim in annotation.audio_trims
105
- ],
106
- annotator=db_annotator
107
- )
108
-
109
- db_json_data = JsonData(
110
- id=json_data.id,
111
- voice_name=json_data.voice_name,
112
- original_subtitle=json_data.original_subtitle,
113
- ignore_it=json_data.ignore_it,
114
- is_approved_in_second_phase=json_data.is_approved_in_second_phase,
115
- annotations=[db_annotation]
116
- )
117
-
118
- db.add(db_json_data)
119
- db.commit()
120
-
121
- except Exception as e:
122
- db.rollback()
123
- print(e)
124
- finally:
125
- db.close()
126
-
127
- # فراخوانی تابع ایجاد جدول‌ها و افزودن داده
128
- initialize_database()
129
-
130
- # داده‌ی نمونه برای افزودن
131
- json_data = {
132
- "id": 16562,
133
- "voice_name": "voice1.wav",
134
- "original_subtitle": "خلاصه با دلی ناراحت این کار رو کرد کاری رو که به نظرم میاد کار عقلانی بوده و رفتن رسیدن همون جایی که خاطره خداحافظی ۶ سال پیش رو واسه شکلتون زنده کرد اون بار به عنوان invalid اون بار به عنوان ادمی که از پس کار برنیومده",
135
- "ignore_it": False,
136
- "annotations": [
137
- {
138
- "annotator": "amin76",
139
- "annotated_subtitle": "خلاصه با دلی ناراحت این کار رو کرد کاری رو که به نظرم میاد کار عقلانی بوده و رفتن رسیدن همون جایی که خاطره خداحافظی شش سال پیش رو واسه شکلتون زنده کرد اون بار به عنوان اينوليد اون بار به عنوان آدمی که از پس کار برنیومده",
140
- "audio_trims": [
141
- {"start": 14.5, "end": 15.0},
142
- {"start": 14.3, "end": 15.0}
143
- ],
144
- "is_first_phase_accepted": False,
145
- "create_at": "2025-05-20T16:05:53.138811",
146
- "update_at": "2025-05-20T16:06:28.525393"
147
- }
148
- ],
149
- "is_approved_in_second_phase": False
150
- }
151
-
152
- save_json_data(json_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -3,4 +3,5 @@ sqlalchemy
3
  pydantic
4
  mysql-connector-python
5
  soundfile
6
- librosa
 
 
3
  pydantic
4
  mysql-connector-python
5
  soundfile
6
+ librosa
7
+ pydantic-settings
seed_db.py DELETED
@@ -1,52 +0,0 @@
1
- import mysql.connector
2
- from datasets import load_dataset
3
- from huggingface_hub import login
4
- import config
5
-
6
-
7
- def seed():
8
- login(token=config.hf_token)
9
- dataset = load_dataset(config.hf_tts_ds_repo, split="train", trust_remote_code=True)
10
-
11
- print(dataset.column_names)
12
- print(dataset[0])
13
-
14
- conn = mysql.connector.connect(**config.db_config)
15
- cursor = conn.cursor()
16
-
17
- cursor.execute(
18
- """
19
- CREATE TABLE IF NOT EXISTS tts_data (
20
- id INT AUTO_INCREMENT PRIMARY KEY,
21
- filename VARCHAR(200) UNIQUE,
22
- sentence TEXT
23
- )
24
- """
25
- )
26
-
27
- batch_size = 1000
28
- batch = []
29
-
30
- for i, item in enumerate(dataset):
31
- filename = item["audio"]["path"]
32
- sentence = item["sentence"]
33
- batch.append((filename, sentence))
34
-
35
- if len(batch) == batch_size:
36
- cursor.executemany(
37
- "INSERT INTO tts_data (filename, sentence) VALUES (%s, %s)", batch
38
- )
39
- conn.commit()
40
- print(f"✅ {i + 1} records saved!")
41
- batch = []
42
-
43
- if batch:
44
- cursor.executemany(
45
- "INSERT INTO tts_data (filename, sentence) VALUES (%s, %s)", batch
46
- )
47
- conn.commit()
48
- print(f"✅ last {len(batch)} records saved.")
49
-
50
- cursor.close()
51
- conn.close()
52
- return "done!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db_test.py → test/db_test.py RENAMED
@@ -2,16 +2,18 @@ import mysql.connector
2
  from mysql.connector import Error
3
  import config
4
 
5
-
6
  try:
7
  conn = mysql.connector.connect(**config.db_config)
8
  if conn.is_connected():
9
  print("✅OK!")
10
 
11
  cursor = conn.cursor()
12
- cursor.execute("DROP TABLE IF EXISTS tts_data")
13
- version = cursor.fetchone()
14
- print("🟢Done")
 
 
 
15
 
16
  except Error as e:
17
  print("❌ error:", e)
@@ -21,4 +23,4 @@ finally:
21
  cursor.close()
22
  if 'conn' in locals() and conn.is_connected():
23
  conn.close()
24
- print("⛔️Closed.")
 
2
  from mysql.connector import Error
3
  import config
4
 
 
5
  try:
6
  conn = mysql.connector.connect(**config.db_config)
7
  if conn.is_connected():
8
  print("✅OK!")
9
 
10
  cursor = conn.cursor()
11
+ cursor.execute("SELECT id, filename, sentence FROM tts_data ORDER BY id DESC LIMIT 5")
12
+ records = cursor.fetchall()
13
+
14
+ print("🟢Last 5 records:")
15
+ for record in records:
16
+ print(record)
17
 
18
  except Error as e:
19
  print("❌ error:", e)
 
23
  cursor.close()
24
  if 'conn' in locals() and conn.is_connected():
25
  conn.close()
26
+ print("⛔️Closed.")
test/test.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ USERS = {"u1": "123", "u2": "234"}
4
+
5
+
6
+ def local_login(username: str, password: str, session: dict):
7
+ """Authenticate against the in-memory USERS dict."""
8
+ if USERS.get(username) != password:
9
+ return (
10
+ "❌ Wrong username or password!",
11
+ gr.update(), # keep login form unchanged
12
+ gr.update(visible=False), # hide dashboard
13
+ )
14
+
15
+ # Successful login
16
+ session["user"] = username
17
+ return (
18
+ f"✅ Hello *{username}*",
19
+ gr.update(visible=False), # hide login form
20
+ gr.update(visible=True), # show dashboard
21
+ )
22
+
23
+
24
+ # --- 1. Update the logout function -----------------------------------------
25
+ def local_logout(session: dict):
26
+ """Clear the session, show the login form, hide the dashboard,
27
+ and clear any previous login message."""
28
+ session.clear()
29
+ return (
30
+ gr.update(visible=True), # show login_form
31
+ gr.update(visible=False), # hide dashboard
32
+ gr.update(value=""), # clear login_msg
33
+ )
34
+
35
+
36
+ def echo_with_user(text: str, session: dict):
37
+ """Return 'username: text'."""
38
+ user = session.get("user", "(none)")
39
+ return f"{user}: {text}"
40
+
41
+
42
+ def oauth_welcome(profile: gr.OAuthProfile, session: dict):
43
+ """Executed after a successful OAuth login."""
44
+ if profile is None:
45
+ return gr.update(visible=False)
46
+
47
+ session["user"] = profile.username
48
+ return gr.update(value=f"✅ Welcome {profile.username}", visible=True)
49
+
50
+
51
+ with gr.Blocks(title="Multi-user Login Demo") as demo:
52
+ # Session state is kept per browser tab
53
+ session = gr.State({})
54
+
55
+ # -------------------- Login form --------------------
56
+ with gr.Column(visible=True) as login_form:
57
+ username = gr.Text(label="Username")
58
+ password = gr.Text(label="Password", type="password")
59
+ login_btn = gr.Button("Log in")
60
+ login_msg = gr.Markdown()
61
+
62
+ # -------------------- Dashboard --------------------
63
+ with gr.Column(visible=False) as dashboard:
64
+ welcome = gr.Markdown()
65
+ echo_box = gr.Textbox(label="Echo – whatever you type will be returned")
66
+ echo_btn = gr.Button("Echo") # renamed button
67
+ echo_output = gr.Markdown() # where result is shown
68
+ logout_btn = gr.Button("Log out")
69
+
70
+ # -------------- Callbacks (local DB version) --------
71
+ login_btn.click(
72
+ fn=local_login,
73
+ inputs=[username, password, session],
74
+ outputs=[login_msg, login_form, dashboard],
75
+ concurrency_limit=10,
76
+ ).then(
77
+ lambda s: f"👋 Active user: {s.get('user', '')}",
78
+ inputs=session,
79
+ outputs=welcome,
80
+ )
81
+
82
+ logout_btn.click(
83
+ fn=local_logout,
84
+ inputs=session,
85
+ outputs=[login_form, dashboard, login_msg], # ← added login_msg
86
+ )
87
+
88
+ echo_btn.click(
89
+ fn=echo_with_user,
90
+ inputs=[echo_box, session],
91
+ outputs=echo_output,
92
+ )
93
+
94
+
95
+ demo.queue(default_concurrency_limit=50)
96
+ demo.launch()
utils/__init__.py ADDED
File without changes
utils/auth.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # utils/auth.py
2
+ import gradio as gr
3
+
4
+
5
+ class AuthService:
6
+ """
7
+ Authentication / business-logic layer.
8
+ Replace with real DB + hashed passwords in production.
9
+ """
10
+ _USERS = {"u1": "123", "u2": "234"}
11
+
12
+ # ─────────────────── core actions ────────────────────
13
+ @staticmethod
14
+ def login(username: str, password: str, session: dict):
15
+ if AuthService._USERS.get(username) != password:
16
+ return (
17
+ "❌ Wrong username or password!",
18
+ gr.update(), # keep login form visible
19
+ gr.update(visible=False), # hide dashboard
20
+ )
21
+
22
+ # success
23
+ session["user"] = username
24
+ return (
25
+ f"✅ Hello *{username}*",
26
+ gr.update(visible=False), # hide login form
27
+ gr.update(visible=True), # show dashboard
28
+ )
29
+
30
+ @staticmethod
31
+ def logout(session: dict):
32
+ session.clear()
33
+ return (
34
+ gr.update(visible=True), # show login page
35
+ gr.update(visible=False), # hide dashboard
36
+ gr.update(value="") # clear old messages
37
+ )
38
+
39
+ @staticmethod
40
+ def echo(text: str, session: dict):
41
+ user = session.get("user", "(none)")
42
+ return f"{user}: {text}"
utils/database.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import create_engine
2
+ from sqlalchemy.orm import sessionmaker
3
+ from contextlib import contextmanager
4
+ from config import conf
5
+ from utils.logger import Logger
6
+
7
+ log = Logger.get_logger()
8
+
9
+
10
+ def get_db_engine():
11
+ """Create DB engine with error handling for HF Spaces"""
12
+ try:
13
+ engine = create_engine(
14
+ conf.db_url,
15
+ pool_pre_ping=True,
16
+ pool_size=5,
17
+ max_overflow=10,
18
+ connect_args={"connect_timeout": 10},
19
+ )
20
+ log.info("Database engine created successfully")
21
+ return engine
22
+ except Exception as e:
23
+ log.error(f"Failed to create database engine: {e}")
24
+
25
+
26
+ engine = get_db_engine()
27
+ SessionLocal = sessionmaker(bind=engine)
28
+
29
+
30
+ @contextmanager
31
+ def get_db():
32
+ """Session manager for HF Spaces"""
33
+ db = SessionLocal()
34
+ try:
35
+ yield db
36
+ db.commit()
37
+ except Exception as e:
38
+ db.rollback()
39
+ log.error(f"Database error: {e}")
40
+ raise
41
+ finally:
42
+ db.close()
43
+
44
+
45
+ def initialize_database():
46
+ """Initialize tables with HF Spaces compatibility"""
47
+ try:
48
+ from data.models import Base
49
+
50
+ Base.metadata.create_all(bind=engine)
51
+ log.info("Tables created successfully")
52
+ except Exception as e:
53
+ log.error(f"Table creation failed: {e}")
utils/logger.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import sys
3
+ from typing import Optional
4
+
5
+
6
+ class Logger:
7
+ _initialized = False
8
+
9
+ @classmethod
10
+ def initialize(cls, level: str = "INFO", name: Optional[str] = None):
11
+ if cls._initialized:
12
+ return
13
+
14
+ logger = logging.getLogger(name or __name__)
15
+ logger.setLevel(level)
16
+
17
+ # Create console handler
18
+ handler = logging.StreamHandler(sys.stdout)
19
+ handler.setLevel(level)
20
+
21
+ # Simple formatter
22
+ formatter = logging.Formatter(
23
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
24
+ datefmt="%Y-%m-%d %H:%M:%S",
25
+ )
26
+ handler.setFormatter(formatter)
27
+
28
+ # Add handler and prevent duplication
29
+ if not logger.handlers:
30
+ logger.addHandler(handler)
31
+
32
+ cls._initialized = True
33
+ return logger
34
+
35
+ @classmethod
36
+ def get_logger(cls, name: Optional[str] = None) -> logging.Logger:
37
+ if not cls._initialized:
38
+ cls.initialize()
39
+ return logging.getLogger(name or __name__)