Skip to content
Samad Mahmoodi edited this page May 31, 2026 · 3 revisions

1. How alive is the project?

The project currently have more than 7k stars on Github, 612 commits and more than 51 contributors. The last app release was on July 14, 2025, which is the 23rd release.

2. How important is it?

This is a FOSS download manager written in python and downloaded more than 1.1 million times. The project is relatively popular in its domain.

3. What is it suitable for?

This is a cross platform download manager suitable for downloading files. Works on Linux, macOS and Windows.

4. What technologies are involved?

Python, PyQt5 for GUI, Aria2(download engine)

5. Development phase or Evolution phase?

The project already have several releases during years of development and it’s evolving by adding new features and bug fixes.

6. Are there many issues to fix?

Currently there are 309 open issues on the GitHub page and 571 closed issues.


Issue: Sorting Data Table

The issue can be found here: https://github.com/persepolisdm/persepolis/issues/1112

A user requested several features, including the ability to sort tabs by clicking on the table headers.

The app already has the ability to sort items, but it is only accessible through the menu, and it is limited to File Name, Size, First Try Date, Last Try Date, and Status.

During the implementation, we noticed that the existing sorting function for size had a bug and did not sort values properly.

image3

Requirement Objective:

  • Implement sorting by clicking on each header with the ability to sort in ascending and descending.
  • Support sorting for:
    • Downloaded size
    • Download percentage
  • Display correct sorting indicators in the UI (▲, ▼)
  • Save and restore the sorting preference between application runs

Implementation and Fix:

During the development of this feature, we tried to rely on existing functions and implement the code in a way that matches the coding patterns and function signatures used by the original developers.

Original Flow

Persepolis uses a QTableWidget to display downloads. Each row represents a download entry with fields such as File name, Status, Size, Downloaded size, Percentage, etc.

Instead of relying on Qt’s built-in sorting, a manual sorting system was implemented by the original developers.

The original sorting implementation works by extracting values from the data table widget, mapping each row to a dictionary, and then performing the sorting using Python’s built-in sorted().

# data extraction
downloaded_str = self.download_table.item(row, 3).text()
gid = self.download_table.item(row, 8).text()

# map to dict
gid_value_dict[gid] = parsed_value

# perform sort
gid_sorted_list = sorted(
    gid_value_dict,
    key=gid_value_dict.get,
    reverse=True
)

To maintain consistency with the existing code style, we implemented sorting by Downloaded Size and Download Percentage in the same way as the original developers.

Click Event

To enable click events on each column header, we use the sectionClicked signal to trigger the sorting function.

self.download_table.horizontalHeader().sectionClicked.connect(self.downloadTableHeaderClicked)

And we can perform sorting for each column by this function

# sort by clickable headers in download table
def downloadTableHeaderClicked(self, column):
    self.sort_desc = not self.sort_desc
    if column == 0:
        self.sortByName()
    elif column == 1:
        self.sortByStatus()
    elif column == 2:
        self.sortBySize()
    elif column == 3:
        self.sortByDownloaded()
    elif column == 4:
        self.sortByPercent()
    elif column == 10:
        self.sortByFirstTry()
    elif column == 11:
        self.sortByLastTry()

Indicator and sorting direction

This functionality is implemented by retrieving the column name and appending an indicator icon based on the sorting direction (ascending or descending).

For simplicity, ASCII arrow symbols are used instead of bitmap icons, for example: ▲ and ▼.

def updateSortHeaderIndicator(self, active_key=None, descending=False):
        self.resetColumnByHeaderText()
        selected = self.findColumnByHeaderText(active_key)

        header = self.download_table.horizontalHeaderItem(selected)
        title = header.text()+ (' ▼' if descending else ' ▲')
        header.setText(title)

After each sorting operation, this function is called to display the indicator on the corresponding column.

Restoring Sort Status

To preserve the sorting state after the application is reopened, the sorting preferences must be saved and restored when the application starts.

The project already uses Qt’s built-in QSettings class for storing and loading user preferences, so it is also used to save the sorting column and sorting direction.

By calling the restoreLastSort function after the application loads, the previous sorting state can be restored automatically.

Sort By Size Bug

During development, we noticed that sorting by file size was not functioning correctly. After investigating the issue, we discovered that the size parsing logic was implemented incorrectly.

size_int = float(size_str[:-3])
size_symbol = str(size_str[-2])

For values such as "12 MiB", the extracted character for size_symbol was incorrectly identified as "i" instead of "M", which represents megabytes. Adjusting the index to -3 resolve the issue.

image2

Flow Diagram

image1

Fix Source Code:

The changes can be found at the following link:
https://github.com/persepolisdm/persepolis/pull/1124/changes

Pull Request

A pull request submitted to the github page of the project.
https://github.com/persepolisdm/persepolis/pull/1124

Project Maintainability Assessment

Documentation

Score: 3/5

The project provides sufficient documentation for understanding the basic functionality and setup process. However, some aspects of the documentation are incomplete, making onboarding and issue resolution more difficult for new contributors.

Suggested Improvement: Provide more developer-oriented documentation, including architectural overviews and contribution guidelines.

Source Code Quality

Score: 5/5

The source code is well-structured and organized, making it relatively easy to locate, understand, and modify the components involved in the selected issues. In particular, the graphical user interface (GUI) is clearly separated from the business logic, which improves readability and maintainability.

Suggested Improvement: Continue maintaining the current code organization and coding standards as the project evolves.

Testing Support

Score: 0/5

The project does not provide any automated tests. As a result, verifying changes requires manual testing, which increases the risk of introducing regressions.

Suggested Improvement: Introduce unit and integration tests and provide documentation on how to execute them.

Community Support

Score: 4/5

The maintainers and contributors are generally responsive and helpful. Their feedback was valuable for understanding the project and validating proposed fixes. However, response times may vary depending on contributor availability.

Suggested Improvement: Encourage more consistent and timely feedback on issues and pull requests.

Development Environment

Score: 4/5

The project is built using Python, Qt, and other widely used libraries, making the development environment relatively straightforward to set up. Although a requirements.txt file is provided, some dependencies were missing or not clearly documented, which created minor setup difficulties.

Suggested Improvement: Provide a step-by-step setup guide and ensure that all required dependencies are listed and kept up to date.

Overall Assessment

Overall Score: 3.5/5

Overall, the project demonstrates a good level of maintainability. The source code is well organized and easy to modify, while the development environment is relatively accessible. The main areas requiring improvement are testing support and documentation. Addressing these issues would further reduce maintenance effort and improve the experience for future contributors.