Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 13 additions & 127 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,135 +38,21 @@ QUL is implemented using Ruby on Rails, and Active Admin for implementing the ad
- Import Data: Import data from different sources.
- Quranic grammar and morphology: Manage Quranic grammar and morphology data.

## Setup Guide
This guide will help you set up the QUL project on your local machine. Follow the steps below to get started.
## Documentation
Start with the docs index: [docs/README.md](docs/README.md)

### Prerequisites
- **Ruby**: Version 3.3.3
- **Rails**: Version 7.0.3
- **RVM or rbenv**: For managing Ruby versions
- **PostgreSQL**: 14.3 or higher
- **Redis**: 7.0.0 or higher
Website docs index: [https://qul.tarteel.ai/docs](https://qul.tarteel.ai/docs)

#### 1. Clone the repository
```bash
git clone git@github.com:TarteelAI/quranic-universal-library.git
cd quranic-universal-library
```
Primary path for resource users:

#### 2. Install Ruby and setup environment
```bash
rvm install 3.3.3
rvm use 3.3.3
rvm gemset create qul
rvm gemset use qul
```
- Getting Started: [docs/getting-started.md](docs/getting-started.md)
- Downloading and Using Data: [docs/downloading-data.md](docs/downloading-data.md)
- Resource Guides Index: [docs/resource-guides-index.md](docs/resource-guides-index.md)
- Datasets: [docs/datasets.md](docs/datasets.md)
- Data Model: [docs/data-model.md](docs/data-model.md)
- Tutorials: [docs/tutorials.md](docs/tutorials.md)
- FAQ: [docs/faq.md](docs/faq.md)

#### 3. Install PostgreSQL
- **Ubuntu/Debian**
```bash
sudo apt-get update
sudo apt-get install postgresql postgresql-contrib libpq-dev
```
- **MacOS**
```bash
brew install postgresql
```
- **Windows**
Download and install the latest version of PostgreSQL from the [official website](https://www.postgresql.org/download/windows/).
## Contributing

#### 4. Install Dependencies
```bash
gem install bundler
bundle install
```

* **Tip:** To prevent Bundler from reinstalling due to a version mismatch, you can install the specific version listed under `BUNDLED_WITH` in `Gemfile.lock` using the command `gem install bundler -v [version]`

#### 5. Database Configuration

**QUL requires two databases:**

1. **`quran_dev`**: This database holds all Quranic data, including translations, tafsirs, audio etc. It's accessed through `quran_api_db_dev` for the development environment.
2. **`quran_community_tarteel`**: This database hold data about user accounts, permissions, and user generated content and change log about translations.

#### 6. Create Databases
Create the **`quran_community_tarteel`** database for managing user content.
```bash
rails db:create
```

For **`quran_dev`** you can create it manually or change the database name to `quran_dev` for `development` group in database.yml file and run `rails db:create` again.

#### 7. Load the data for **`quran_dev`** database
The `quran_dev` database dump is available in both SQL and binary formats. Follow the appropriate instructions below to restore the database.

> **Mini Database Dump for Development**
>
> The database backup provided below contains a limited subset of data, specifically selected for local development and testing. It is not a full production database and is intended solely for development and contribution purposes.
>
> We **do not provide or share** the full database backup. This mini dump contains all the essential data required to run QUL locally or contribute new features. If you're working on a feature that need more data, please open an issue.
> If you're looking for specific resource, please visit our [Resources page](https://qul.tarteel.ai/resources/) to download it.


#### Restoring from SQL Dump
7.1 **Restore using SQL Dump:**
Download the [SQL dump file](https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.sql.zip) and restore it using
```bash
psql quran_dev < "path to sql dump file"
```
If it didn't work try running the following command:
```bash
psql -U postgres -d quran_dev -f path/to/quran_dev.sql
```

7.2 **Restore using binary dump:**
Download the [Binary dump file](https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.dump.zip) and restore it using
```bash
pg_restore --host localhost --port 5432 --no-owner --no-privileges --no-tablespaces --no-acl --dbname quran_dev -v "path to binary dump file"
```

### 8. Run the migrations for **`quran_community_tarteel`** database
```ruby
rails db:migrate
rails db:migrate
rails db:seed
```

#### 9. Run the Application
```bash
bin/dev
```

🌟Insha`Allah! Your application should be up and running at time point! 🌟

You can now visit [http://localhost:3000](http://localhost:3000) in your browser to explore the app.

🔐 Head over to the admin panel at [http://localhost:3000/admin](http://localhost:3000/admin)

### 10. Contributing to QUL
We welcome contributions to enhance the QUL project! If you'd like to contribute, please follow these steps:

10.1 **Fork the Repository:**
Click on the "Fork" button at the top right of this page to create your own copy of the repository.

10.2 **Clone Your Fork:**
```bash
git clone https://github.com/your_username/quranic-universal-library.git
```

10.3. **Create a new feature branch:**
```bash
git checkout -b making-qul
```
10.4 **Make Your Changes:**

10.5 **Push Your Changes:**
```bash
git add .
git commit -m "Add a brief description of your changes"
git push origin your-feature-branch
```
10.6 **Create a Pull Request:**

May Allah reward your efforts and make them beneficial for the community! 🤲
Use [docs/contributing.md](docs/contributing.md) for the complete contribution flow and PR checklist.
7 changes: 6 additions & 1 deletion app/assets/config/manifest.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
//= link_tree ../images
//= link_tree ../fonts
//= link_tree ../builds

//= link application.css
//= link active_admin.css
//= link landing.css
//= link export.css
//= link tinymce_custom.css
//= link pdf.css
63 changes: 58 additions & 5 deletions app/assets/stylesheets/application.tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@
}

.tw-docs pre {
@apply tw-p-4 tw-rounded-md tw-overflow-x-auto;
background-color: #ecf0f1;
font-size: 1.1em;
color: #2c3e50;
@apply tw-p-4 tw-overflow-x-auto;
background-color: #0f172a;
color: #e2e8f0;
font-size: 0.95rem;
}

.tw-docs code {
Expand All @@ -85,6 +85,59 @@
color: #2c3e50;
}

.tw-docs pre code {
@apply tw-p-0 tw-bg-transparent tw-rounded-none;
color: inherit;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}

.tw-docs-code-block,
.tw-docs-playground {
@apply tw-my-6 tw-overflow-hidden tw-rounded-xl tw-border tw-border-gray-200 tw-bg-white tw-shadow-sm;
}

.tw-docs-code-header {
@apply tw-flex tw-items-center tw-justify-between tw-gap-2 tw-bg-gray-900 tw-px-3 tw-py-2 tw-text-gray-100;
}

.tw-docs-code-language {
@apply tw-text-xs tw-font-semibold tw-uppercase tw-tracking-wide;
}

.tw-docs-code-actions {
@apply tw-flex tw-items-center tw-gap-2;
}

.tw-docs-code-button {
@apply tw-rounded tw-border-0 tw-bg-gray-700 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-text-gray-100 hover:tw-bg-gray-600;
}

.tw-docs-playground-grid {
@apply tw-grid xl:tw-grid-cols-2;
}

.tw-docs-playground-pane {
@apply tw-flex tw-flex-col tw-bg-white;
}

.tw-docs-playground-pane + .tw-docs-playground-pane {
@apply tw-border-t tw-border-gray-200 xl:tw-border-l xl:tw-border-t-0;
}

.tw-docs-playground-label {
@apply tw-bg-gray-50 tw-px-4 tw-py-2 tw-text-xs tw-font-semibold tw-uppercase tw-tracking-wide tw-text-gray-500;
}

.tw-docs-playground-editor {
@apply tw-min-h-[340px] lg:tw-min-h-[420px] tw-w-full tw-resize-y tw-border-0 tw-p-4 tw-font-mono tw-text-[13px] tw-leading-6 focus:tw-ring-2 focus:tw-ring-blue-500;
tab-size: 2;
background: linear-gradient(#ffffff, #ffffff) padding-box, linear-gradient(to bottom, #f8fafc, #f1f5f9) border-box;
}

.tw-docs-playground-preview {
@apply tw-min-h-[340px] lg:tw-min-h-[420px] tw-w-full tw-border-0 tw-bg-white;
}

.tw-docs .example {
@apply tw-p-4 tw-rounded-lg tw-mt-3 tw-mb-3;
background-color: #eafaf1;
Expand Down Expand Up @@ -163,4 +216,4 @@

.contributor-logo:hover .logo {
transform: scale(1.05);
}
}
27 changes: 27 additions & 0 deletions app/controllers/community_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@ def stt_validation
end

def docs
docs_page_service = DocsPageService.new
@docs_page = docs_page_service.find(params[:key])

if @docs_page.present?
@docs_source = :markdown
elsif (@docs_tag = DownloadableResourceTag.find_by(slug: params[:key]))
@docs_source = :tag
elsif docs_partial_exists?(params[:key])
@docs_source = :partial
@docs_partial = params[:key]
else
raise ActionController::RoutingError, "Not Found"
end

render layout: false if request.xhr?
end

def docs_index
@docs_page = DocsPageService.new.readme
raise ActionController::RoutingError, "Not Found" unless @docs_page.present?

render layout: false if request.xhr?
end

Expand Down Expand Up @@ -82,4 +103,10 @@ def authorize_access!
def init_presenter
@presenter = CommunityPresenter.new(self)
end

def docs_partial_exists?(key)
return false unless key.to_s.match?(/\A[a-z0-9_-]+\z/)

lookup_context.exists?("docs/#{key}", [], true)
end
end
64 changes: 53 additions & 11 deletions app/controllers/resources_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ class ResourcesController < CommunityController
before_action :init_presenter

def index
@resources = view_context.downloadable_resource_cards.values
@resource_cards = view_context.downloadable_resource_cards.values

sort_by = params[:sort_key]
sort_order = params[:sort_order]

if sort_by.present? && ['name', 'count'].include?(sort_by)
@resources = @resources.sort_by { |resource| resource[sort_by.to_sym] }
@resources.reverse! if sort_order == 'desc'
@resource_cards = @resource_cards.sort_by { |resource| resource[sort_by.to_sym] }
@resource_cards.reverse! if sort_order == 'desc'
end

@resource_search = if params[:q].present? || params[:tags].present?
build_resource_search(searchable_resources_scope, global: true)
else
empty_resource_search(global: true)
end
end

Expand Down Expand Up @@ -56,26 +62,62 @@ def download
end

def show
@resources = DownloadableResource
.published
.includes(:downloadable_resource_tags, :related_resources)
.where(resource_type: params[:id])
base_scope = searchable_resources_scope.where(resource_type: params[:id])

sort_by = params[:sort_key]
sort_order = params[:sort_order].to_s == 'desc' ? 'desc' : 'asc'

if sort_by.present? && ['name'].include?(sort_by)
@resources = @resources.order("name #{sort_order}")
base_scope = base_scope.order("name #{sort_order}")
end
@presenter.set_resource(@resources.first)
@all_resources = base_scope.to_a

if @resources.empty?
if @all_resources.empty?
redirect_to resources_path, alert: 'Sorry, this resource does not exist.'
return
end

@resource_search = build_resource_search(base_scope, global: false)
@resources = @resource_search.results
@presenter.set_resource(@all_resources.first)
end

protected

def searchable_resources_scope
DownloadableResource
.published
.includes(:downloadable_resource_tags, :related_resources, :resource_content)
end

def build_resource_search(scope, global:)
Resources::SearchQuery
.new(
scope: scope,
query: params[:q],
selected_tags: params[:tags],
selected_resource_types: params[:resource_types],
global: global
)
.call
end

def empty_resource_search(global:)
Resources::SearchQuery::Result.new(
query: params[:q].to_s,
text_query: '',
selected_tags: Array(params[:tags]).reject(&:blank?),
selected_resource_types: Array(params[:resource_types]).reject(&:blank?),
parsed_reference: nil,
results: [],
primary_results: [],
related_results: [],
available_tags: [],
available_resource_types: [],
global: global
)
end

def set_resource
@resource = DownloadableResource.published.find(params[:id])
end
Expand Down Expand Up @@ -151,4 +193,4 @@ def handle_ayah_topics
@is_verse_search = search.verse_key_search?
end
end
end
end
Loading