Skip to content

Commit 4517d7e

Browse files
author
Shaji Ahmed
committed
Major UI overhaul and code restructuring
Features implemented: • Smart breadcrumb navigation system with URL pattern matching • Public Sans font integration for improved typography • Comprehensive template partials system for reusable components • Color-coded resource format badges (CSV, JSON, XML, PDF, XLSX) • Enhanced CSV/Excel preview functionality using pandas DataFrame.head() • Mutual exclusive filtering system for topics and organizations • Resource card optimization with footer-based counts and direct linking Technical improvements: • Project restructure: web/ → app/, ekan/ → project/ • Modern dependency management with uv and pyproject.toml • Custom Django template tags for breadcrumbs and query management • Bootstrap 5.3.8 integration with enhanced styling • Database seeding with 5 standardized format types • Improved data preview with better table rendering and hover effects UI/UX enhancements: • Consistent card layouts across datasets, organizations, and topics • Responsive design improvements with proper spacing • Enhanced sidebar filtering with clear visual feedback • Better resource preview with shape information display • Professional color scheme for format identification
1 parent cf092bd commit 4517d7e

File tree

108 files changed

+3690
-2004
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+3690
-2004
lines changed

.env.example

Lines changed: 0 additions & 29 deletions
This file was deleted.

.gitignore

Lines changed: 14 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,34 @@
1-
# Byte-compiled / optimized / DLL files
1+
# Python
2+
*.pyc
23
__pycache__/
3-
*.py[cod]
4-
*$py.class
5-
6-
# C extensions
7-
*.so
8-
9-
# Distribution / packaging
104
.Python
115
build/
126
develop-eggs/
137
dist/
14-
downloads/
158
eggs/
16-
.eggs/
179
lib/
1810
lib64/
1911
parts/
2012
sdist/
2113
var/
22-
wheels/
2314
*.egg-info/
2415
.installed.cfg
2516
*.egg
26-
MANIFEST
27-
28-
# PyInstaller
29-
# Usually these files are written by a python script from a template
30-
# before PyInstaller builds the exe, so as to inject date/other infos into it.
31-
*.manifest
32-
*.spec
33-
34-
# Installer logs
35-
pip-log.txt
36-
pip-delete-this-directory.txt
37-
38-
# Unit test / coverage reports
39-
htmlcov/
40-
.tox/
41-
.coverage
42-
.coverage.*
43-
.cache
44-
nosetests.xml
45-
coverage.xml
46-
*.cover
47-
.hypothesis/
48-
.pytest_cache/
49-
50-
# Translations
51-
*.mo
52-
*.pot
53-
54-
# Django stuff:
55-
*.log
56-
local_settings.py
57-
db.sqlite3
58-
59-
# Flask stuff:
60-
instance/
61-
.webassets-cache
62-
63-
# Scrapy stuff:
64-
.scrapy
6517

66-
# Sphinx documentation
67-
docs/_build/
68-
69-
# PyBuilder
70-
target/
71-
72-
# Jupyter Notebook
73-
.ipynb_checkpoints
74-
75-
# pyenv
76-
.python-version
77-
78-
# celery beat schedule file
79-
celerybeat-schedule
80-
81-
# SageMath parsed files
82-
*.sage.py
83-
84-
# Environments
85-
.env
86-
.venv
18+
# Virtual Environment
19+
.venv/
8720
env/
88-
venv/
89-
ENV/
90-
env.bak/
91-
venv.bak/
21+
.env/
9222

93-
# Spyder project settings
94-
.spyderproject
95-
.spyproject
96-
97-
# Rope project settings
98-
.ropeproject
99-
100-
# mkdocs documentation
101-
/site
102-
103-
# mypy
104-
.mypy_cache/
23+
# Django
24+
db.sqlite3
25+
media/
26+
static_root/
27+
local_settings.py
10528

106-
# VS Code
29+
# IDE specific files
30+
.idea/
10731
.vscode/
10832

109-
# Uploaded Files
110-
uploads/
33+
# Logs
34+
*.log

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

LICENSE

Lines changed: 0 additions & 21 deletions
This file was deleted.

README.md

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,6 @@ A simplified and 'Easier' version of a Knowledge Archive Network, inspired from
33

44
![alt text](https://github.com/schajee/ekan/blob/master/web/static/images/screenshot.png "Logo Title Text 1")
55

6-
## Installation
7-
1. Install Python 3.5+ and PIP
8-
2. Clone repository (`git clone https://github.com/schajee/ekan.git`)
9-
3. Create and activate virtual environment (`virtualenv env` and `source env/bin/activate`)
10-
4. Install dependencies (`pip install -r requirements.txt`)
11-
5. Copy `.env.example` to `.env` and adjust as required
12-
6. Build and run migrations (`python manage.py makemigrations` and `python manage.py migrate`)
13-
7. Create Superuser (`python manage.py createsuperuser` or `python manage.py seed admin`)
14-
8. Load initial data through fixtures (`python manage.py loaddata init.json`)
15-
9. Run server (`python manage.py runserver`)
16-
176
## Information Architecture
187
The system consists of a few basic entities...
198
* A Resource is the basic unit of information or data. A resource can be a file or a url to a file that contains data.
@@ -22,6 +11,3 @@ The system consists of a few basic entities...
2211
* A Topic is a classification mechanism to tag datasets into thematic areas. This allows users to find related datasets. A dataset can belong to multiple topics.
2312

2413
In addition, a resource can be of a Format, while a dataset is released under a License. Organisation's managers are Staff users who have access to Admin for editing and managing content.
25-
26-
## Current Status
27-
EKAN is currently under active development. Contributions, discussions, and especially pull-requests are very welcome.

api/apps.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33

44
class ApiConfig(AppConfig):
5+
default_auto_field = 'django.db.models.BigAutoField'
56
name = 'api'

api/serializers.py

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,85 @@
1-
from django.contrib.auth.models import User, Group
2-
from web.models import Dataset
31
from rest_framework import serializers
2+
from app.models import Dataset, Organisation, Topic, Resource, License, Format
43

54

6-
class UserSerializer(serializers.HyperlinkedModelSerializer):
5+
class LicenseSerializer(serializers.ModelSerializer):
76
class Meta:
8-
model = User
9-
fields = ('url', 'username', 'email', 'groups')
7+
model = License
8+
fields = ['id', 'title', 'slug', 'description', 'url', 'icon', 'is_open']
109

1110

12-
class GroupSerializer(serializers.HyperlinkedModelSerializer):
11+
class FormatSerializer(serializers.ModelSerializer):
1312
class Meta:
14-
model = Group
15-
fields = ('url', 'name')
13+
model = Format
14+
fields = ['id', 'title', 'slug', 'description', 'icon', 'mime_type', 'is_data_format']
1615

1716

18-
class DatasetSerializer(serializers.HyperlinkedModelSerializer):
17+
class TopicSerializer(serializers.ModelSerializer):
18+
dataset_count = serializers.SerializerMethodField()
19+
20+
class Meta:
21+
model = Topic
22+
fields = ['id', 'title', 'slug', 'description', 'icon', 'color', 'is_featured', 'dataset_count']
23+
24+
def get_dataset_count(self, obj):
25+
return obj.datasets.filter(is_published=True).count()
26+
27+
28+
class OrganisationSerializer(serializers.ModelSerializer):
29+
dataset_count = serializers.SerializerMethodField()
30+
manager_name = serializers.SerializerMethodField()
31+
32+
class Meta:
33+
model = Organisation
34+
fields = ['id', 'title', 'slug', 'description', 'url', 'logo', 'manager_name',
35+
'is_active', 'created', 'updated', 'dataset_count']
36+
37+
def get_dataset_count(self, obj):
38+
return obj.datasets.filter(is_published=True).count()
39+
40+
def get_manager_name(self, obj):
41+
return obj.manager.get_full_name() if obj.manager else None
42+
43+
44+
class ResourceSerializer(serializers.ModelSerializer):
45+
format_details = FormatSerializer(source='format', read_only=True)
46+
file_size_human = serializers.ReadOnlyField()
47+
is_file_upload = serializers.ReadOnlyField()
48+
is_external_url = serializers.ReadOnlyField()
49+
download_url = serializers.SerializerMethodField()
50+
51+
class Meta:
52+
model = Resource
53+
fields = ['id', 'title', 'slug', 'description', 'file', 'url', 'size',
54+
'file_size_human', 'mimetype', 'encoding', 'format_details',
55+
'is_preview_available', 'download_count', 'is_file_upload',
56+
'is_external_url', 'download_url', 'created', 'updated']
57+
58+
def get_download_url(self, obj):
59+
request = self.context.get('request')
60+
if request:
61+
from django.urls import reverse
62+
return request.build_absolute_uri(
63+
reverse('app:resource_download', kwargs={'slug': obj.slug})
64+
)
65+
return None
66+
67+
68+
class DatasetSerializer(serializers.ModelSerializer):
69+
organisation_details = OrganisationSerializer(source='organisation', read_only=True)
70+
topics_details = TopicSerializer(source='topics', many=True, read_only=True)
71+
license_details = LicenseSerializer(source='license', read_only=True)
72+
resources = ResourceSerializer(many=True, read_only=True)
73+
resource_count = serializers.ReadOnlyField()
74+
author_name = serializers.SerializerMethodField()
75+
1976
class Meta:
2077
model = Dataset
21-
fields = ('title', 'slug')
78+
fields = ['id', 'title', 'slug', 'description', 'notes', 'organisation',
79+
'organisation_details', 'topics', 'topics_details', 'license',
80+
'license_details', 'author_name', 'maintainer_name', 'maintainer_email',
81+
'is_published', 'is_featured', 'resource_count', 'resources',
82+
'created', 'updated', 'published_date']
83+
84+
def get_author_name(self, obj):
85+
return obj.author.get_full_name() if obj.author else obj.author.username

api/urls.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from django.urls import path, include
2+
from rest_framework.routers import DefaultRouter
3+
from . import views
4+
5+
# Create a router and register our viewsets with it.
6+
router = DefaultRouter()
7+
router.register(r'datasets', views.DatasetViewSet)
8+
router.register(r'organisations', views.OrganisationViewSet)
9+
router.register(r'topics', views.TopicViewSet)
10+
router.register(r'resources', views.ResourceViewSet)
11+
12+
# The API URLs are now determined automatically by the router.
13+
urlpatterns = [
14+
path('v1/', include(router.urls)),
15+
]

0 commit comments

Comments
 (0)