feat: Add email count tracking to folders with total and pending counts
This commit is contained in:
@@ -130,6 +130,49 @@ class IMAPService:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_folder_email_count(self, folder_name: str) -> int:
|
||||||
|
"""Get the count of emails in a specific folder."""
|
||||||
|
try:
|
||||||
|
# Connect to IMAP server
|
||||||
|
self._connect()
|
||||||
|
|
||||||
|
# Login
|
||||||
|
self.connection.login(
|
||||||
|
self.config.get('username', ''),
|
||||||
|
self.config.get('password', '')
|
||||||
|
)
|
||||||
|
|
||||||
|
# Select the folder
|
||||||
|
resp_code, content = self.connection.select(folder_name)
|
||||||
|
if resp_code != 'OK':
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Get email count
|
||||||
|
resp_code, content = self.connection.search(None, 'ALL')
|
||||||
|
if resp_code != 'OK':
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Count the emails
|
||||||
|
email_ids = content[0].split()
|
||||||
|
count = len(email_ids)
|
||||||
|
|
||||||
|
# Close folder and logout
|
||||||
|
self.connection.close()
|
||||||
|
self.connection.logout()
|
||||||
|
self.connection = None
|
||||||
|
|
||||||
|
return count
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error getting email count for folder {folder_name}: {str(e)}")
|
||||||
|
if self.connection:
|
||||||
|
try:
|
||||||
|
self.connection.logout()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.connection = None
|
||||||
|
return 0
|
||||||
|
|
||||||
def sync_folders(self) -> Tuple[bool, str]:
|
def sync_folders(self) -> Tuple[bool, str]:
|
||||||
"""Sync IMAP folders with local database."""
|
"""Sync IMAP folders with local database."""
|
||||||
try:
|
try:
|
||||||
@@ -171,6 +214,12 @@ class IMAPService:
|
|||||||
)
|
)
|
||||||
db.session.add(new_folder)
|
db.session.add(new_folder)
|
||||||
synced_count += 1
|
synced_count += 1
|
||||||
|
else:
|
||||||
|
# Update existing folder with email counts
|
||||||
|
# Get the total count of emails in this folder
|
||||||
|
total_count = self.get_folder_email_count(folder_name)
|
||||||
|
existing_folder.total_count = total_count
|
||||||
|
existing_folder.pending_count = 0 # Initially set to 0
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return True, f"Successfully synced {synced_count} folders"
|
return True, f"Successfully synced {synced_count} folders"
|
||||||
|
|||||||
@@ -40,5 +40,7 @@ class Folder(Base):
|
|||||||
rule_text = db.Column(db.Text)
|
rule_text = db.Column(db.Text)
|
||||||
priority = db.Column(db.Integer)
|
priority = db.Column(db.Integer)
|
||||||
organize_enabled = db.Column(db.Boolean, default=True)
|
organize_enabled = db.Column(db.Boolean, default=True)
|
||||||
|
total_count = db.Column(db.Integer, default=0)
|
||||||
|
pending_count = db.Column(db.Integer, default=0)
|
||||||
|
|
||||||
user = db.relationship('User', backref=db.backref('folders', lazy=True))
|
user = db.relationship('User', backref=db.backref('folders', lazy=True))
|
||||||
@@ -1,37 +1,38 @@
|
|||||||
<div id="folder-{{ folder.id }}" class="card bg-base-100 shadow-xl border border-base-300 hover:shadow-lg transition-shadow duration-200">
|
<div id="folder-{{ folder.id }}" class="card bg-base-100 shadow-xl border border-base-300 hover:shadow-lg transition-shadow duration-200">
|
||||||
|
|
||||||
<div class="card-body" data-loading-states>
|
<div class="card-body" data-loading-states>
|
||||||
<div class="flex justify-between items-start mb-2">
|
<div class="flex justify-between items-start mb-2">
|
||||||
<h3 class="text-xl font-bold truncate">{{ folder.name }}</h3>
|
<h3 class="text-xl font-bold truncate flex-grow">{{ folder.name }}</h3>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<button class="btn btn-sm btn-outline"
|
<button class="btn btn-sm btn-outline"
|
||||||
hx-get="/api/folders/{{ folder.id }}/edit"
|
hx-get="/api/folders/{{ folder.id }}/edit"
|
||||||
hx-target="#modal-holder"
|
hx-target="#modal-holder"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
hx-trigger="click"
|
hx-trigger="click"
|
||||||
data-loading-disable
|
data-loading-disable
|
||||||
>
|
>
|
||||||
<i class="fas fa-edit" data-loading-class="!hidden"></i>
|
<i class="fas fa-edit" data-loading-class="!hidden"></i>
|
||||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-sm btn-outline btn-error"
|
<button class="btn btn-sm btn-outline btn-error"
|
||||||
hx-delete="/api/folders/{{ folder.id }}"
|
hx-delete="/api/folders/{{ folder.id }}"
|
||||||
hx-target="#folders-list"
|
hx-target="#folders-list"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
hx-confirm="Are you sure you want to delete this folder?"
|
hx-confirm="Are you sure you want to delete this folder?"
|
||||||
data-loading-disable
|
data-loading-disable
|
||||||
>
|
>
|
||||||
<i class="fas fa-trash" data-loading-class="!hidden"></i>
|
<i class="fas fa-trash" data-loading-class="!hidden"></i>
|
||||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bg-base-200 rounded-box p-4 mb-4">
|
|
||||||
<p class="text-base-content/80">{{ folder.rule_text }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-between items-center mt-2">
|
<!-- Email count badges placed below title but in a separate row -->
|
||||||
|
<div class="flex justify-between items-center mb-2">
|
||||||
|
<div class="flex space-x-1">
|
||||||
|
<span class="badge badge-outline">{{ folder.total_count }} emails</span>
|
||||||
|
<span class="badge badge-secondary">{{ folder.pending_count }} pending</span>
|
||||||
|
</div>
|
||||||
{% if folder.priority == 1 %}
|
{% if folder.priority == 1 %}
|
||||||
<span class="badge badge-error">High Priority</span>
|
<span class="badge badge-error">High Priority</span>
|
||||||
{% elif folder.priority == -1 %}
|
{% elif folder.priority == -1 %}
|
||||||
@@ -39,6 +40,13 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<span class="badge badge-primary">Normal Priority</span>
|
<span class="badge badge-primary">Normal Priority</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-base-200 rounded-box p-4 mb-4">
|
||||||
|
<p class="text-base-content/80">{{ folder.rule_text }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-between items-center mt-2">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<span class="text-xs">Organize:</span>
|
<span class="text-xs">Organize:</span>
|
||||||
<input
|
<input
|
||||||
@@ -52,7 +60,7 @@
|
|||||||
data-loading-disable
|
data-loading-disable
|
||||||
aria-label="Toggle organize enabled">
|
aria-label="Toggle organize enabled">
|
||||||
</input>
|
</input>
|
||||||
|
|
||||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
"""Add email count fields to folders
|
||||||
|
|
||||||
|
Revision ID: a3ad1b9a0e5f
|
||||||
|
Revises: f8ba65458ba2
|
||||||
|
Create Date: 2025-08-05 11:00:46.880458
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'a3ad1b9a0e5f'
|
||||||
|
down_revision = 'f8ba65458ba2'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('folders', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('total_count', sa.Integer(), nullable=True))
|
||||||
|
batch_op.add_column(sa.Column('pending_count', sa.Integer(), nullable=True))
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('folders', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('pending_count')
|
||||||
|
batch_op.drop_column('total_count')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
Reference in New Issue
Block a user